From 125b181aec7a67c71234284ecf6d9c729d05deda Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 1 Oct 2009 13:22:27 -0500 Subject: staging: Add proper selection of WIRELESS_EXT and WEXT_PRIV After the incorporation of the patch entitled "wext: refactor", some of the wireless drivers in drivers/staging fail to build because they need to have CONFIG_WIRELESS_EXT and CONFIG_WEXT_PRIV defined. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/staging/rtl8187se/Kconfig | 3 ++- drivers/staging/rtl8192e/Kconfig | 3 ++- drivers/staging/vt6655/Kconfig | 4 +++- drivers/staging/vt6656/Kconfig | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/rtl8187se/Kconfig b/drivers/staging/rtl8187se/Kconfig index 236e42725447..faf6c6087414 100644 --- a/drivers/staging/rtl8187se/Kconfig +++ b/drivers/staging/rtl8187se/Kconfig @@ -1,6 +1,7 @@ config RTL8187SE tristate "RealTek RTL8187SE Wireless LAN NIC driver" depends on PCI - depends on WIRELESS_EXT + select WIRELESS_EXT + select WEXT_PRIV default N ---help--- diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig index 3100aa58c940..5c077b9fdc77 100644 --- a/drivers/staging/rtl8192e/Kconfig +++ b/drivers/staging/rtl8192e/Kconfig @@ -1,6 +1,7 @@ config RTL8192E tristate "RealTek RTL8192E Wireless LAN NIC driver" depends on PCI - depends on WIRELESS_EXT + select WIRELESS_EXT + select WEXT_PRIV default N ---help--- diff --git a/drivers/staging/vt6655/Kconfig b/drivers/staging/vt6655/Kconfig index 9bec95adcce2..825bbc4fc3fa 100644 --- a/drivers/staging/vt6655/Kconfig +++ b/drivers/staging/vt6655/Kconfig @@ -1,6 +1,8 @@ config VT6655 tristate "VIA Technologies VT6655 support" - depends on WIRELESS_EXT && PCI + depends on PCI + select WIRELESS_EXT + select WEXT_PRIV ---help--- This is a vendor-written driver for VIA VT6655. diff --git a/drivers/staging/vt6656/Kconfig b/drivers/staging/vt6656/Kconfig index 3165f2c42079..87bcd269310c 100644 --- a/drivers/staging/vt6656/Kconfig +++ b/drivers/staging/vt6656/Kconfig @@ -1,6 +1,8 @@ config VT6656 tristate "VIA Technologies VT6656 support" - depends on WIRELESS_EXT && USB + depends on USB + select WIRELESS_EXT + select WEXT_PRIV ---help--- This is a vendor-written driver for VIA VT6656. -- cgit v1.2.3-59-g8ed1b From 955015bb0b42167d14f776ff5947ae2463a974dc Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 20 Oct 2009 13:38:11 +0900 Subject: strip: move driver to staging Move the strip ("Starmode Radio IP") driver to drivers/staging. For several years this driver has only seen API "bombing-run" changes, and few people ever had the hardware. This driver represents unnecessary ongoing maintenance for no clear benefit. This patch brought to you by the "hacking" session at the 2009 Kernel Summit in Tokyo, Japan... Acked-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 23 - drivers/net/wireless/Makefile | 1 - drivers/net/wireless/strip.c | 2805 ---------------------------------------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/strip/Kconfig | 22 + drivers/staging/strip/Makefile | 1 + drivers/staging/strip/strip.c | 2805 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 2831 insertions(+), 2829 deletions(-) delete mode 100644 drivers/net/wireless/strip.c create mode 100644 drivers/staging/strip/Kconfig create mode 100644 drivers/staging/strip/Makefile create mode 100644 drivers/staging/strip/strip.c (limited to 'drivers/staging') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 85f8bf4112c1..5df47486e358 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -25,29 +25,6 @@ menuconfig WLAN_PRE80211 This option does not affect the kernel build, it only lets you choose drivers. -config STRIP - tristate "STRIP (Metricom starmode radio IP)" - depends on INET && WLAN_PRE80211 - select WIRELESS_EXT - ---help--- - Say Y if you have a Metricom radio and intend to use Starmode Radio - IP. STRIP is a radio protocol developed for the MosquitoNet project - to send Internet traffic using Metricom radios. Metricom radios are - small, battery powered, 100kbit/sec packet radio transceivers, about - the size and weight of a cellular telephone. (You may also have heard - them called "Metricom modems" but we avoid the term "modem" because - it misleads many people into thinking that you can plug a Metricom - modem into a phone line and use it as a modem.) - - You can use STRIP on any Linux machine with a serial port, although - it is obviously most useful for people with laptop computers. If you - think you might get a Metricom radio in the future, there is no harm - in saying Y to STRIP now, except that it makes the kernel a bit - bigger. - - To compile this as a module, choose M here: the module will be - called strip. - config ARLAN tristate "Aironet Arlan 655 & IC2200 DS support" depends on ISA && !64BIT && WLAN_PRE80211 diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7a4647e78fd3..527c272aa1af 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ -obj-$(CONFIG_STRIP) += strip.o obj-$(CONFIG_ARLAN) += arlan.o arlan-objs := arlan-main.o arlan-proc.o diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c deleted file mode 100644 index ea6a87c19319..000000000000 --- a/drivers/net/wireless/strip.c +++ /dev/null @@ -1,2805 +0,0 @@ -/* - * Copyright 1996 The Board of Trustees of The Leland Stanford - * Junior University. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. Stanford University - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - * - * strip.c This module implements Starmode Radio IP (STRIP) - * for kernel-based devices like TTY. It interfaces between a - * raw TTY, and the kernel's INET protocol layers (via DDI). - * - * Version: @(#)strip.c 1.3 July 1997 - * - * Author: Stuart Cheshire - * - * Fixes: v0.9 12th Feb 1996 (SC) - * New byte stuffing (2+6 run-length encoding) - * New watchdog timer task - * New Protocol key (SIP0) - * - * v0.9.1 3rd March 1996 (SC) - * Changed to dynamic device allocation -- no more compile - * time (or boot time) limit on the number of STRIP devices. - * - * v0.9.2 13th March 1996 (SC) - * Uses arp cache lookups (but doesn't send arp packets yet) - * - * v0.9.3 17th April 1996 (SC) - * Fixed bug where STR_ERROR flag was getting set unneccessarily - * (causing otherwise good packets to be unneccessarily dropped) - * - * v0.9.4 27th April 1996 (SC) - * First attempt at using "&COMMAND" Starmode AT commands - * - * v0.9.5 29th May 1996 (SC) - * First attempt at sending (unicast) ARP packets - * - * v0.9.6 5th June 1996 (Elliot) - * Put "message level" tags in every "printk" statement - * - * v0.9.7 13th June 1996 (laik) - * Added support for the /proc fs - * - * v0.9.8 July 1996 (Mema) - * Added packet logging - * - * v1.0 November 1996 (SC) - * Fixed (severe) memory leaks in the /proc fs code - * Fixed race conditions in the logging code - * - * v1.1 January 1997 (SC) - * Deleted packet logging (use tcpdump instead) - * Added support for Metricom Firmware v204 features - * (like message checksums) - * - * v1.2 January 1997 (SC) - * Put portables list back in - * - * v1.3 July 1997 (SC) - * Made STRIP driver set the radio's baud rate automatically. - * It is no longer necessarily to manually set the radio's - * rate permanently to 115200 -- the driver handles setting - * the rate automatically. - */ - -#ifdef MODULE -static const char StripVersion[] = "1.3A-STUART.CHESHIRE-MODULAR"; -#else -static const char StripVersion[] = "1.3A-STUART.CHESHIRE"; -#endif - -#define TICKLE_TIMERS 0 -#define EXT_COUNTERS 1 - - -/************************************************************************/ -/* Header files */ - -#include -#include -#include -#include -#include -#include - -# include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/************************************************************************/ -/* Useful structures and definitions */ - -/* - * A MetricomKey identifies the protocol being carried inside a Metricom - * Starmode packet. - */ - -typedef union { - __u8 c[4]; - __u32 l; -} MetricomKey; - -/* - * An IP address can be viewed as four bytes in memory (which is what it is) or as - * a single 32-bit long (which is convenient for assignment, equality testing etc.) - */ - -typedef union { - __u8 b[4]; - __u32 l; -} IPaddr; - -/* - * A MetricomAddressString is used to hold a printable representation of - * a Metricom address. - */ - -typedef struct { - __u8 c[24]; -} MetricomAddressString; - -/* Encapsulation can expand packet of size x to 65/64x + 1 - * Sent packet looks like "*
*" - * 1 1 1-18 1 4 ? 1 - * eg. *0000-1234*SIP0 - * We allow 31 bytes for the stars, the key, the address and the s - */ -#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) - -/* - * A STRIP_Header is never really sent over the radio, but making a dummy - * header for internal use within the kernel that looks like an Ethernet - * header makes certain other software happier. For example, tcpdump - * already understands Ethernet headers. - */ - -typedef struct { - MetricomAddress dst_addr; /* Destination address, e.g. "0000-1234" */ - MetricomAddress src_addr; /* Source address, e.g. "0000-5678" */ - unsigned short protocol; /* The protocol type, using Ethernet codes */ -} STRIP_Header; - -typedef struct { - char c[60]; -} MetricomNode; - -#define NODE_TABLE_SIZE 32 -typedef struct { - struct timeval timestamp; - int num_nodes; - MetricomNode node[NODE_TABLE_SIZE]; -} MetricomNodeTable; - -enum { FALSE = 0, TRUE = 1 }; - -/* - * Holds the radio's firmware version. - */ -typedef struct { - char c[50]; -} FirmwareVersion; - -/* - * Holds the radio's serial number. - */ -typedef struct { - char c[18]; -} SerialNumber; - -/* - * Holds the radio's battery voltage. - */ -typedef struct { - char c[11]; -} BatteryVoltage; - -typedef struct { - char c[8]; -} char8; - -enum { - NoStructure = 0, /* Really old firmware */ - StructuredMessages = 1, /* Parsable AT response msgs */ - ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */ -}; - -struct strip { - int magic; - /* - * These are pointers to the malloc()ed frame buffers. - */ - - unsigned char *rx_buff; /* buffer for received IP packet */ - unsigned char *sx_buff; /* buffer for received serial data */ - int sx_count; /* received serial data counter */ - int sx_size; /* Serial buffer size */ - unsigned char *tx_buff; /* transmitter buffer */ - unsigned char *tx_head; /* pointer to next byte to XMIT */ - int tx_left; /* bytes left in XMIT queue */ - int tx_size; /* Serial buffer size */ - - /* - * STRIP interface statistics. - */ - - unsigned long rx_packets; /* inbound frames counter */ - unsigned long tx_packets; /* outbound frames counter */ - unsigned long rx_errors; /* Parity, etc. errors */ - unsigned long tx_errors; /* Planned stuff */ - unsigned long rx_dropped; /* No memory for skb */ - unsigned long tx_dropped; /* When MTU change */ - unsigned long rx_over_errors; /* Frame bigger than STRIP buf. */ - - unsigned long pps_timer; /* Timer to determine pps */ - unsigned long rx_pps_count; /* Counter to determine pps */ - unsigned long tx_pps_count; /* Counter to determine pps */ - unsigned long sx_pps_count; /* Counter to determine pps */ - unsigned long rx_average_pps; /* rx packets per second * 8 */ - unsigned long tx_average_pps; /* tx packets per second * 8 */ - unsigned long sx_average_pps; /* sent packets per second * 8 */ - -#ifdef EXT_COUNTERS - unsigned long rx_bytes; /* total received bytes */ - unsigned long tx_bytes; /* total received bytes */ - unsigned long rx_rbytes; /* bytes thru radio i/f */ - unsigned long tx_rbytes; /* bytes thru radio i/f */ - unsigned long rx_sbytes; /* tot bytes thru serial i/f */ - unsigned long tx_sbytes; /* tot bytes thru serial i/f */ - unsigned long rx_ebytes; /* tot stat/err bytes */ - unsigned long tx_ebytes; /* tot stat/err bytes */ -#endif - - /* - * Internal variables. - */ - - struct list_head list; /* Linked list of devices */ - - int discard; /* Set if serial error */ - int working; /* Is radio working correctly? */ - int firmware_level; /* Message structuring level */ - int next_command; /* Next periodic command */ - unsigned int user_baud; /* The user-selected baud rate */ - int mtu; /* Our mtu (to spot changes!) */ - long watchdog_doprobe; /* Next time to test the radio */ - long watchdog_doreset; /* Time to do next reset */ - long gratuitous_arp; /* Time to send next ARP refresh */ - long arp_interval; /* Next ARP interval */ - struct timer_list idle_timer; /* For periodic wakeup calls */ - MetricomAddress true_dev_addr; /* True address of radio */ - int manual_dev_addr; /* Hack: See note below */ - - FirmwareVersion firmware_version; /* The radio's firmware version */ - SerialNumber serial_number; /* The radio's serial number */ - BatteryVoltage battery_voltage; /* The radio's battery voltage */ - - /* - * Other useful structures. - */ - - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* Our device structure */ - - /* - * Neighbour radio records - */ - - MetricomNodeTable portables; - MetricomNodeTable poletops; -}; - -/* - * Note: manual_dev_addr hack - * - * It is not possible to change the hardware address of a Metricom radio, - * or to send packets with a user-specified hardware source address, thus - * trying to manually set a hardware source address is a questionable - * thing to do. However, if the user *does* manually set the hardware - * source address of a STRIP interface, then the kernel will believe it, - * and use it in certain places. For example, the hardware address listed - * by ifconfig will be the manual address, not the true one. - * (Both addresses are listed in /proc/net/strip.) - * Also, ARP packets will be sent out giving the user-specified address as - * the source address, not the real address. This is dangerous, because - * it means you won't receive any replies -- the ARP replies will go to - * the specified address, which will be some other radio. The case where - * this is useful is when that other radio is also connected to the same - * machine. This allows you to connect a pair of radios to one machine, - * and to use one exclusively for inbound traffic, and the other - * exclusively for outbound traffic. Pretty neat, huh? - * - * Here's the full procedure to set this up: - * - * 1. "slattach" two interfaces, e.g. st0 for outgoing packets, - * and st1 for incoming packets - * - * 2. "ifconfig" st0 (outbound radio) to have the hardware address - * which is the real hardware address of st1 (inbound radio). - * Now when it sends out packets, it will masquerade as st1, and - * replies will be sent to that radio, which is exactly what we want. - * - * 3. Set the route table entry ("route add default ..." or - * "route add -net ...", as appropriate) to send packets via the st0 - * interface (outbound radio). Do not add any route which sends packets - * out via the st1 interface -- that radio is for inbound traffic only. - * - * 4. "ifconfig" st1 (inbound radio) to have hardware address zero. - * This tells the STRIP driver to "shut down" that interface and not - * send any packets through it. In particular, it stops sending the - * periodic gratuitous ARP packets that a STRIP interface normally sends. - * Also, when packets arrive on that interface, it will search the - * interface list to see if there is another interface who's manual - * hardware address matches its own real address (i.e. st0 in this - * example) and if so it will transfer ownership of the skbuff to - * that interface, so that it looks to the kernel as if the packet - * arrived on that interface. This is necessary because when the - * kernel sends an ARP packet on st0, it expects to get a reply on - * st0, and if it sees the reply come from st1 then it will ignore - * it (to be accurate, it puts the entry in the ARP table, but - * labelled in such a way that st0 can't use it). - * - * Thanks to Petros Maniatis for coming up with the idea of splitting - * inbound and outbound traffic between two interfaces, which turned - * out to be really easy to implement, even if it is a bit of a hack. - * - * Having set a manual address on an interface, you can restore it - * to automatic operation (where the address is automatically kept - * consistent with the real address of the radio) by setting a manual - * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF" - * This 'turns off' manual override mode for the device address. - * - * Note: The IEEE 802 headers reported in tcpdump will show the *real* - * radio addresses the packets were sent and received from, so that you - * can see what is really going on with packets, and which interfaces - * they are really going through. - */ - - -/************************************************************************/ -/* Constants */ - -/* - * CommandString1 works on all radios - * Other CommandStrings are only used with firmware that provides structured responses. - * - * ats319=1 Enables Info message for node additions and deletions - * ats319=2 Enables Info message for a new best node - * ats319=4 Enables checksums - * ats319=8 Enables ACK messages - */ - -static const int MaxCommandStringLength = 32; -static const int CompatibilityCommand = 1; - -static const char CommandString0[] = "*&COMMAND*ATS319=7"; /* Turn on checksums & info messages */ -static const char CommandString1[] = "*&COMMAND*ATS305?"; /* Query radio name */ -static const char CommandString2[] = "*&COMMAND*ATS325?"; /* Query battery voltage */ -static const char CommandString3[] = "*&COMMAND*ATS300?"; /* Query version information */ -static const char CommandString4[] = "*&COMMAND*ATS311?"; /* Query poletop list */ -static const char CommandString5[] = "*&COMMAND*AT~LA"; /* Query portables list */ -typedef struct { - const char *string; - long length; -} StringDescriptor; - -static const StringDescriptor CommandString[] = { - {CommandString0, sizeof(CommandString0) - 1}, - {CommandString1, sizeof(CommandString1) - 1}, - {CommandString2, sizeof(CommandString2) - 1}, - {CommandString3, sizeof(CommandString3) - 1}, - {CommandString4, sizeof(CommandString4) - 1}, - {CommandString5, sizeof(CommandString5) - 1} -}; - -#define GOT_ALL_RADIO_INFO(S) \ - ((S)->firmware_version.c[0] && \ - (S)->battery_voltage.c[0] && \ - memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address))) - -static const char hextable[16] = "0123456789ABCDEF"; - -static const MetricomAddress zero_address; -static const MetricomAddress broadcast_address = - { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }; - -static const MetricomKey SIP0Key = { "SIP0" }; -static const MetricomKey ARP0Key = { "ARP0" }; -static const MetricomKey ATR_Key = { "ATR " }; -static const MetricomKey ACK_Key = { "ACK_" }; -static const MetricomKey INF_Key = { "INF_" }; -static const MetricomKey ERR_Key = { "ERR_" }; - -static const long MaxARPInterval = 60 * HZ; /* One minute */ - -/* - * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for - * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion - * for STRIP encoding, that translates to a maximum payload MTU of 1155. - * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes - * long, including IP header, UDP header, and NFS header. Setting the STRIP - * MTU to 1152 allows us to send default sized NFS packets without fragmentation. - */ -static const unsigned short MAX_SEND_MTU = 1152; -static const unsigned short MAX_RECV_MTU = 1500; /* Hoping for Ethernet sized packets in the future! */ -static const unsigned short DEFAULT_STRIP_MTU = 1152; -static const int STRIP_MAGIC = 0x5303; -static const long LongTime = 0x7FFFFFFF; - -/************************************************************************/ -/* Global variables */ - -static LIST_HEAD(strip_list); -static DEFINE_SPINLOCK(strip_lock); - -/************************************************************************/ -/* Macros */ - -/* Returns TRUE if text T begins with prefix P */ -#define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1)) - -/* Returns TRUE if text T of length L is equal to string S */ -#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1)) - -#define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \ - (X)>='a' && (X)<='f' ? (X)-'a'+10 : \ - (X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 ) - -#define READHEX16(X) ((__u16)(READHEX(X))) - -#define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0) - -#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) - -#define JIFFIE_TO_SEC(X) ((X) / HZ) - - -/************************************************************************/ -/* Utility routines */ - -static int arp_query(unsigned char *haddr, u32 paddr, - struct net_device *dev) -{ - struct neighbour *neighbor_entry; - int ret = 0; - - neighbor_entry = neigh_lookup(&arp_tbl, &paddr, dev); - - if (neighbor_entry != NULL) { - neighbor_entry->used = jiffies; - if (neighbor_entry->nud_state & NUD_VALID) { - memcpy(haddr, neighbor_entry->ha, dev->addr_len); - ret = 1; - } - neigh_release(neighbor_entry); - } - return ret; -} - -static void DumpData(char *msg, struct strip *strip_info, __u8 * ptr, - __u8 * end) -{ - static const int MAX_DumpData = 80; - __u8 pkt_text[MAX_DumpData], *p = pkt_text; - - *p++ = '\"'; - - while (ptr < end && p < &pkt_text[MAX_DumpData - 4]) { - if (*ptr == '\\') { - *p++ = '\\'; - *p++ = '\\'; - } else { - if (*ptr >= 32 && *ptr <= 126) { - *p++ = *ptr; - } else { - sprintf(p, "\\%02X", *ptr); - p += 3; - } - } - ptr++; - } - - if (ptr == end) - *p++ = '\"'; - *p++ = 0; - - printk(KERN_INFO "%s: %-13s%s\n", strip_info->dev->name, msg, pkt_text); -} - - -/************************************************************************/ -/* Byte stuffing/unstuffing routines */ - -/* Stuffing scheme: - * 00 Unused (reserved character) - * 01-3F Run of 2-64 different characters - * 40-7F Run of 1-64 different characters plus a single zero at the end - * 80-BF Run of 1-64 of the same character - * C0-FF Run of 1-64 zeroes (ASCII 0) - */ - -typedef enum { - Stuff_Diff = 0x00, - Stuff_DiffZero = 0x40, - Stuff_Same = 0x80, - Stuff_Zero = 0xC0, - Stuff_NoCode = 0xFF, /* Special code, meaning no code selected */ - - Stuff_CodeMask = 0xC0, - Stuff_CountMask = 0x3F, - Stuff_MaxCount = 0x3F, - Stuff_Magic = 0x0D /* The value we are eliminating */ -} StuffingCode; - -/* StuffData encodes the data starting at "src" for "length" bytes. - * It writes it to the buffer pointed to by "dst" (which must be at least - * as long as 1 + 65/64 of the input length). The output may be up to 1.6% - * larger than the input for pathological input, but will usually be smaller. - * StuffData returns the new value of the dst pointer as its result. - * "code_ptr_ptr" points to a "__u8 *" which is used to hold encoding state - * between calls, allowing an encoded packet to be incrementally built up - * from small parts. On the first call, the "__u8 *" pointed to should be - * initialized to NULL; between subsequent calls the calling routine should - * leave the value alone and simply pass it back unchanged so that the - * encoder can recover its current state. - */ - -#define StuffData_FinishBlock(X) \ -(*code_ptr = (X) ^ Stuff_Magic, code = Stuff_NoCode) - -static __u8 *StuffData(__u8 * src, __u32 length, __u8 * dst, - __u8 ** code_ptr_ptr) -{ - __u8 *end = src + length; - __u8 *code_ptr = *code_ptr_ptr; - __u8 code = Stuff_NoCode, count = 0; - - if (!length) - return (dst); - - if (code_ptr) { - /* - * Recover state from last call, if applicable - */ - code = (*code_ptr ^ Stuff_Magic) & Stuff_CodeMask; - count = (*code_ptr ^ Stuff_Magic) & Stuff_CountMask; - } - - while (src < end) { - switch (code) { - /* Stuff_NoCode: If no current code, select one */ - case Stuff_NoCode: - /* Record where we're going to put this code */ - code_ptr = dst++; - count = 0; /* Reset the count (zero means one instance) */ - /* Tentatively start a new block */ - if (*src == 0) { - code = Stuff_Zero; - src++; - } else { - code = Stuff_Same; - *dst++ = *src++ ^ Stuff_Magic; - } - /* Note: We optimistically assume run of same -- */ - /* which will be fixed later in Stuff_Same */ - /* if it turns out not to be true. */ - break; - - /* Stuff_Zero: We already have at least one zero encoded */ - case Stuff_Zero: - /* If another zero, count it, else finish this code block */ - if (*src == 0) { - count++; - src++; - } else { - StuffData_FinishBlock(Stuff_Zero + count); - } - break; - - /* Stuff_Same: We already have at least one byte encoded */ - case Stuff_Same: - /* If another one the same, count it */ - if ((*src ^ Stuff_Magic) == code_ptr[1]) { - count++; - src++; - break; - } - /* else, this byte does not match this block. */ - /* If we already have two or more bytes encoded, finish this code block */ - if (count) { - StuffData_FinishBlock(Stuff_Same + count); - break; - } - /* else, we only have one so far, so switch to Stuff_Diff code */ - code = Stuff_Diff; - /* and fall through to Stuff_Diff case below - * Note cunning cleverness here: case Stuff_Diff compares - * the current character with the previous two to see if it - * has a run of three the same. Won't this be an error if - * there aren't two previous characters stored to compare with? - * No. Because we know the current character is *not* the same - * as the previous one, the first test below will necessarily - * fail and the send half of the "if" won't be executed. - */ - - /* Stuff_Diff: We have at least two *different* bytes encoded */ - case Stuff_Diff: - /* If this is a zero, must encode a Stuff_DiffZero, and begin a new block */ - if (*src == 0) { - StuffData_FinishBlock(Stuff_DiffZero + - count); - } - /* else, if we have three in a row, it is worth starting a Stuff_Same block */ - else if ((*src ^ Stuff_Magic) == dst[-1] - && dst[-1] == dst[-2]) { - /* Back off the last two characters we encoded */ - code += count - 2; - /* Note: "Stuff_Diff + 0" is an illegal code */ - if (code == Stuff_Diff + 0) { - code = Stuff_Same + 0; - } - StuffData_FinishBlock(code); - code_ptr = dst - 2; - /* dst[-1] already holds the correct value */ - count = 2; /* 2 means three bytes encoded */ - code = Stuff_Same; - } - /* else, another different byte, so add it to the block */ - else { - *dst++ = *src ^ Stuff_Magic; - count++; - } - src++; /* Consume the byte */ - break; - } - if (count == Stuff_MaxCount) { - StuffData_FinishBlock(code + count); - } - } - if (code == Stuff_NoCode) { - *code_ptr_ptr = NULL; - } else { - *code_ptr_ptr = code_ptr; - StuffData_FinishBlock(code + count); - } - return (dst); -} - -/* - * UnStuffData decodes the data at "src", up to (but not including) "end". - * It writes the decoded data into the buffer pointed to by "dst", up to a - * maximum of "dst_length", and returns the new value of "src" so that a - * follow-on call can read more data, continuing from where the first left off. - * - * There are three types of results: - * 1. The source data runs out before extracting "dst_length" bytes: - * UnStuffData returns NULL to indicate failure. - * 2. The source data produces exactly "dst_length" bytes: - * UnStuffData returns new_src = end to indicate that all bytes were consumed. - * 3. "dst_length" bytes are extracted, with more remaining. - * UnStuffData returns new_src < end to indicate that there are more bytes - * to be read. - * - * Note: The decoding may be destructive, in that it may alter the source - * data in the process of decoding it (this is necessary to allow a follow-on - * call to resume correctly). - */ - -static __u8 *UnStuffData(__u8 * src, __u8 * end, __u8 * dst, - __u32 dst_length) -{ - __u8 *dst_end = dst + dst_length; - /* Sanity check */ - if (!src || !end || !dst || !dst_length) - return (NULL); - while (src < end && dst < dst_end) { - int count = (*src ^ Stuff_Magic) & Stuff_CountMask; - switch ((*src ^ Stuff_Magic) & Stuff_CodeMask) { - case Stuff_Diff: - if (src + 1 + count >= end) - return (NULL); - do { - *dst++ = *++src ^ Stuff_Magic; - } - while (--count >= 0 && dst < dst_end); - if (count < 0) - src += 1; - else { - if (count == 0) - *src = Stuff_Same ^ Stuff_Magic; - else - *src = - (Stuff_Diff + - count) ^ Stuff_Magic; - } - break; - case Stuff_DiffZero: - if (src + 1 + count >= end) - return (NULL); - do { - *dst++ = *++src ^ Stuff_Magic; - } - while (--count >= 0 && dst < dst_end); - if (count < 0) - *src = Stuff_Zero ^ Stuff_Magic; - else - *src = - (Stuff_DiffZero + count) ^ Stuff_Magic; - break; - case Stuff_Same: - if (src + 1 >= end) - return (NULL); - do { - *dst++ = src[1] ^ Stuff_Magic; - } - while (--count >= 0 && dst < dst_end); - if (count < 0) - src += 2; - else - *src = (Stuff_Same + count) ^ Stuff_Magic; - break; - case Stuff_Zero: - do { - *dst++ = 0; - } - while (--count >= 0 && dst < dst_end); - if (count < 0) - src += 1; - else - *src = (Stuff_Zero + count) ^ Stuff_Magic; - break; - } - } - if (dst < dst_end) - return (NULL); - else - return (src); -} - - -/************************************************************************/ -/* General routines for STRIP */ - -/* - * set_baud sets the baud rate to the rate defined by baudcode - */ -static void set_baud(struct tty_struct *tty, speed_t baudrate) -{ - struct ktermios old_termios; - - mutex_lock(&tty->termios_mutex); - old_termios =*(tty->termios); - tty_encode_baud_rate(tty, baudrate, baudrate); - tty->ops->set_termios(tty, &old_termios); - mutex_unlock(&tty->termios_mutex); -} - -/* - * Convert a string to a Metricom Address. - */ - -#define IS_RADIO_ADDRESS(p) ( \ - isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ - (p)[4] == '-' && \ - isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) - -static int string_to_radio_address(MetricomAddress * addr, __u8 * p) -{ - if (!IS_RADIO_ADDRESS(p)) - return (1); - addr->c[0] = 0; - addr->c[1] = 0; - addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]); - addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]); - addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]); - addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]); - return (0); -} - -/* - * Convert a Metricom Address to a string. - */ - -static __u8 *radio_address_to_string(const MetricomAddress * addr, - MetricomAddressString * p) -{ - sprintf(p->c, "%02X%02X-%02X%02X", addr->c[2], addr->c[3], - addr->c[4], addr->c[5]); - return (p->c); -} - -/* - * Note: Must make sure sx_size is big enough to receive a stuffed - * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's - * big enough to receive a large radio neighbour list (currently 4K). - */ - -static int allocate_buffers(struct strip *strip_info, int mtu) -{ - struct net_device *dev = strip_info->dev; - int sx_size = max_t(int, STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); - int tx_size = STRIP_ENCAP_SIZE(mtu) + MaxCommandStringLength; - __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); - __u8 *s = kmalloc(sx_size, GFP_ATOMIC); - __u8 *t = kmalloc(tx_size, GFP_ATOMIC); - if (r && s && t) { - strip_info->rx_buff = r; - strip_info->sx_buff = s; - strip_info->tx_buff = t; - strip_info->sx_size = sx_size; - strip_info->tx_size = tx_size; - strip_info->mtu = dev->mtu = mtu; - return (1); - } - kfree(r); - kfree(s); - kfree(t); - return (0); -} - -/* - * MTU has been changed by the IP layer. - * We could be in - * an upcall from the tty driver, or in an ip packet queue. - */ -static int strip_change_mtu(struct net_device *dev, int new_mtu) -{ - struct strip *strip_info = netdev_priv(dev); - int old_mtu = strip_info->mtu; - unsigned char *orbuff = strip_info->rx_buff; - unsigned char *osbuff = strip_info->sx_buff; - unsigned char *otbuff = strip_info->tx_buff; - - if (new_mtu > MAX_SEND_MTU) { - printk(KERN_ERR - "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", - strip_info->dev->name, MAX_SEND_MTU); - return -EINVAL; - } - - spin_lock_bh(&strip_lock); - if (!allocate_buffers(strip_info, new_mtu)) { - printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n", - strip_info->dev->name); - spin_unlock_bh(&strip_lock); - return -ENOMEM; - } - - if (strip_info->sx_count) { - if (strip_info->sx_count <= strip_info->sx_size) - memcpy(strip_info->sx_buff, osbuff, - strip_info->sx_count); - else { - strip_info->discard = strip_info->sx_count; - strip_info->rx_over_errors++; - } - } - - if (strip_info->tx_left) { - if (strip_info->tx_left <= strip_info->tx_size) - memcpy(strip_info->tx_buff, strip_info->tx_head, - strip_info->tx_left); - else { - strip_info->tx_left = 0; - strip_info->tx_dropped++; - } - } - strip_info->tx_head = strip_info->tx_buff; - spin_unlock_bh(&strip_lock); - - printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n", - strip_info->dev->name, old_mtu, strip_info->mtu); - - kfree(orbuff); - kfree(osbuff); - kfree(otbuff); - return 0; -} - -static void strip_unlock(struct strip *strip_info) -{ - /* - * Set the timer to go off in one second. - */ - strip_info->idle_timer.expires = jiffies + 1 * HZ; - add_timer(&strip_info->idle_timer); - netif_wake_queue(strip_info->dev); -} - - - -/* - * If the time is in the near future, time_delta prints the number of - * seconds to go into the buffer and returns the address of the buffer. - * If the time is not in the near future, it returns the address of the - * string "Not scheduled" The buffer must be long enough to contain the - * ascii representation of the number plus 9 charactes for the " seconds" - * and the null character. - */ -#ifdef CONFIG_PROC_FS -static char *time_delta(char buffer[], long time) -{ - time -= jiffies; - if (time > LongTime / 2) - return ("Not scheduled"); - if (time < 0) - time = 0; /* Don't print negative times */ - sprintf(buffer, "%ld seconds", time / HZ); - return (buffer); -} - -/* get Nth element of the linked list */ -static struct strip *strip_get_idx(loff_t pos) -{ - struct strip *str; - int i = 0; - - list_for_each_entry_rcu(str, &strip_list, list) { - if (pos == i) - return str; - ++i; - } - return NULL; -} - -static void *strip_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - rcu_read_lock(); - return *pos ? strip_get_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *strip_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct list_head *l; - struct strip *s; - - ++*pos; - if (v == SEQ_START_TOKEN) - return strip_get_idx(1); - - s = v; - l = &s->list; - list_for_each_continue_rcu(l, &strip_list) { - return list_entry(l, struct strip, list); - } - return NULL; -} - -static void strip_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - -static void strip_seq_neighbours(struct seq_file *seq, - const MetricomNodeTable * table, - const char *title) -{ - /* We wrap this in a do/while loop, so if the table changes */ - /* while we're reading it, we just go around and try again. */ - struct timeval t; - - do { - int i; - t = table->timestamp; - if (table->num_nodes) - seq_printf(seq, "\n %s\n", title); - for (i = 0; i < table->num_nodes; i++) { - MetricomNode node; - - spin_lock_bh(&strip_lock); - node = table->node[i]; - spin_unlock_bh(&strip_lock); - seq_printf(seq, " %s\n", node.c); - } - } while (table->timestamp.tv_sec != t.tv_sec - || table->timestamp.tv_usec != t.tv_usec); -} - -/* - * This function prints radio status information via the seq_file - * interface. The interface takes care of buffer size and over - * run issues. - * - * The buffer in seq_file is PAGESIZE (4K) - * so this routine should never print more or it will get truncated. - * With the maximum of 32 portables and 32 poletops - * reported, the routine outputs 3107 bytes into the buffer. - */ -static void strip_seq_status_info(struct seq_file *seq, - const struct strip *strip_info) -{ - char temp[32]; - MetricomAddressString addr_string; - - /* First, we must copy all of our data to a safe place, */ - /* in case a serial interrupt comes in and changes it. */ - int tx_left = strip_info->tx_left; - unsigned long rx_average_pps = strip_info->rx_average_pps; - unsigned long tx_average_pps = strip_info->tx_average_pps; - unsigned long sx_average_pps = strip_info->sx_average_pps; - int working = strip_info->working; - int firmware_level = strip_info->firmware_level; - long watchdog_doprobe = strip_info->watchdog_doprobe; - long watchdog_doreset = strip_info->watchdog_doreset; - long gratuitous_arp = strip_info->gratuitous_arp; - long arp_interval = strip_info->arp_interval; - FirmwareVersion firmware_version = strip_info->firmware_version; - SerialNumber serial_number = strip_info->serial_number; - BatteryVoltage battery_voltage = strip_info->battery_voltage; - char *if_name = strip_info->dev->name; - MetricomAddress true_dev_addr = strip_info->true_dev_addr; - MetricomAddress dev_dev_addr = - *(MetricomAddress *) strip_info->dev->dev_addr; - int manual_dev_addr = strip_info->manual_dev_addr; -#ifdef EXT_COUNTERS - unsigned long rx_bytes = strip_info->rx_bytes; - unsigned long tx_bytes = strip_info->tx_bytes; - unsigned long rx_rbytes = strip_info->rx_rbytes; - unsigned long tx_rbytes = strip_info->tx_rbytes; - unsigned long rx_sbytes = strip_info->rx_sbytes; - unsigned long tx_sbytes = strip_info->tx_sbytes; - unsigned long rx_ebytes = strip_info->rx_ebytes; - unsigned long tx_ebytes = strip_info->tx_ebytes; -#endif - - seq_printf(seq, "\nInterface name\t\t%s\n", if_name); - seq_printf(seq, " Radio working:\t\t%s\n", working ? "Yes" : "No"); - radio_address_to_string(&true_dev_addr, &addr_string); - seq_printf(seq, " Radio address:\t\t%s\n", addr_string.c); - if (manual_dev_addr) { - radio_address_to_string(&dev_dev_addr, &addr_string); - seq_printf(seq, " Device address:\t%s\n", addr_string.c); - } - seq_printf(seq, " Firmware version:\t%s", !working ? "Unknown" : - !firmware_level ? "Should be upgraded" : - firmware_version.c); - if (firmware_level >= ChecksummedMessages) - seq_printf(seq, " (Checksums Enabled)"); - seq_printf(seq, "\n"); - seq_printf(seq, " Serial number:\t\t%s\n", serial_number.c); - seq_printf(seq, " Battery voltage:\t%s\n", battery_voltage.c); - seq_printf(seq, " Transmit queue (bytes):%d\n", tx_left); - seq_printf(seq, " Receive packet rate: %ld packets per second\n", - rx_average_pps / 8); - seq_printf(seq, " Transmit packet rate: %ld packets per second\n", - tx_average_pps / 8); - seq_printf(seq, " Sent packet rate: %ld packets per second\n", - sx_average_pps / 8); - seq_printf(seq, " Next watchdog probe:\t%s\n", - time_delta(temp, watchdog_doprobe)); - seq_printf(seq, " Next watchdog reset:\t%s\n", - time_delta(temp, watchdog_doreset)); - seq_printf(seq, " Next gratuitous ARP:\t"); - - if (!memcmp - (strip_info->dev->dev_addr, zero_address.c, - sizeof(zero_address))) - seq_printf(seq, "Disabled\n"); - else { - seq_printf(seq, "%s\n", time_delta(temp, gratuitous_arp)); - seq_printf(seq, " Next ARP interval:\t%ld seconds\n", - JIFFIE_TO_SEC(arp_interval)); - } - - if (working) { -#ifdef EXT_COUNTERS - seq_printf(seq, "\n"); - seq_printf(seq, - " Total bytes: \trx:\t%lu\ttx:\t%lu\n", - rx_bytes, tx_bytes); - seq_printf(seq, - " thru radio: \trx:\t%lu\ttx:\t%lu\n", - rx_rbytes, tx_rbytes); - seq_printf(seq, - " thru serial port: \trx:\t%lu\ttx:\t%lu\n", - rx_sbytes, tx_sbytes); - seq_printf(seq, - " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", - rx_ebytes, tx_ebytes); -#endif - strip_seq_neighbours(seq, &strip_info->poletops, - "Poletops:"); - strip_seq_neighbours(seq, &strip_info->portables, - "Portables:"); - } -} - -/* - * This function is exports status information from the STRIP driver through - * the /proc file system. - */ -static int strip_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_printf(seq, "strip_version: %s\n", StripVersion); - else - strip_seq_status_info(seq, (const struct strip *)v); - return 0; -} - - -static const struct seq_operations strip_seq_ops = { - .start = strip_seq_start, - .next = strip_seq_next, - .stop = strip_seq_stop, - .show = strip_seq_show, -}; - -static int strip_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &strip_seq_ops); -} - -static const struct file_operations strip_seq_fops = { - .owner = THIS_MODULE, - .open = strip_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif - - - -/************************************************************************/ -/* Sending routines */ - -static void ResetRadio(struct strip *strip_info) -{ - struct tty_struct *tty = strip_info->tty; - static const char init[] = "ate0q1dt**starmode\r**"; - StringDescriptor s = { init, sizeof(init) - 1 }; - - /* - * If the radio isn't working anymore, - * we should clear the old status information. - */ - if (strip_info->working) { - printk(KERN_INFO "%s: No response: Resetting radio.\n", - strip_info->dev->name); - strip_info->firmware_version.c[0] = '\0'; - strip_info->serial_number.c[0] = '\0'; - strip_info->battery_voltage.c[0] = '\0'; - strip_info->portables.num_nodes = 0; - do_gettimeofday(&strip_info->portables.timestamp); - strip_info->poletops.num_nodes = 0; - do_gettimeofday(&strip_info->poletops.timestamp); - } - - strip_info->pps_timer = jiffies; - strip_info->rx_pps_count = 0; - strip_info->tx_pps_count = 0; - strip_info->sx_pps_count = 0; - strip_info->rx_average_pps = 0; - strip_info->tx_average_pps = 0; - strip_info->sx_average_pps = 0; - - /* Mark radio address as unknown */ - *(MetricomAddress *) & strip_info->true_dev_addr = zero_address; - if (!strip_info->manual_dev_addr) - *(MetricomAddress *) strip_info->dev->dev_addr = - zero_address; - strip_info->working = FALSE; - strip_info->firmware_level = NoStructure; - strip_info->next_command = CompatibilityCommand; - strip_info->watchdog_doprobe = jiffies + 10 * HZ; - strip_info->watchdog_doreset = jiffies + 1 * HZ; - - /* If the user has selected a baud rate above 38.4 see what magic we have to do */ - if (strip_info->user_baud > 38400) { - /* - * Subtle stuff: Pay attention :-) - * If the serial port is currently at the user's selected (>38.4) rate, - * then we temporarily switch to 19.2 and issue the ATS304 command - * to tell the radio to switch to the user's selected rate. - * If the serial port is not currently at that rate, that means we just - * issued the ATS304 command last time through, so this time we restore - * the user's selected rate and issue the normal starmode reset string. - */ - if (strip_info->user_baud == tty_get_baud_rate(tty)) { - static const char b0[] = "ate0q1s304=57600\r"; - static const char b1[] = "ate0q1s304=115200\r"; - static const StringDescriptor baudstring[2] = - { {b0, sizeof(b0) - 1} - , {b1, sizeof(b1) - 1} - }; - set_baud(tty, 19200); - if (strip_info->user_baud == 57600) - s = baudstring[0]; - else if (strip_info->user_baud == 115200) - s = baudstring[1]; - else - s = baudstring[1]; /* For now */ - } else - set_baud(tty, strip_info->user_baud); - } - - tty->ops->write(tty, s.string, s.length); -#ifdef EXT_COUNTERS - strip_info->tx_ebytes += s.length; -#endif -} - -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ - -static void strip_write_some_more(struct tty_struct *tty) -{ - struct strip *strip_info = tty->disc_data; - - /* First make sure we're connected. */ - if (!strip_info || strip_info->magic != STRIP_MAGIC || - !netif_running(strip_info->dev)) - return; - - if (strip_info->tx_left > 0) { - int num_written = - tty->ops->write(tty, strip_info->tx_head, - strip_info->tx_left); - strip_info->tx_left -= num_written; - strip_info->tx_head += num_written; -#ifdef EXT_COUNTERS - strip_info->tx_sbytes += num_written; -#endif - } else { /* Else start transmission of another packet */ - - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - strip_unlock(strip_info); - } -} - -static __u8 *add_checksum(__u8 * buffer, __u8 * end) -{ - __u16 sum = 0; - __u8 *p = buffer; - while (p < end) - sum += *p++; - end[3] = hextable[sum & 0xF]; - sum >>= 4; - end[2] = hextable[sum & 0xF]; - sum >>= 4; - end[1] = hextable[sum & 0xF]; - sum >>= 4; - end[0] = hextable[sum & 0xF]; - return (end + 4); -} - -static unsigned char *strip_make_packet(unsigned char *buffer, - struct strip *strip_info, - struct sk_buff *skb) -{ - __u8 *ptr = buffer; - __u8 *stuffstate = NULL; - STRIP_Header *header = (STRIP_Header *) skb->data; - MetricomAddress haddr = header->dst_addr; - int len = skb->len - sizeof(STRIP_Header); - MetricomKey key; - - /*HexDump("strip_make_packet", strip_info, skb->data, skb->data + skb->len); */ - - if (header->protocol == htons(ETH_P_IP)) - key = SIP0Key; - else if (header->protocol == htons(ETH_P_ARP)) - key = ARP0Key; - else { - printk(KERN_ERR - "%s: strip_make_packet: Unknown packet type 0x%04X\n", - strip_info->dev->name, ntohs(header->protocol)); - return (NULL); - } - - if (len > strip_info->mtu) { - printk(KERN_ERR - "%s: Dropping oversized transmit packet: %d bytes\n", - strip_info->dev->name, len); - return (NULL); - } - - /* - * If we're sending to ourselves, discard the packet. - * (Metricom radios choke if they try to send a packet to their own address.) - */ - if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) { - printk(KERN_ERR "%s: Dropping packet addressed to self\n", - strip_info->dev->name); - return (NULL); - } - - /* - * If this is a broadcast packet, send it to our designated Metricom - * 'broadcast hub' radio (First byte of address being 0xFF means broadcast) - */ - if (haddr.c[0] == 0xFF) { - __be32 brd = 0; - struct in_device *in_dev; - - rcu_read_lock(); - in_dev = __in_dev_get_rcu(strip_info->dev); - if (in_dev == NULL) { - rcu_read_unlock(); - return NULL; - } - if (in_dev->ifa_list) - brd = in_dev->ifa_list->ifa_broadcast; - rcu_read_unlock(); - - /* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */ - if (!arp_query(haddr.c, brd, strip_info->dev)) { - printk(KERN_ERR - "%s: Unable to send packet (no broadcast hub configured)\n", - strip_info->dev->name); - return (NULL); - } - /* - * If we are the broadcast hub, don't bother sending to ourselves. - * (Metricom radios choke if they try to send a packet to their own address.) - */ - if (!memcmp - (haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) - return (NULL); - } - - *ptr++ = 0x0D; - *ptr++ = '*'; - *ptr++ = hextable[haddr.c[2] >> 4]; - *ptr++ = hextable[haddr.c[2] & 0xF]; - *ptr++ = hextable[haddr.c[3] >> 4]; - *ptr++ = hextable[haddr.c[3] & 0xF]; - *ptr++ = '-'; - *ptr++ = hextable[haddr.c[4] >> 4]; - *ptr++ = hextable[haddr.c[4] & 0xF]; - *ptr++ = hextable[haddr.c[5] >> 4]; - *ptr++ = hextable[haddr.c[5] & 0xF]; - *ptr++ = '*'; - *ptr++ = key.c[0]; - *ptr++ = key.c[1]; - *ptr++ = key.c[2]; - *ptr++ = key.c[3]; - - ptr = - StuffData(skb->data + sizeof(STRIP_Header), len, ptr, - &stuffstate); - - if (strip_info->firmware_level >= ChecksummedMessages) - ptr = add_checksum(buffer + 1, ptr); - - *ptr++ = 0x0D; - return (ptr); -} - -static void strip_send(struct strip *strip_info, struct sk_buff *skb) -{ - MetricomAddress haddr; - unsigned char *ptr = strip_info->tx_buff; - int doreset = (long) jiffies - strip_info->watchdog_doreset >= 0; - int doprobe = (long) jiffies - strip_info->watchdog_doprobe >= 0 - && !doreset; - __be32 addr, brd; - - /* - * 1. If we have a packet, encapsulate it and put it in the buffer - */ - if (skb) { - char *newptr = strip_make_packet(ptr, strip_info, skb); - strip_info->tx_pps_count++; - if (!newptr) - strip_info->tx_dropped++; - else { - ptr = newptr; - strip_info->sx_pps_count++; - strip_info->tx_packets++; /* Count another successful packet */ -#ifdef EXT_COUNTERS - strip_info->tx_bytes += skb->len; - strip_info->tx_rbytes += ptr - strip_info->tx_buff; -#endif - /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr); */ - /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr); */ - } - } - - /* - * 2. If it is time for another tickle, tack it on, after the packet - */ - if (doprobe) { - StringDescriptor ts = CommandString[strip_info->next_command]; -#if TICKLE_TIMERS - { - struct timeval tv; - do_gettimeofday(&tv); - printk(KERN_INFO "**** Sending tickle string %d at %02d.%06d\n", - strip_info->next_command, tv.tv_sec % 100, - tv.tv_usec); - } -#endif - if (ptr == strip_info->tx_buff) - *ptr++ = 0x0D; - - *ptr++ = '*'; /* First send "**" to provoke an error message */ - *ptr++ = '*'; - - /* Then add the command */ - memcpy(ptr, ts.string, ts.length); - - /* Add a checksum ? */ - if (strip_info->firmware_level < ChecksummedMessages) - ptr += ts.length; - else - ptr = add_checksum(ptr, ptr + ts.length); - - *ptr++ = 0x0D; /* Terminate the command with a */ - - /* Cycle to next periodic command? */ - if (strip_info->firmware_level >= StructuredMessages) - if (++strip_info->next_command >= - ARRAY_SIZE(CommandString)) - strip_info->next_command = 0; -#ifdef EXT_COUNTERS - strip_info->tx_ebytes += ts.length; -#endif - strip_info->watchdog_doprobe = jiffies + 10 * HZ; - strip_info->watchdog_doreset = jiffies + 1 * HZ; - /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev->name); */ - } - - /* - * 3. Set up the strip_info ready to send the data (if any). - */ - strip_info->tx_head = strip_info->tx_buff; - strip_info->tx_left = ptr - strip_info->tx_buff; - set_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags); - /* - * 4. Debugging check to make sure we're not overflowing the buffer. - */ - if (strip_info->tx_size - strip_info->tx_left < 20) - printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", - strip_info->dev->name, strip_info->tx_left, - strip_info->tx_size - strip_info->tx_left); - - /* - * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in - * the buffer, strip_write_some_more will send it after the reset has finished - */ - if (doreset) { - ResetRadio(strip_info); - return; - } - - if (1) { - struct in_device *in_dev; - - brd = addr = 0; - rcu_read_lock(); - in_dev = __in_dev_get_rcu(strip_info->dev); - if (in_dev) { - if (in_dev->ifa_list) { - brd = in_dev->ifa_list->ifa_broadcast; - addr = in_dev->ifa_list->ifa_local; - } - } - rcu_read_unlock(); - } - - - /* - * 6. If it is time for a periodic ARP, queue one up to be sent. - * We only do this if: - * 1. The radio is working - * 2. It's time to send another periodic ARP - * 3. We really know what our address is (and it is not manually set to zero) - * 4. We have a designated broadcast address configured - * If we queue up an ARP packet when we don't have a designated broadcast - * address configured, then the packet will just have to be discarded in - * strip_make_packet. This is not fatal, but it causes misleading information - * to be displayed in tcpdump. tcpdump will report that periodic APRs are - * being sent, when in fact they are not, because they are all being dropped - * in the strip_make_packet routine. - */ - if (strip_info->working - && (long) jiffies - strip_info->gratuitous_arp >= 0 - && memcmp(strip_info->dev->dev_addr, zero_address.c, - sizeof(zero_address)) - && arp_query(haddr.c, brd, strip_info->dev)) { - /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", - strip_info->dev->name, strip_info->arp_interval / HZ); */ - strip_info->gratuitous_arp = - jiffies + strip_info->arp_interval; - strip_info->arp_interval *= 2; - if (strip_info->arp_interval > MaxARPInterval) - strip_info->arp_interval = MaxARPInterval; - if (addr) - arp_send(ARPOP_REPLY, ETH_P_ARP, addr, /* Target address of ARP packet is our address */ - strip_info->dev, /* Device to send packet on */ - addr, /* Source IP address this ARP packet comes from */ - NULL, /* Destination HW address is NULL (broadcast it) */ - strip_info->dev->dev_addr, /* Source HW address is our HW address */ - strip_info->dev->dev_addr); /* Target HW address is our HW address (redundant) */ - } - - /* - * 7. All ready. Start the transmission - */ - strip_write_some_more(strip_info->tty); -} - -/* Encapsulate a datagram and kick it into a TTY queue. */ -static netdev_tx_t strip_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct strip *strip_info = netdev_priv(dev); - - if (!netif_running(dev)) { - printk(KERN_ERR "%s: xmit call when iface is down\n", - dev->name); - return NETDEV_TX_BUSY; - } - - netif_stop_queue(dev); - - del_timer(&strip_info->idle_timer); - - - if (time_after(jiffies, strip_info->pps_timer + HZ)) { - unsigned long t = jiffies - strip_info->pps_timer; - unsigned long rx_pps_count = - DIV_ROUND_CLOSEST(strip_info->rx_pps_count*HZ*8, t); - unsigned long tx_pps_count = - DIV_ROUND_CLOSEST(strip_info->tx_pps_count*HZ*8, t); - unsigned long sx_pps_count = - DIV_ROUND_CLOSEST(strip_info->sx_pps_count*HZ*8, t); - - strip_info->pps_timer = jiffies; - strip_info->rx_pps_count = 0; - strip_info->tx_pps_count = 0; - strip_info->sx_pps_count = 0; - - strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2; - strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2; - strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2; - - if (rx_pps_count / 8 >= 10) - printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n", - strip_info->dev->name, rx_pps_count / 8); - if (tx_pps_count / 8 >= 10) - printk(KERN_INFO "%s: WARNING: Tx %ld packets per second.\n", - strip_info->dev->name, tx_pps_count / 8); - if (sx_pps_count / 8 >= 10) - printk(KERN_INFO "%s: WARNING: Sending %ld packets per second.\n", - strip_info->dev->name, sx_pps_count / 8); - } - - spin_lock_bh(&strip_lock); - - strip_send(strip_info, skb); - - spin_unlock_bh(&strip_lock); - - if (skb) - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* - * IdleTask periodically calls strip_xmit, so even when we have no IP packets - * to send for an extended period of time, the watchdog processing still gets - * done to ensure that the radio stays in Starmode - */ - -static void strip_IdleTask(unsigned long parameter) -{ - strip_xmit(NULL, (struct net_device *) parameter); -} - -/* - * Create the MAC header for an arbitrary protocol layer - * - * saddr!=NULL means use this specific address (n/a for Metricom) - * saddr==NULL means use default device source address - * daddr!=NULL means use this destination address - * daddr==NULL means leave destination address alone - * (e.g. unresolved arp -- kernel will call - * rebuild_header later to fill in the address) - */ - -static int strip_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len) -{ - struct strip *strip_info = netdev_priv(dev); - STRIP_Header *header = (STRIP_Header *) skb_push(skb, sizeof(STRIP_Header)); - - /*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type, - type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : ""); */ - - header->src_addr = strip_info->true_dev_addr; - header->protocol = htons(type); - - /*HexDump("strip_header", netdev_priv(dev), skb->data, skb->data + skb->len); */ - - if (!daddr) - return (-dev->hard_header_len); - - header->dst_addr = *(MetricomAddress *) daddr; - return (dev->hard_header_len); -} - -/* - * Rebuild the MAC header. This is called after an ARP - * (or in future other address resolution) has completed on this - * sk_buff. We now let ARP fill in the other fields. - * I think this should return zero if packet is ready to send, - * or non-zero if it needs more time to do an address lookup - */ - -static int strip_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - STRIP_Header *header = (STRIP_Header *) skb->data; - - /* Arp find returns zero if if knows the address, */ - /* or if it doesn't know the address it sends an ARP packet and returns non-zero */ - return arp_find(header->dst_addr.c, skb) ? 1 : 0; -#else - return 0; -#endif -} - - -/************************************************************************/ -/* Receiving routines */ - -/* - * This function parses the response to the ATS300? command, - * extracting the radio version and serial number. - */ -static void get_radio_version(struct strip *strip_info, __u8 * ptr, __u8 * end) -{ - __u8 *p, *value_begin, *value_end; - int len; - - /* Determine the beginning of the second line of the payload */ - p = ptr; - while (p < end && *p != 10) - p++; - if (p >= end) - return; - p++; - value_begin = p; - - /* Determine the end of line */ - while (p < end && *p != 10) - p++; - if (p >= end) - return; - value_end = p; - p++; - - len = value_end - value_begin; - len = min_t(int, len, sizeof(FirmwareVersion) - 1); - if (strip_info->firmware_version.c[0] == 0) - printk(KERN_INFO "%s: Radio Firmware: %.*s\n", - strip_info->dev->name, len, value_begin); - sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); - - /* Look for the first colon */ - while (p < end && *p != ':') - p++; - if (p >= end) - return; - /* Skip over the space */ - p += 2; - len = sizeof(SerialNumber) - 1; - if (p + len <= end) { - sprintf(strip_info->serial_number.c, "%.*s", len, p); - } else { - printk(KERN_DEBUG - "STRIP: radio serial number shorter (%zd) than expected (%d)\n", - end - p, len); - } -} - -/* - * This function parses the response to the ATS325? command, - * extracting the radio battery voltage. - */ -static void get_radio_voltage(struct strip *strip_info, __u8 * ptr, __u8 * end) -{ - int len; - - len = sizeof(BatteryVoltage) - 1; - if (ptr + len <= end) { - sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); - } else { - printk(KERN_DEBUG - "STRIP: radio voltage string shorter (%zd) than expected (%d)\n", - end - ptr, len); - } -} - -/* - * This function parses the responses to the AT~LA and ATS311 commands, - * which list the radio's neighbours. - */ -static void get_radio_neighbours(MetricomNodeTable * table, __u8 * ptr, __u8 * end) -{ - table->num_nodes = 0; - while (ptr < end && table->num_nodes < NODE_TABLE_SIZE) { - MetricomNode *node = &table->node[table->num_nodes++]; - char *dst = node->c, *limit = dst + sizeof(*node) - 1; - while (ptr < end && *ptr <= 32) - ptr++; - while (ptr < end && dst < limit && *ptr != 10) - *dst++ = *ptr++; - *dst++ = 0; - while (ptr < end && ptr[-1] != 10) - ptr++; - } - do_gettimeofday(&table->timestamp); -} - -static int get_radio_address(struct strip *strip_info, __u8 * p) -{ - MetricomAddress addr; - - if (string_to_radio_address(&addr, p)) - return (1); - - /* See if our radio address has changed */ - if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr))) { - MetricomAddressString addr_string; - radio_address_to_string(&addr, &addr_string); - printk(KERN_INFO "%s: Radio address = %s\n", - strip_info->dev->name, addr_string.c); - strip_info->true_dev_addr = addr; - if (!strip_info->manual_dev_addr) - *(MetricomAddress *) strip_info->dev->dev_addr = - addr; - /* Give the radio a few seconds to get its head straight, then send an arp */ - strip_info->gratuitous_arp = jiffies + 15 * HZ; - strip_info->arp_interval = 1 * HZ; - } - return (0); -} - -static int verify_checksum(struct strip *strip_info) -{ - __u8 *p = strip_info->sx_buff; - __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4; - u_short sum = - (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) | - (READHEX16(end[2]) << 4) | (READHEX16(end[3])); - while (p < end) - sum -= *p++; - if (sum == 0 && strip_info->firmware_level == StructuredMessages) { - strip_info->firmware_level = ChecksummedMessages; - printk(KERN_INFO "%s: Radio provides message checksums\n", - strip_info->dev->name); - } - return (sum == 0); -} - -static void RecvErr(char *msg, struct strip *strip_info) -{ - __u8 *ptr = strip_info->sx_buff; - __u8 *end = strip_info->sx_buff + strip_info->sx_count; - DumpData(msg, strip_info, ptr, end); - strip_info->rx_errors++; -} - -static void RecvErr_Message(struct strip *strip_info, __u8 * sendername, - const __u8 * msg, u_long len) -{ - if (has_prefix(msg, len, "001")) { /* Not in StarMode! */ - RecvErr("Error Msg:", strip_info); - printk(KERN_INFO "%s: Radio %s is not in StarMode\n", - strip_info->dev->name, sendername); - } - - else if (has_prefix(msg, len, "002")) { /* Remap handle */ - /* We ignore "Remap handle" messages for now */ - } - - else if (has_prefix(msg, len, "003")) { /* Can't resolve name */ - RecvErr("Error Msg:", strip_info); - printk(KERN_INFO "%s: Destination radio name is unknown\n", - strip_info->dev->name); - } - - else if (has_prefix(msg, len, "004")) { /* Name too small or missing */ - strip_info->watchdog_doreset = jiffies + LongTime; -#if TICKLE_TIMERS - { - struct timeval tv; - do_gettimeofday(&tv); - printk(KERN_INFO - "**** Got ERR_004 response at %02d.%06d\n", - tv.tv_sec % 100, tv.tv_usec); - } -#endif - if (!strip_info->working) { - strip_info->working = TRUE; - printk(KERN_INFO "%s: Radio now in starmode\n", - strip_info->dev->name); - /* - * If the radio has just entered a working state, we should do our first - * probe ASAP, so that we find out our radio address etc. without delay. - */ - strip_info->watchdog_doprobe = jiffies; - } - if (strip_info->firmware_level == NoStructure && sendername) { - strip_info->firmware_level = StructuredMessages; - strip_info->next_command = 0; /* Try to enable checksums ASAP */ - printk(KERN_INFO - "%s: Radio provides structured messages\n", - strip_info->dev->name); - } - if (strip_info->firmware_level >= StructuredMessages) { - /* - * If this message has a valid checksum on the end, then the call to verify_checksum - * will elevate the firmware_level to ChecksummedMessages for us. (The actual return - * code from verify_checksum is ignored here.) - */ - verify_checksum(strip_info); - /* - * If the radio has structured messages but we don't yet have all our information about it, - * we should do probes without delay, until we have gathered all the information - */ - if (!GOT_ALL_RADIO_INFO(strip_info)) - strip_info->watchdog_doprobe = jiffies; - } - } - - else if (has_prefix(msg, len, "005")) /* Bad count specification */ - RecvErr("Error Msg:", strip_info); - - else if (has_prefix(msg, len, "006")) /* Header too big */ - RecvErr("Error Msg:", strip_info); - - else if (has_prefix(msg, len, "007")) { /* Body too big */ - RecvErr("Error Msg:", strip_info); - printk(KERN_ERR - "%s: Error! Packet size too big for radio.\n", - strip_info->dev->name); - } - - else if (has_prefix(msg, len, "008")) { /* Bad character in name */ - RecvErr("Error Msg:", strip_info); - printk(KERN_ERR - "%s: Radio name contains illegal character\n", - strip_info->dev->name); - } - - else if (has_prefix(msg, len, "009")) /* No count or line terminator */ - RecvErr("Error Msg:", strip_info); - - else if (has_prefix(msg, len, "010")) /* Invalid checksum */ - RecvErr("Error Msg:", strip_info); - - else if (has_prefix(msg, len, "011")) /* Checksum didn't match */ - RecvErr("Error Msg:", strip_info); - - else if (has_prefix(msg, len, "012")) /* Failed to transmit packet */ - RecvErr("Error Msg:", strip_info); - - else - RecvErr("Error Msg:", strip_info); -} - -static void process_AT_response(struct strip *strip_info, __u8 * ptr, - __u8 * end) -{ - u_long len; - __u8 *p = ptr; - while (p < end && p[-1] != 10) - p++; /* Skip past first newline character */ - /* Now ptr points to the AT command, and p points to the text of the response. */ - len = p - ptr; - -#if TICKLE_TIMERS - { - struct timeval tv; - do_gettimeofday(&tv); - printk(KERN_INFO "**** Got AT response %.7s at %02d.%06d\n", - ptr, tv.tv_sec % 100, tv.tv_usec); - } -#endif - - if (has_prefix(ptr, len, "ATS300?")) - get_radio_version(strip_info, p, end); - else if (has_prefix(ptr, len, "ATS305?")) - get_radio_address(strip_info, p); - else if (has_prefix(ptr, len, "ATS311?")) - get_radio_neighbours(&strip_info->poletops, p, end); - else if (has_prefix(ptr, len, "ATS319=7")) - verify_checksum(strip_info); - else if (has_prefix(ptr, len, "ATS325?")) - get_radio_voltage(strip_info, p, end); - else if (has_prefix(ptr, len, "AT~LA")) - get_radio_neighbours(&strip_info->portables, p, end); - else - RecvErr("Unknown AT Response:", strip_info); -} - -static void process_ACK(struct strip *strip_info, __u8 * ptr, __u8 * end) -{ - /* Currently we don't do anything with ACKs from the radio */ -} - -static void process_Info(struct strip *strip_info, __u8 * ptr, __u8 * end) -{ - if (ptr + 16 > end) - RecvErr("Bad Info Msg:", strip_info); -} - -static struct net_device *get_strip_dev(struct strip *strip_info) -{ - /* If our hardware address is *manually set* to zero, and we know our */ - /* real radio hardware address, try to find another strip device that has been */ - /* manually set to that address that we can 'transfer ownership' of this packet to */ - if (strip_info->manual_dev_addr && - !memcmp(strip_info->dev->dev_addr, zero_address.c, - sizeof(zero_address)) - && memcmp(&strip_info->true_dev_addr, zero_address.c, - sizeof(zero_address))) { - struct net_device *dev; - read_lock_bh(&dev_base_lock); - for_each_netdev(&init_net, dev) { - if (dev->type == strip_info->dev->type && - !memcmp(dev->dev_addr, - &strip_info->true_dev_addr, - sizeof(MetricomAddress))) { - printk(KERN_INFO - "%s: Transferred packet ownership to %s.\n", - strip_info->dev->name, dev->name); - read_unlock_bh(&dev_base_lock); - return (dev); - } - } - read_unlock_bh(&dev_base_lock); - } - return (strip_info->dev); -} - -/* - * Send one completely decapsulated datagram to the next layer. - */ - -static void deliver_packet(struct strip *strip_info, STRIP_Header * header, - __u16 packetlen) -{ - struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen); - if (!skb) { - printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", - strip_info->dev->name); - strip_info->rx_dropped++; - } else { - memcpy(skb_put(skb, sizeof(STRIP_Header)), header, - sizeof(STRIP_Header)); - memcpy(skb_put(skb, packetlen), strip_info->rx_buff, - packetlen); - skb->dev = get_strip_dev(strip_info); - skb->protocol = header->protocol; - skb_reset_mac_header(skb); - - /* Having put a fake header on the front of the sk_buff for the */ - /* benefit of tools like tcpdump, skb_pull now 'consumes' that */ - /* fake header before we hand the packet up to the next layer. */ - skb_pull(skb, sizeof(STRIP_Header)); - - /* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */ - strip_info->rx_packets++; - strip_info->rx_pps_count++; -#ifdef EXT_COUNTERS - strip_info->rx_bytes += packetlen; -#endif - netif_rx(skb); - } -} - -static void process_IP_packet(struct strip *strip_info, - STRIP_Header * header, __u8 * ptr, - __u8 * end) -{ - __u16 packetlen; - - /* Decode start of the IP packet header */ - ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4); - if (!ptr) { - RecvErr("IP Packet too short", strip_info); - return; - } - - packetlen = ((__u16) strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3]; - - if (packetlen > MAX_RECV_MTU) { - printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n", - strip_info->dev->name, packetlen); - strip_info->rx_dropped++; - return; - } - - /*printk(KERN_INFO "%s: Got %d byte IP packet\n", strip_info->dev->name, packetlen); */ - - /* Decode remainder of the IP packet */ - ptr = - UnStuffData(ptr, end, strip_info->rx_buff + 4, packetlen - 4); - if (!ptr) { - RecvErr("IP Packet too short", strip_info); - return; - } - - if (ptr < end) { - RecvErr("IP Packet too long", strip_info); - return; - } - - header->protocol = htons(ETH_P_IP); - - deliver_packet(strip_info, header, packetlen); -} - -static void process_ARP_packet(struct strip *strip_info, - STRIP_Header * header, __u8 * ptr, - __u8 * end) -{ - __u16 packetlen; - struct arphdr *arphdr = (struct arphdr *) strip_info->rx_buff; - - /* Decode start of the ARP packet */ - ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8); - if (!ptr) { - RecvErr("ARP Packet too short", strip_info); - return; - } - - packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2; - - if (packetlen > MAX_RECV_MTU) { - printk(KERN_INFO - "%s: Dropping oversized received ARP packet: %d bytes\n", - strip_info->dev->name, packetlen); - strip_info->rx_dropped++; - return; - } - - /*printk(KERN_INFO "%s: Got %d byte ARP %s\n", - strip_info->dev->name, packetlen, - ntohs(arphdr->ar_op) == ARPOP_REQUEST ? "request" : "reply"); */ - - /* Decode remainder of the ARP packet */ - ptr = - UnStuffData(ptr, end, strip_info->rx_buff + 8, packetlen - 8); - if (!ptr) { - RecvErr("ARP Packet too short", strip_info); - return; - } - - if (ptr < end) { - RecvErr("ARP Packet too long", strip_info); - return; - } - - header->protocol = htons(ETH_P_ARP); - - deliver_packet(strip_info, header, packetlen); -} - -/* - * process_text_message processes a -terminated block of data received - * from the radio that doesn't begin with a '*' character. All normal - * Starmode communication messages with the radio begin with a '*', - * so any text that does not indicates a serial port error, a radio that - * is in Hayes command mode instead of Starmode, or a radio with really - * old firmware that doesn't frame its Starmode responses properly. - */ -static void process_text_message(struct strip *strip_info) -{ - __u8 *msg = strip_info->sx_buff; - int len = strip_info->sx_count; - - /* Check for anything that looks like it might be our radio name */ - /* (This is here for backwards compatibility with old firmware) */ - if (len == 9 && get_radio_address(strip_info, msg) == 0) - return; - - if (text_equal(msg, len, "OK")) - return; /* Ignore 'OK' responses from prior commands */ - if (text_equal(msg, len, "ERROR")) - return; /* Ignore 'ERROR' messages */ - if (has_prefix(msg, len, "ate0q1")) - return; /* Ignore character echo back from the radio */ - - /* Catch other error messages */ - /* (This is here for backwards compatibility with old firmware) */ - if (has_prefix(msg, len, "ERR_")) { - RecvErr_Message(strip_info, NULL, &msg[4], len - 4); - return; - } - - RecvErr("No initial *", strip_info); -} - -/* - * process_message processes a -terminated block of data received - * from the radio. If the radio is not in Starmode or has old firmware, - * it may be a line of text in response to an AT command. Ideally, with - * a current radio that's properly in Starmode, all data received should - * be properly framed and checksummed radio message blocks, containing - * either a starmode packet, or a other communication from the radio - * firmware, like "INF_" Info messages and &COMMAND responses. - */ -static void process_message(struct strip *strip_info) -{ - STRIP_Header header = { zero_address, zero_address, 0 }; - __u8 *ptr = strip_info->sx_buff; - __u8 *end = strip_info->sx_buff + strip_info->sx_count; - __u8 sendername[32], *sptr = sendername; - MetricomKey key; - - /*HexDump("Receiving", strip_info, ptr, end); */ - - /* Check for start of address marker, and then skip over it */ - if (*ptr == '*') - ptr++; - else { - process_text_message(strip_info); - return; - } - - /* Copy out the return address */ - while (ptr < end && *ptr != '*' - && sptr < ARRAY_END(sendername) - 1) - *sptr++ = *ptr++; - *sptr = 0; /* Null terminate the sender name */ - - /* Check for end of address marker, and skip over it */ - if (ptr >= end || *ptr != '*') { - RecvErr("No second *", strip_info); - return; - } - ptr++; /* Skip the second '*' */ - - /* If the sender name is "&COMMAND", ignore this 'packet' */ - /* (This is here for backwards compatibility with old firmware) */ - if (!strcmp(sendername, "&COMMAND")) { - strip_info->firmware_level = NoStructure; - strip_info->next_command = CompatibilityCommand; - return; - } - - if (ptr + 4 > end) { - RecvErr("No proto key", strip_info); - return; - } - - /* Get the protocol key out of the buffer */ - key.c[0] = *ptr++; - key.c[1] = *ptr++; - key.c[2] = *ptr++; - key.c[3] = *ptr++; - - /* If we're using checksums, verify the checksum at the end of the packet */ - if (strip_info->firmware_level >= ChecksummedMessages) { - end -= 4; /* Chop the last four bytes off the packet (they're the checksum) */ - if (ptr > end) { - RecvErr("Missing Checksum", strip_info); - return; - } - if (!verify_checksum(strip_info)) { - RecvErr("Bad Checksum", strip_info); - return; - } - } - - /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev->name, sendername); */ - - /* - * Fill in (pseudo) source and destination addresses in the packet. - * We assume that the destination address was our address (the radio does not - * tell us this). If the radio supplies a source address, then we use it. - */ - header.dst_addr = strip_info->true_dev_addr; - string_to_radio_address(&header.src_addr, sendername); - -#ifdef EXT_COUNTERS - if (key.l == SIP0Key.l) { - strip_info->rx_rbytes += (end - ptr); - process_IP_packet(strip_info, &header, ptr, end); - } else if (key.l == ARP0Key.l) { - strip_info->rx_rbytes += (end - ptr); - process_ARP_packet(strip_info, &header, ptr, end); - } else if (key.l == ATR_Key.l) { - strip_info->rx_ebytes += (end - ptr); - process_AT_response(strip_info, ptr, end); - } else if (key.l == ACK_Key.l) { - strip_info->rx_ebytes += (end - ptr); - process_ACK(strip_info, ptr, end); - } else if (key.l == INF_Key.l) { - strip_info->rx_ebytes += (end - ptr); - process_Info(strip_info, ptr, end); - } else if (key.l == ERR_Key.l) { - strip_info->rx_ebytes += (end - ptr); - RecvErr_Message(strip_info, sendername, ptr, end - ptr); - } else - RecvErr("Unrecognized protocol key", strip_info); -#else - if (key.l == SIP0Key.l) - process_IP_packet(strip_info, &header, ptr, end); - else if (key.l == ARP0Key.l) - process_ARP_packet(strip_info, &header, ptr, end); - else if (key.l == ATR_Key.l) - process_AT_response(strip_info, ptr, end); - else if (key.l == ACK_Key.l) - process_ACK(strip_info, ptr, end); - else if (key.l == INF_Key.l) - process_Info(strip_info, ptr, end); - else if (key.l == ERR_Key.l) - RecvErr_Message(strip_info, sendername, ptr, end - ptr); - else - RecvErr("Unrecognized protocol key", strip_info); -#endif -} - -#define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \ - (X) == TTY_FRAME ? "Framing Error" : \ - (X) == TTY_PARITY ? "Parity Error" : \ - (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error") - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of STRIP data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. - */ - -static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct strip *strip_info = tty->disc_data; - const unsigned char *end = cp + count; - - if (!strip_info || strip_info->magic != STRIP_MAGIC - || !netif_running(strip_info->dev)) - return; - - spin_lock_bh(&strip_lock); -#if 0 - { - struct timeval tv; - do_gettimeofday(&tv); - printk(KERN_INFO - "**** strip_receive_buf: %3d bytes at %02d.%06d\n", - count, tv.tv_sec % 100, tv.tv_usec); - } -#endif - -#ifdef EXT_COUNTERS - strip_info->rx_sbytes += count; -#endif - - /* Read the characters out of the buffer */ - while (cp < end) { - if (fp && *fp) - printk(KERN_INFO "%s: %s on serial port\n", - strip_info->dev->name, TTYERROR(*fp)); - if (fp && *fp++ && !strip_info->discard) { /* If there's a serial error, record it */ - /* If we have some characters in the buffer, discard them */ - strip_info->discard = strip_info->sx_count; - strip_info->rx_errors++; - } - - /* Leading control characters (CR, NL, Tab, etc.) are ignored */ - if (strip_info->sx_count > 0 || *cp >= ' ') { - if (*cp == 0x0D) { /* If end of packet, decide what to do with it */ - if (strip_info->sx_count > 3000) - printk(KERN_INFO - "%s: Cut a %d byte packet (%zd bytes remaining)%s\n", - strip_info->dev->name, - strip_info->sx_count, - end - cp - 1, - strip_info-> - discard ? " (discarded)" : - ""); - if (strip_info->sx_count > - strip_info->sx_size) { - strip_info->rx_over_errors++; - printk(KERN_INFO - "%s: sx_buff overflow (%d bytes total)\n", - strip_info->dev->name, - strip_info->sx_count); - } else if (strip_info->discard) - printk(KERN_INFO - "%s: Discarding bad packet (%d/%d)\n", - strip_info->dev->name, - strip_info->discard, - strip_info->sx_count); - else - process_message(strip_info); - strip_info->discard = 0; - strip_info->sx_count = 0; - } else { - /* Make sure we have space in the buffer */ - if (strip_info->sx_count < - strip_info->sx_size) - strip_info->sx_buff[strip_info-> - sx_count] = - *cp; - strip_info->sx_count++; - } - } - cp++; - } - spin_unlock_bh(&strip_lock); -} - - -/************************************************************************/ -/* General control routines */ - -static int set_mac_address(struct strip *strip_info, - MetricomAddress * addr) -{ - /* - * We're using a manually specified address if the address is set - * to anything other than all ones. Setting the address to all ones - * disables manual mode and goes back to automatic address determination - * (tracking the true address that the radio has). - */ - strip_info->manual_dev_addr = - memcmp(addr->c, broadcast_address.c, - sizeof(broadcast_address)); - if (strip_info->manual_dev_addr) - *(MetricomAddress *) strip_info->dev->dev_addr = *addr; - else - *(MetricomAddress *) strip_info->dev->dev_addr = - strip_info->true_dev_addr; - return 0; -} - -static int strip_set_mac_address(struct net_device *dev, void *addr) -{ - struct strip *strip_info = netdev_priv(dev); - struct sockaddr *sa = addr; - printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name); - set_mac_address(strip_info, (MetricomAddress *) sa->sa_data); - return 0; -} - -static struct net_device_stats *strip_get_stats(struct net_device *dev) -{ - struct strip *strip_info = netdev_priv(dev); - static struct net_device_stats stats; - - memset(&stats, 0, sizeof(struct net_device_stats)); - - stats.rx_packets = strip_info->rx_packets; - stats.tx_packets = strip_info->tx_packets; - stats.rx_dropped = strip_info->rx_dropped; - stats.tx_dropped = strip_info->tx_dropped; - stats.tx_errors = strip_info->tx_errors; - stats.rx_errors = strip_info->rx_errors; - stats.rx_over_errors = strip_info->rx_over_errors; - return (&stats); -} - - -/************************************************************************/ -/* Opening and closing */ - -/* - * Here's the order things happen: - * When the user runs "slattach -p strip ..." - * 1. The TTY module calls strip_open;; - * 2. strip_open calls strip_alloc - * 3. strip_alloc calls register_netdev - * 4. register_netdev calls strip_dev_init - * 5. then strip_open finishes setting up the strip_info - * - * When the user runs "ifconfig st up address netmask ..." - * 6. strip_open_low gets called - * - * When the user runs "ifconfig st down" - * 7. strip_close_low gets called - * - * When the user kills the slattach process - * 8. strip_close gets called - * 9. strip_close calls dev_close - * 10. if the device is still up, then dev_close calls strip_close_low - * 11. strip_close calls strip_free - */ - -/* Open the low-level part of the STRIP channel. Easy! */ - -static int strip_open_low(struct net_device *dev) -{ - struct strip *strip_info = netdev_priv(dev); - - if (strip_info->tty == NULL) - return (-ENODEV); - - if (!allocate_buffers(strip_info, dev->mtu)) - return (-ENOMEM); - - strip_info->sx_count = 0; - strip_info->tx_left = 0; - - strip_info->discard = 0; - strip_info->working = FALSE; - strip_info->firmware_level = NoStructure; - strip_info->next_command = CompatibilityCommand; - strip_info->user_baud = tty_get_baud_rate(strip_info->tty); - - printk(KERN_INFO "%s: Initializing Radio.\n", - strip_info->dev->name); - ResetRadio(strip_info); - strip_info->idle_timer.expires = jiffies + 1 * HZ; - add_timer(&strip_info->idle_timer); - netif_wake_queue(dev); - return (0); -} - - -/* - * Close the low-level part of the STRIP channel. Easy! - */ - -static int strip_close_low(struct net_device *dev) -{ - struct strip *strip_info = netdev_priv(dev); - - if (strip_info->tty == NULL) - return -EBUSY; - clear_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags); - netif_stop_queue(dev); - - /* - * Free all STRIP frame buffers. - */ - kfree(strip_info->rx_buff); - strip_info->rx_buff = NULL; - kfree(strip_info->sx_buff); - strip_info->sx_buff = NULL; - kfree(strip_info->tx_buff); - strip_info->tx_buff = NULL; - - del_timer(&strip_info->idle_timer); - return 0; -} - -static const struct header_ops strip_header_ops = { - .create = strip_header, - .rebuild = strip_rebuild_header, -}; - - -static const struct net_device_ops strip_netdev_ops = { - .ndo_open = strip_open_low, - .ndo_stop = strip_close_low, - .ndo_start_xmit = strip_xmit, - .ndo_set_mac_address = strip_set_mac_address, - .ndo_get_stats = strip_get_stats, - .ndo_change_mtu = strip_change_mtu, -}; - -/* - * This routine is called by DDI when the - * (dynamically assigned) device is registered - */ - -static void strip_dev_setup(struct net_device *dev) -{ - /* - * Finish setting up the DEVICE info. - */ - - dev->trans_start = 0; - dev->tx_queue_len = 30; /* Drop after 30 frames queued */ - - dev->flags = 0; - dev->mtu = DEFAULT_STRIP_MTU; - dev->type = ARPHRD_METRICOM; /* dtang */ - dev->hard_header_len = sizeof(STRIP_Header); - /* - * netdev_priv(dev) Already holds a pointer to our struct strip - */ - - *(MetricomAddress *)dev->broadcast = broadcast_address; - dev->dev_addr[0] = 0; - dev->addr_len = sizeof(MetricomAddress); - - dev->header_ops = &strip_header_ops, - dev->netdev_ops = &strip_netdev_ops; -} - -/* - * Free a STRIP channel. - */ - -static void strip_free(struct strip *strip_info) -{ - spin_lock_bh(&strip_lock); - list_del_rcu(&strip_info->list); - spin_unlock_bh(&strip_lock); - - strip_info->magic = 0; - - free_netdev(strip_info->dev); -} - - -/* - * Allocate a new free STRIP channel - */ -static struct strip *strip_alloc(void) -{ - struct list_head *n; - struct net_device *dev; - struct strip *strip_info; - - dev = alloc_netdev(sizeof(struct strip), "st%d", - strip_dev_setup); - - if (!dev) - return NULL; /* If no more memory, return */ - - - strip_info = netdev_priv(dev); - strip_info->dev = dev; - - strip_info->magic = STRIP_MAGIC; - strip_info->tty = NULL; - - strip_info->gratuitous_arp = jiffies + LongTime; - strip_info->arp_interval = 0; - init_timer(&strip_info->idle_timer); - strip_info->idle_timer.data = (long) dev; - strip_info->idle_timer.function = strip_IdleTask; - - - spin_lock_bh(&strip_lock); - rescan: - /* - * Search the list to find where to put our new entry - * (and in the process decide what channel number it is - * going to be) - */ - list_for_each(n, &strip_list) { - struct strip *s = hlist_entry(n, struct strip, list); - - if (s->dev->base_addr == dev->base_addr) { - ++dev->base_addr; - goto rescan; - } - } - - sprintf(dev->name, "st%ld", dev->base_addr); - - list_add_tail_rcu(&strip_info->list, &strip_list); - spin_unlock_bh(&strip_lock); - - return strip_info; -} - -/* - * Open the high-level part of the STRIP channel. - * This function is called by the TTY module when the - * STRIP line discipline is called for. Because we are - * sure the tty line exists, we only have to link it to - * a free STRIP channel... - */ - -static int strip_open(struct tty_struct *tty) -{ - struct strip *strip_info = tty->disc_data; - - /* - * First make sure we're not already connected. - */ - - if (strip_info && strip_info->magic == STRIP_MAGIC) - return -EEXIST; - - /* - * We need a write method. - */ - - if (tty->ops->write == NULL || tty->ops->set_termios == NULL) - return -EOPNOTSUPP; - - /* - * OK. Find a free STRIP channel to use. - */ - if ((strip_info = strip_alloc()) == NULL) - return -ENFILE; - - /* - * Register our newly created device so it can be ifconfig'd - * strip_dev_init() will be called as a side-effect - */ - - if (register_netdev(strip_info->dev) != 0) { - printk(KERN_ERR "strip: register_netdev() failed.\n"); - strip_free(strip_info); - return -ENFILE; - } - - strip_info->tty = tty; - tty->disc_data = strip_info; - tty->receive_room = 65536; - - tty_driver_flush_buffer(tty); - - /* - * Restore default settings - */ - - strip_info->dev->type = ARPHRD_METRICOM; /* dtang */ - - /* - * Set tty options - */ - - tty->termios->c_iflag |= IGNBRK | IGNPAR; /* Ignore breaks and parity errors. */ - tty->termios->c_cflag |= CLOCAL; /* Ignore modem control signals. */ - tty->termios->c_cflag &= ~HUPCL; /* Don't close on hup */ - - printk(KERN_INFO "STRIP: device \"%s\" activated\n", - strip_info->dev->name); - - /* - * Done. We have linked the TTY line to a channel. - */ - return (strip_info->dev->base_addr); -} - -/* - * Close down a STRIP channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to STRIP - * (which usually is TTY again). - */ - -static void strip_close(struct tty_struct *tty) -{ - struct strip *strip_info = tty->disc_data; - - /* - * First make sure we're connected. - */ - - if (!strip_info || strip_info->magic != STRIP_MAGIC) - return; - - unregister_netdev(strip_info->dev); - - tty->disc_data = NULL; - strip_info->tty = NULL; - printk(KERN_INFO "STRIP: device \"%s\" closed down\n", - strip_info->dev->name); - strip_free(strip_info); - tty->disc_data = NULL; -} - - -/************************************************************************/ -/* Perform I/O control calls on an active STRIP channel. */ - -static int strip_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct strip *strip_info = tty->disc_data; - - /* - * First make sure we're connected. - */ - - if (!strip_info || strip_info->magic != STRIP_MAGIC) - return -EINVAL; - - switch (cmd) { - case SIOCGIFNAME: - if(copy_to_user((void __user *) arg, strip_info->dev->name, strlen(strip_info->dev->name) + 1)) - return -EFAULT; - break; - case SIOCSIFHWADDR: - { - MetricomAddress addr; - //printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev->name); - if(copy_from_user(&addr, (void __user *) arg, sizeof(MetricomAddress))) - return -EFAULT; - return set_mac_address(strip_info, &addr); - } - default: - return tty_mode_ioctl(tty, file, cmd, arg); - break; - } - return 0; -} - - -/************************************************************************/ -/* Initialization */ - -static struct tty_ldisc_ops strip_ldisc = { - .magic = TTY_LDISC_MAGIC, - .name = "strip", - .owner = THIS_MODULE, - .open = strip_open, - .close = strip_close, - .ioctl = strip_ioctl, - .receive_buf = strip_receive_buf, - .write_wakeup = strip_write_some_more, -}; - -/* - * Initialize the STRIP driver. - * This routine is called at boot time, to bootstrap the multi-channel - * STRIP driver - */ - -static char signon[] __initdata = - KERN_INFO "STRIP: Version %s (unlimited channels)\n"; - -static int __init strip_init_driver(void) -{ - int status; - - printk(signon, StripVersion); - - - /* - * Fill in our line protocol discipline, and register it - */ - if ((status = tty_register_ldisc(N_STRIP, &strip_ldisc))) - printk(KERN_ERR "STRIP: can't register line discipline (err = %d)\n", - status); - - /* - * Register the status file with /proc - */ - proc_net_fops_create(&init_net, "strip", S_IFREG | S_IRUGO, &strip_seq_fops); - - return status; -} - -module_init(strip_init_driver); - -static const char signoff[] __exitdata = - KERN_INFO "STRIP: Module Unloaded\n"; - -static void __exit strip_exit_driver(void) -{ - int i; - struct list_head *p,*n; - - /* module ref count rules assure that all entries are unregistered */ - list_for_each_safe(p, n, &strip_list) { - struct strip *s = list_entry(p, struct strip, list); - strip_free(s); - } - - /* Unregister with the /proc/net file here. */ - proc_net_remove(&init_net, "strip"); - - if ((i = tty_unregister_ldisc(N_STRIP))) - printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i); - - printk(signoff); -} - -module_exit(strip_exit_driver); - -MODULE_AUTHOR("Stuart Cheshire "); -MODULE_DESCRIPTION("Starmode Radio IP (STRIP) Device Driver"); -MODULE_LICENSE("Dual BSD/GPL"); - -MODULE_SUPPORTED_DEVICE("Starmode Radio IP (STRIP) modem"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 9a4dd5992f65..7ddc5c8d8956 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -131,5 +131,7 @@ source "drivers/staging/iio/Kconfig" source "drivers/staging/cowloop/Kconfig" +source "drivers/staging/strip/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 104f2f8897ec..4386a620e9b2 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_RAR_REGISTER) += rar/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_COWLOOP) += cowloop/ +obj-$(CONFIG_STRIP) += strip/ diff --git a/drivers/staging/strip/Kconfig b/drivers/staging/strip/Kconfig new file mode 100644 index 000000000000..36257b5cd6e1 --- /dev/null +++ b/drivers/staging/strip/Kconfig @@ -0,0 +1,22 @@ +config STRIP + tristate "STRIP (Metricom starmode radio IP)" + depends on INET + select WIRELESS_EXT + ---help--- + Say Y if you have a Metricom radio and intend to use Starmode Radio + IP. STRIP is a radio protocol developed for the MosquitoNet project + to send Internet traffic using Metricom radios. Metricom radios are + small, battery powered, 100kbit/sec packet radio transceivers, about + the size and weight of a cellular telephone. (You may also have heard + them called "Metricom modems" but we avoid the term "modem" because + it misleads many people into thinking that you can plug a Metricom + modem into a phone line and use it as a modem.) + + You can use STRIP on any Linux machine with a serial port, although + it is obviously most useful for people with laptop computers. If you + think you might get a Metricom radio in the future, there is no harm + in saying Y to STRIP now, except that it makes the kernel a bit + bigger. + + To compile this as a module, choose M here: the module will be + called strip. diff --git a/drivers/staging/strip/Makefile b/drivers/staging/strip/Makefile new file mode 100644 index 000000000000..6417bdcac2fb --- /dev/null +++ b/drivers/staging/strip/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_STRIP) += strip.o diff --git a/drivers/staging/strip/strip.c b/drivers/staging/strip/strip.c new file mode 100644 index 000000000000..ea6a87c19319 --- /dev/null +++ b/drivers/staging/strip/strip.c @@ -0,0 +1,2805 @@ +/* + * Copyright 1996 The Board of Trustees of The Leland Stanford + * Junior University. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. Stanford University + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * strip.c This module implements Starmode Radio IP (STRIP) + * for kernel-based devices like TTY. It interfaces between a + * raw TTY, and the kernel's INET protocol layers (via DDI). + * + * Version: @(#)strip.c 1.3 July 1997 + * + * Author: Stuart Cheshire + * + * Fixes: v0.9 12th Feb 1996 (SC) + * New byte stuffing (2+6 run-length encoding) + * New watchdog timer task + * New Protocol key (SIP0) + * + * v0.9.1 3rd March 1996 (SC) + * Changed to dynamic device allocation -- no more compile + * time (or boot time) limit on the number of STRIP devices. + * + * v0.9.2 13th March 1996 (SC) + * Uses arp cache lookups (but doesn't send arp packets yet) + * + * v0.9.3 17th April 1996 (SC) + * Fixed bug where STR_ERROR flag was getting set unneccessarily + * (causing otherwise good packets to be unneccessarily dropped) + * + * v0.9.4 27th April 1996 (SC) + * First attempt at using "&COMMAND" Starmode AT commands + * + * v0.9.5 29th May 1996 (SC) + * First attempt at sending (unicast) ARP packets + * + * v0.9.6 5th June 1996 (Elliot) + * Put "message level" tags in every "printk" statement + * + * v0.9.7 13th June 1996 (laik) + * Added support for the /proc fs + * + * v0.9.8 July 1996 (Mema) + * Added packet logging + * + * v1.0 November 1996 (SC) + * Fixed (severe) memory leaks in the /proc fs code + * Fixed race conditions in the logging code + * + * v1.1 January 1997 (SC) + * Deleted packet logging (use tcpdump instead) + * Added support for Metricom Firmware v204 features + * (like message checksums) + * + * v1.2 January 1997 (SC) + * Put portables list back in + * + * v1.3 July 1997 (SC) + * Made STRIP driver set the radio's baud rate automatically. + * It is no longer necessarily to manually set the radio's + * rate permanently to 115200 -- the driver handles setting + * the rate automatically. + */ + +#ifdef MODULE +static const char StripVersion[] = "1.3A-STUART.CHESHIRE-MODULAR"; +#else +static const char StripVersion[] = "1.3A-STUART.CHESHIRE"; +#endif + +#define TICKLE_TIMERS 0 +#define EXT_COUNTERS 1 + + +/************************************************************************/ +/* Header files */ + +#include +#include +#include +#include +#include +#include + +# include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/************************************************************************/ +/* Useful structures and definitions */ + +/* + * A MetricomKey identifies the protocol being carried inside a Metricom + * Starmode packet. + */ + +typedef union { + __u8 c[4]; + __u32 l; +} MetricomKey; + +/* + * An IP address can be viewed as four bytes in memory (which is what it is) or as + * a single 32-bit long (which is convenient for assignment, equality testing etc.) + */ + +typedef union { + __u8 b[4]; + __u32 l; +} IPaddr; + +/* + * A MetricomAddressString is used to hold a printable representation of + * a Metricom address. + */ + +typedef struct { + __u8 c[24]; +} MetricomAddressString; + +/* Encapsulation can expand packet of size x to 65/64x + 1 + * Sent packet looks like "*
*" + * 1 1 1-18 1 4 ? 1 + * eg. *0000-1234*SIP0 + * We allow 31 bytes for the stars, the key, the address and the s + */ +#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) + +/* + * A STRIP_Header is never really sent over the radio, but making a dummy + * header for internal use within the kernel that looks like an Ethernet + * header makes certain other software happier. For example, tcpdump + * already understands Ethernet headers. + */ + +typedef struct { + MetricomAddress dst_addr; /* Destination address, e.g. "0000-1234" */ + MetricomAddress src_addr; /* Source address, e.g. "0000-5678" */ + unsigned short protocol; /* The protocol type, using Ethernet codes */ +} STRIP_Header; + +typedef struct { + char c[60]; +} MetricomNode; + +#define NODE_TABLE_SIZE 32 +typedef struct { + struct timeval timestamp; + int num_nodes; + MetricomNode node[NODE_TABLE_SIZE]; +} MetricomNodeTable; + +enum { FALSE = 0, TRUE = 1 }; + +/* + * Holds the radio's firmware version. + */ +typedef struct { + char c[50]; +} FirmwareVersion; + +/* + * Holds the radio's serial number. + */ +typedef struct { + char c[18]; +} SerialNumber; + +/* + * Holds the radio's battery voltage. + */ +typedef struct { + char c[11]; +} BatteryVoltage; + +typedef struct { + char c[8]; +} char8; + +enum { + NoStructure = 0, /* Really old firmware */ + StructuredMessages = 1, /* Parsable AT response msgs */ + ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */ +}; + +struct strip { + int magic; + /* + * These are pointers to the malloc()ed frame buffers. + */ + + unsigned char *rx_buff; /* buffer for received IP packet */ + unsigned char *sx_buff; /* buffer for received serial data */ + int sx_count; /* received serial data counter */ + int sx_size; /* Serial buffer size */ + unsigned char *tx_buff; /* transmitter buffer */ + unsigned char *tx_head; /* pointer to next byte to XMIT */ + int tx_left; /* bytes left in XMIT queue */ + int tx_size; /* Serial buffer size */ + + /* + * STRIP interface statistics. + */ + + unsigned long rx_packets; /* inbound frames counter */ + unsigned long tx_packets; /* outbound frames counter */ + unsigned long rx_errors; /* Parity, etc. errors */ + unsigned long tx_errors; /* Planned stuff */ + unsigned long rx_dropped; /* No memory for skb */ + unsigned long tx_dropped; /* When MTU change */ + unsigned long rx_over_errors; /* Frame bigger than STRIP buf. */ + + unsigned long pps_timer; /* Timer to determine pps */ + unsigned long rx_pps_count; /* Counter to determine pps */ + unsigned long tx_pps_count; /* Counter to determine pps */ + unsigned long sx_pps_count; /* Counter to determine pps */ + unsigned long rx_average_pps; /* rx packets per second * 8 */ + unsigned long tx_average_pps; /* tx packets per second * 8 */ + unsigned long sx_average_pps; /* sent packets per second * 8 */ + +#ifdef EXT_COUNTERS + unsigned long rx_bytes; /* total received bytes */ + unsigned long tx_bytes; /* total received bytes */ + unsigned long rx_rbytes; /* bytes thru radio i/f */ + unsigned long tx_rbytes; /* bytes thru radio i/f */ + unsigned long rx_sbytes; /* tot bytes thru serial i/f */ + unsigned long tx_sbytes; /* tot bytes thru serial i/f */ + unsigned long rx_ebytes; /* tot stat/err bytes */ + unsigned long tx_ebytes; /* tot stat/err bytes */ +#endif + + /* + * Internal variables. + */ + + struct list_head list; /* Linked list of devices */ + + int discard; /* Set if serial error */ + int working; /* Is radio working correctly? */ + int firmware_level; /* Message structuring level */ + int next_command; /* Next periodic command */ + unsigned int user_baud; /* The user-selected baud rate */ + int mtu; /* Our mtu (to spot changes!) */ + long watchdog_doprobe; /* Next time to test the radio */ + long watchdog_doreset; /* Time to do next reset */ + long gratuitous_arp; /* Time to send next ARP refresh */ + long arp_interval; /* Next ARP interval */ + struct timer_list idle_timer; /* For periodic wakeup calls */ + MetricomAddress true_dev_addr; /* True address of radio */ + int manual_dev_addr; /* Hack: See note below */ + + FirmwareVersion firmware_version; /* The radio's firmware version */ + SerialNumber serial_number; /* The radio's serial number */ + BatteryVoltage battery_voltage; /* The radio's battery voltage */ + + /* + * Other useful structures. + */ + + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* Our device structure */ + + /* + * Neighbour radio records + */ + + MetricomNodeTable portables; + MetricomNodeTable poletops; +}; + +/* + * Note: manual_dev_addr hack + * + * It is not possible to change the hardware address of a Metricom radio, + * or to send packets with a user-specified hardware source address, thus + * trying to manually set a hardware source address is a questionable + * thing to do. However, if the user *does* manually set the hardware + * source address of a STRIP interface, then the kernel will believe it, + * and use it in certain places. For example, the hardware address listed + * by ifconfig will be the manual address, not the true one. + * (Both addresses are listed in /proc/net/strip.) + * Also, ARP packets will be sent out giving the user-specified address as + * the source address, not the real address. This is dangerous, because + * it means you won't receive any replies -- the ARP replies will go to + * the specified address, which will be some other radio. The case where + * this is useful is when that other radio is also connected to the same + * machine. This allows you to connect a pair of radios to one machine, + * and to use one exclusively for inbound traffic, and the other + * exclusively for outbound traffic. Pretty neat, huh? + * + * Here's the full procedure to set this up: + * + * 1. "slattach" two interfaces, e.g. st0 for outgoing packets, + * and st1 for incoming packets + * + * 2. "ifconfig" st0 (outbound radio) to have the hardware address + * which is the real hardware address of st1 (inbound radio). + * Now when it sends out packets, it will masquerade as st1, and + * replies will be sent to that radio, which is exactly what we want. + * + * 3. Set the route table entry ("route add default ..." or + * "route add -net ...", as appropriate) to send packets via the st0 + * interface (outbound radio). Do not add any route which sends packets + * out via the st1 interface -- that radio is for inbound traffic only. + * + * 4. "ifconfig" st1 (inbound radio) to have hardware address zero. + * This tells the STRIP driver to "shut down" that interface and not + * send any packets through it. In particular, it stops sending the + * periodic gratuitous ARP packets that a STRIP interface normally sends. + * Also, when packets arrive on that interface, it will search the + * interface list to see if there is another interface who's manual + * hardware address matches its own real address (i.e. st0 in this + * example) and if so it will transfer ownership of the skbuff to + * that interface, so that it looks to the kernel as if the packet + * arrived on that interface. This is necessary because when the + * kernel sends an ARP packet on st0, it expects to get a reply on + * st0, and if it sees the reply come from st1 then it will ignore + * it (to be accurate, it puts the entry in the ARP table, but + * labelled in such a way that st0 can't use it). + * + * Thanks to Petros Maniatis for coming up with the idea of splitting + * inbound and outbound traffic between two interfaces, which turned + * out to be really easy to implement, even if it is a bit of a hack. + * + * Having set a manual address on an interface, you can restore it + * to automatic operation (where the address is automatically kept + * consistent with the real address of the radio) by setting a manual + * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF" + * This 'turns off' manual override mode for the device address. + * + * Note: The IEEE 802 headers reported in tcpdump will show the *real* + * radio addresses the packets were sent and received from, so that you + * can see what is really going on with packets, and which interfaces + * they are really going through. + */ + + +/************************************************************************/ +/* Constants */ + +/* + * CommandString1 works on all radios + * Other CommandStrings are only used with firmware that provides structured responses. + * + * ats319=1 Enables Info message for node additions and deletions + * ats319=2 Enables Info message for a new best node + * ats319=4 Enables checksums + * ats319=8 Enables ACK messages + */ + +static const int MaxCommandStringLength = 32; +static const int CompatibilityCommand = 1; + +static const char CommandString0[] = "*&COMMAND*ATS319=7"; /* Turn on checksums & info messages */ +static const char CommandString1[] = "*&COMMAND*ATS305?"; /* Query radio name */ +static const char CommandString2[] = "*&COMMAND*ATS325?"; /* Query battery voltage */ +static const char CommandString3[] = "*&COMMAND*ATS300?"; /* Query version information */ +static const char CommandString4[] = "*&COMMAND*ATS311?"; /* Query poletop list */ +static const char CommandString5[] = "*&COMMAND*AT~LA"; /* Query portables list */ +typedef struct { + const char *string; + long length; +} StringDescriptor; + +static const StringDescriptor CommandString[] = { + {CommandString0, sizeof(CommandString0) - 1}, + {CommandString1, sizeof(CommandString1) - 1}, + {CommandString2, sizeof(CommandString2) - 1}, + {CommandString3, sizeof(CommandString3) - 1}, + {CommandString4, sizeof(CommandString4) - 1}, + {CommandString5, sizeof(CommandString5) - 1} +}; + +#define GOT_ALL_RADIO_INFO(S) \ + ((S)->firmware_version.c[0] && \ + (S)->battery_voltage.c[0] && \ + memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address))) + +static const char hextable[16] = "0123456789ABCDEF"; + +static const MetricomAddress zero_address; +static const MetricomAddress broadcast_address = + { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }; + +static const MetricomKey SIP0Key = { "SIP0" }; +static const MetricomKey ARP0Key = { "ARP0" }; +static const MetricomKey ATR_Key = { "ATR " }; +static const MetricomKey ACK_Key = { "ACK_" }; +static const MetricomKey INF_Key = { "INF_" }; +static const MetricomKey ERR_Key = { "ERR_" }; + +static const long MaxARPInterval = 60 * HZ; /* One minute */ + +/* + * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for + * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion + * for STRIP encoding, that translates to a maximum payload MTU of 1155. + * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes + * long, including IP header, UDP header, and NFS header. Setting the STRIP + * MTU to 1152 allows us to send default sized NFS packets without fragmentation. + */ +static const unsigned short MAX_SEND_MTU = 1152; +static const unsigned short MAX_RECV_MTU = 1500; /* Hoping for Ethernet sized packets in the future! */ +static const unsigned short DEFAULT_STRIP_MTU = 1152; +static const int STRIP_MAGIC = 0x5303; +static const long LongTime = 0x7FFFFFFF; + +/************************************************************************/ +/* Global variables */ + +static LIST_HEAD(strip_list); +static DEFINE_SPINLOCK(strip_lock); + +/************************************************************************/ +/* Macros */ + +/* Returns TRUE if text T begins with prefix P */ +#define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1)) + +/* Returns TRUE if text T of length L is equal to string S */ +#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1)) + +#define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \ + (X)>='a' && (X)<='f' ? (X)-'a'+10 : \ + (X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 ) + +#define READHEX16(X) ((__u16)(READHEX(X))) + +#define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0) + +#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) + +#define JIFFIE_TO_SEC(X) ((X) / HZ) + + +/************************************************************************/ +/* Utility routines */ + +static int arp_query(unsigned char *haddr, u32 paddr, + struct net_device *dev) +{ + struct neighbour *neighbor_entry; + int ret = 0; + + neighbor_entry = neigh_lookup(&arp_tbl, &paddr, dev); + + if (neighbor_entry != NULL) { + neighbor_entry->used = jiffies; + if (neighbor_entry->nud_state & NUD_VALID) { + memcpy(haddr, neighbor_entry->ha, dev->addr_len); + ret = 1; + } + neigh_release(neighbor_entry); + } + return ret; +} + +static void DumpData(char *msg, struct strip *strip_info, __u8 * ptr, + __u8 * end) +{ + static const int MAX_DumpData = 80; + __u8 pkt_text[MAX_DumpData], *p = pkt_text; + + *p++ = '\"'; + + while (ptr < end && p < &pkt_text[MAX_DumpData - 4]) { + if (*ptr == '\\') { + *p++ = '\\'; + *p++ = '\\'; + } else { + if (*ptr >= 32 && *ptr <= 126) { + *p++ = *ptr; + } else { + sprintf(p, "\\%02X", *ptr); + p += 3; + } + } + ptr++; + } + + if (ptr == end) + *p++ = '\"'; + *p++ = 0; + + printk(KERN_INFO "%s: %-13s%s\n", strip_info->dev->name, msg, pkt_text); +} + + +/************************************************************************/ +/* Byte stuffing/unstuffing routines */ + +/* Stuffing scheme: + * 00 Unused (reserved character) + * 01-3F Run of 2-64 different characters + * 40-7F Run of 1-64 different characters plus a single zero at the end + * 80-BF Run of 1-64 of the same character + * C0-FF Run of 1-64 zeroes (ASCII 0) + */ + +typedef enum { + Stuff_Diff = 0x00, + Stuff_DiffZero = 0x40, + Stuff_Same = 0x80, + Stuff_Zero = 0xC0, + Stuff_NoCode = 0xFF, /* Special code, meaning no code selected */ + + Stuff_CodeMask = 0xC0, + Stuff_CountMask = 0x3F, + Stuff_MaxCount = 0x3F, + Stuff_Magic = 0x0D /* The value we are eliminating */ +} StuffingCode; + +/* StuffData encodes the data starting at "src" for "length" bytes. + * It writes it to the buffer pointed to by "dst" (which must be at least + * as long as 1 + 65/64 of the input length). The output may be up to 1.6% + * larger than the input for pathological input, but will usually be smaller. + * StuffData returns the new value of the dst pointer as its result. + * "code_ptr_ptr" points to a "__u8 *" which is used to hold encoding state + * between calls, allowing an encoded packet to be incrementally built up + * from small parts. On the first call, the "__u8 *" pointed to should be + * initialized to NULL; between subsequent calls the calling routine should + * leave the value alone and simply pass it back unchanged so that the + * encoder can recover its current state. + */ + +#define StuffData_FinishBlock(X) \ +(*code_ptr = (X) ^ Stuff_Magic, code = Stuff_NoCode) + +static __u8 *StuffData(__u8 * src, __u32 length, __u8 * dst, + __u8 ** code_ptr_ptr) +{ + __u8 *end = src + length; + __u8 *code_ptr = *code_ptr_ptr; + __u8 code = Stuff_NoCode, count = 0; + + if (!length) + return (dst); + + if (code_ptr) { + /* + * Recover state from last call, if applicable + */ + code = (*code_ptr ^ Stuff_Magic) & Stuff_CodeMask; + count = (*code_ptr ^ Stuff_Magic) & Stuff_CountMask; + } + + while (src < end) { + switch (code) { + /* Stuff_NoCode: If no current code, select one */ + case Stuff_NoCode: + /* Record where we're going to put this code */ + code_ptr = dst++; + count = 0; /* Reset the count (zero means one instance) */ + /* Tentatively start a new block */ + if (*src == 0) { + code = Stuff_Zero; + src++; + } else { + code = Stuff_Same; + *dst++ = *src++ ^ Stuff_Magic; + } + /* Note: We optimistically assume run of same -- */ + /* which will be fixed later in Stuff_Same */ + /* if it turns out not to be true. */ + break; + + /* Stuff_Zero: We already have at least one zero encoded */ + case Stuff_Zero: + /* If another zero, count it, else finish this code block */ + if (*src == 0) { + count++; + src++; + } else { + StuffData_FinishBlock(Stuff_Zero + count); + } + break; + + /* Stuff_Same: We already have at least one byte encoded */ + case Stuff_Same: + /* If another one the same, count it */ + if ((*src ^ Stuff_Magic) == code_ptr[1]) { + count++; + src++; + break; + } + /* else, this byte does not match this block. */ + /* If we already have two or more bytes encoded, finish this code block */ + if (count) { + StuffData_FinishBlock(Stuff_Same + count); + break; + } + /* else, we only have one so far, so switch to Stuff_Diff code */ + code = Stuff_Diff; + /* and fall through to Stuff_Diff case below + * Note cunning cleverness here: case Stuff_Diff compares + * the current character with the previous two to see if it + * has a run of three the same. Won't this be an error if + * there aren't two previous characters stored to compare with? + * No. Because we know the current character is *not* the same + * as the previous one, the first test below will necessarily + * fail and the send half of the "if" won't be executed. + */ + + /* Stuff_Diff: We have at least two *different* bytes encoded */ + case Stuff_Diff: + /* If this is a zero, must encode a Stuff_DiffZero, and begin a new block */ + if (*src == 0) { + StuffData_FinishBlock(Stuff_DiffZero + + count); + } + /* else, if we have three in a row, it is worth starting a Stuff_Same block */ + else if ((*src ^ Stuff_Magic) == dst[-1] + && dst[-1] == dst[-2]) { + /* Back off the last two characters we encoded */ + code += count - 2; + /* Note: "Stuff_Diff + 0" is an illegal code */ + if (code == Stuff_Diff + 0) { + code = Stuff_Same + 0; + } + StuffData_FinishBlock(code); + code_ptr = dst - 2; + /* dst[-1] already holds the correct value */ + count = 2; /* 2 means three bytes encoded */ + code = Stuff_Same; + } + /* else, another different byte, so add it to the block */ + else { + *dst++ = *src ^ Stuff_Magic; + count++; + } + src++; /* Consume the byte */ + break; + } + if (count == Stuff_MaxCount) { + StuffData_FinishBlock(code + count); + } + } + if (code == Stuff_NoCode) { + *code_ptr_ptr = NULL; + } else { + *code_ptr_ptr = code_ptr; + StuffData_FinishBlock(code + count); + } + return (dst); +} + +/* + * UnStuffData decodes the data at "src", up to (but not including) "end". + * It writes the decoded data into the buffer pointed to by "dst", up to a + * maximum of "dst_length", and returns the new value of "src" so that a + * follow-on call can read more data, continuing from where the first left off. + * + * There are three types of results: + * 1. The source data runs out before extracting "dst_length" bytes: + * UnStuffData returns NULL to indicate failure. + * 2. The source data produces exactly "dst_length" bytes: + * UnStuffData returns new_src = end to indicate that all bytes were consumed. + * 3. "dst_length" bytes are extracted, with more remaining. + * UnStuffData returns new_src < end to indicate that there are more bytes + * to be read. + * + * Note: The decoding may be destructive, in that it may alter the source + * data in the process of decoding it (this is necessary to allow a follow-on + * call to resume correctly). + */ + +static __u8 *UnStuffData(__u8 * src, __u8 * end, __u8 * dst, + __u32 dst_length) +{ + __u8 *dst_end = dst + dst_length; + /* Sanity check */ + if (!src || !end || !dst || !dst_length) + return (NULL); + while (src < end && dst < dst_end) { + int count = (*src ^ Stuff_Magic) & Stuff_CountMask; + switch ((*src ^ Stuff_Magic) & Stuff_CodeMask) { + case Stuff_Diff: + if (src + 1 + count >= end) + return (NULL); + do { + *dst++ = *++src ^ Stuff_Magic; + } + while (--count >= 0 && dst < dst_end); + if (count < 0) + src += 1; + else { + if (count == 0) + *src = Stuff_Same ^ Stuff_Magic; + else + *src = + (Stuff_Diff + + count) ^ Stuff_Magic; + } + break; + case Stuff_DiffZero: + if (src + 1 + count >= end) + return (NULL); + do { + *dst++ = *++src ^ Stuff_Magic; + } + while (--count >= 0 && dst < dst_end); + if (count < 0) + *src = Stuff_Zero ^ Stuff_Magic; + else + *src = + (Stuff_DiffZero + count) ^ Stuff_Magic; + break; + case Stuff_Same: + if (src + 1 >= end) + return (NULL); + do { + *dst++ = src[1] ^ Stuff_Magic; + } + while (--count >= 0 && dst < dst_end); + if (count < 0) + src += 2; + else + *src = (Stuff_Same + count) ^ Stuff_Magic; + break; + case Stuff_Zero: + do { + *dst++ = 0; + } + while (--count >= 0 && dst < dst_end); + if (count < 0) + src += 1; + else + *src = (Stuff_Zero + count) ^ Stuff_Magic; + break; + } + } + if (dst < dst_end) + return (NULL); + else + return (src); +} + + +/************************************************************************/ +/* General routines for STRIP */ + +/* + * set_baud sets the baud rate to the rate defined by baudcode + */ +static void set_baud(struct tty_struct *tty, speed_t baudrate) +{ + struct ktermios old_termios; + + mutex_lock(&tty->termios_mutex); + old_termios =*(tty->termios); + tty_encode_baud_rate(tty, baudrate, baudrate); + tty->ops->set_termios(tty, &old_termios); + mutex_unlock(&tty->termios_mutex); +} + +/* + * Convert a string to a Metricom Address. + */ + +#define IS_RADIO_ADDRESS(p) ( \ + isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ + (p)[4] == '-' && \ + isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) + +static int string_to_radio_address(MetricomAddress * addr, __u8 * p) +{ + if (!IS_RADIO_ADDRESS(p)) + return (1); + addr->c[0] = 0; + addr->c[1] = 0; + addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]); + addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]); + addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]); + addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]); + return (0); +} + +/* + * Convert a Metricom Address to a string. + */ + +static __u8 *radio_address_to_string(const MetricomAddress * addr, + MetricomAddressString * p) +{ + sprintf(p->c, "%02X%02X-%02X%02X", addr->c[2], addr->c[3], + addr->c[4], addr->c[5]); + return (p->c); +} + +/* + * Note: Must make sure sx_size is big enough to receive a stuffed + * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's + * big enough to receive a large radio neighbour list (currently 4K). + */ + +static int allocate_buffers(struct strip *strip_info, int mtu) +{ + struct net_device *dev = strip_info->dev; + int sx_size = max_t(int, STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); + int tx_size = STRIP_ENCAP_SIZE(mtu) + MaxCommandStringLength; + __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); + __u8 *s = kmalloc(sx_size, GFP_ATOMIC); + __u8 *t = kmalloc(tx_size, GFP_ATOMIC); + if (r && s && t) { + strip_info->rx_buff = r; + strip_info->sx_buff = s; + strip_info->tx_buff = t; + strip_info->sx_size = sx_size; + strip_info->tx_size = tx_size; + strip_info->mtu = dev->mtu = mtu; + return (1); + } + kfree(r); + kfree(s); + kfree(t); + return (0); +} + +/* + * MTU has been changed by the IP layer. + * We could be in + * an upcall from the tty driver, or in an ip packet queue. + */ +static int strip_change_mtu(struct net_device *dev, int new_mtu) +{ + struct strip *strip_info = netdev_priv(dev); + int old_mtu = strip_info->mtu; + unsigned char *orbuff = strip_info->rx_buff; + unsigned char *osbuff = strip_info->sx_buff; + unsigned char *otbuff = strip_info->tx_buff; + + if (new_mtu > MAX_SEND_MTU) { + printk(KERN_ERR + "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", + strip_info->dev->name, MAX_SEND_MTU); + return -EINVAL; + } + + spin_lock_bh(&strip_lock); + if (!allocate_buffers(strip_info, new_mtu)) { + printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n", + strip_info->dev->name); + spin_unlock_bh(&strip_lock); + return -ENOMEM; + } + + if (strip_info->sx_count) { + if (strip_info->sx_count <= strip_info->sx_size) + memcpy(strip_info->sx_buff, osbuff, + strip_info->sx_count); + else { + strip_info->discard = strip_info->sx_count; + strip_info->rx_over_errors++; + } + } + + if (strip_info->tx_left) { + if (strip_info->tx_left <= strip_info->tx_size) + memcpy(strip_info->tx_buff, strip_info->tx_head, + strip_info->tx_left); + else { + strip_info->tx_left = 0; + strip_info->tx_dropped++; + } + } + strip_info->tx_head = strip_info->tx_buff; + spin_unlock_bh(&strip_lock); + + printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n", + strip_info->dev->name, old_mtu, strip_info->mtu); + + kfree(orbuff); + kfree(osbuff); + kfree(otbuff); + return 0; +} + +static void strip_unlock(struct strip *strip_info) +{ + /* + * Set the timer to go off in one second. + */ + strip_info->idle_timer.expires = jiffies + 1 * HZ; + add_timer(&strip_info->idle_timer); + netif_wake_queue(strip_info->dev); +} + + + +/* + * If the time is in the near future, time_delta prints the number of + * seconds to go into the buffer and returns the address of the buffer. + * If the time is not in the near future, it returns the address of the + * string "Not scheduled" The buffer must be long enough to contain the + * ascii representation of the number plus 9 charactes for the " seconds" + * and the null character. + */ +#ifdef CONFIG_PROC_FS +static char *time_delta(char buffer[], long time) +{ + time -= jiffies; + if (time > LongTime / 2) + return ("Not scheduled"); + if (time < 0) + time = 0; /* Don't print negative times */ + sprintf(buffer, "%ld seconds", time / HZ); + return (buffer); +} + +/* get Nth element of the linked list */ +static struct strip *strip_get_idx(loff_t pos) +{ + struct strip *str; + int i = 0; + + list_for_each_entry_rcu(str, &strip_list, list) { + if (pos == i) + return str; + ++i; + } + return NULL; +} + +static void *strip_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) +{ + rcu_read_lock(); + return *pos ? strip_get_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *strip_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *l; + struct strip *s; + + ++*pos; + if (v == SEQ_START_TOKEN) + return strip_get_idx(1); + + s = v; + l = &s->list; + list_for_each_continue_rcu(l, &strip_list) { + return list_entry(l, struct strip, list); + } + return NULL; +} + +static void strip_seq_stop(struct seq_file *seq, void *v) + __releases(RCU) +{ + rcu_read_unlock(); +} + +static void strip_seq_neighbours(struct seq_file *seq, + const MetricomNodeTable * table, + const char *title) +{ + /* We wrap this in a do/while loop, so if the table changes */ + /* while we're reading it, we just go around and try again. */ + struct timeval t; + + do { + int i; + t = table->timestamp; + if (table->num_nodes) + seq_printf(seq, "\n %s\n", title); + for (i = 0; i < table->num_nodes; i++) { + MetricomNode node; + + spin_lock_bh(&strip_lock); + node = table->node[i]; + spin_unlock_bh(&strip_lock); + seq_printf(seq, " %s\n", node.c); + } + } while (table->timestamp.tv_sec != t.tv_sec + || table->timestamp.tv_usec != t.tv_usec); +} + +/* + * This function prints radio status information via the seq_file + * interface. The interface takes care of buffer size and over + * run issues. + * + * The buffer in seq_file is PAGESIZE (4K) + * so this routine should never print more or it will get truncated. + * With the maximum of 32 portables and 32 poletops + * reported, the routine outputs 3107 bytes into the buffer. + */ +static void strip_seq_status_info(struct seq_file *seq, + const struct strip *strip_info) +{ + char temp[32]; + MetricomAddressString addr_string; + + /* First, we must copy all of our data to a safe place, */ + /* in case a serial interrupt comes in and changes it. */ + int tx_left = strip_info->tx_left; + unsigned long rx_average_pps = strip_info->rx_average_pps; + unsigned long tx_average_pps = strip_info->tx_average_pps; + unsigned long sx_average_pps = strip_info->sx_average_pps; + int working = strip_info->working; + int firmware_level = strip_info->firmware_level; + long watchdog_doprobe = strip_info->watchdog_doprobe; + long watchdog_doreset = strip_info->watchdog_doreset; + long gratuitous_arp = strip_info->gratuitous_arp; + long arp_interval = strip_info->arp_interval; + FirmwareVersion firmware_version = strip_info->firmware_version; + SerialNumber serial_number = strip_info->serial_number; + BatteryVoltage battery_voltage = strip_info->battery_voltage; + char *if_name = strip_info->dev->name; + MetricomAddress true_dev_addr = strip_info->true_dev_addr; + MetricomAddress dev_dev_addr = + *(MetricomAddress *) strip_info->dev->dev_addr; + int manual_dev_addr = strip_info->manual_dev_addr; +#ifdef EXT_COUNTERS + unsigned long rx_bytes = strip_info->rx_bytes; + unsigned long tx_bytes = strip_info->tx_bytes; + unsigned long rx_rbytes = strip_info->rx_rbytes; + unsigned long tx_rbytes = strip_info->tx_rbytes; + unsigned long rx_sbytes = strip_info->rx_sbytes; + unsigned long tx_sbytes = strip_info->tx_sbytes; + unsigned long rx_ebytes = strip_info->rx_ebytes; + unsigned long tx_ebytes = strip_info->tx_ebytes; +#endif + + seq_printf(seq, "\nInterface name\t\t%s\n", if_name); + seq_printf(seq, " Radio working:\t\t%s\n", working ? "Yes" : "No"); + radio_address_to_string(&true_dev_addr, &addr_string); + seq_printf(seq, " Radio address:\t\t%s\n", addr_string.c); + if (manual_dev_addr) { + radio_address_to_string(&dev_dev_addr, &addr_string); + seq_printf(seq, " Device address:\t%s\n", addr_string.c); + } + seq_printf(seq, " Firmware version:\t%s", !working ? "Unknown" : + !firmware_level ? "Should be upgraded" : + firmware_version.c); + if (firmware_level >= ChecksummedMessages) + seq_printf(seq, " (Checksums Enabled)"); + seq_printf(seq, "\n"); + seq_printf(seq, " Serial number:\t\t%s\n", serial_number.c); + seq_printf(seq, " Battery voltage:\t%s\n", battery_voltage.c); + seq_printf(seq, " Transmit queue (bytes):%d\n", tx_left); + seq_printf(seq, " Receive packet rate: %ld packets per second\n", + rx_average_pps / 8); + seq_printf(seq, " Transmit packet rate: %ld packets per second\n", + tx_average_pps / 8); + seq_printf(seq, " Sent packet rate: %ld packets per second\n", + sx_average_pps / 8); + seq_printf(seq, " Next watchdog probe:\t%s\n", + time_delta(temp, watchdog_doprobe)); + seq_printf(seq, " Next watchdog reset:\t%s\n", + time_delta(temp, watchdog_doreset)); + seq_printf(seq, " Next gratuitous ARP:\t"); + + if (!memcmp + (strip_info->dev->dev_addr, zero_address.c, + sizeof(zero_address))) + seq_printf(seq, "Disabled\n"); + else { + seq_printf(seq, "%s\n", time_delta(temp, gratuitous_arp)); + seq_printf(seq, " Next ARP interval:\t%ld seconds\n", + JIFFIE_TO_SEC(arp_interval)); + } + + if (working) { +#ifdef EXT_COUNTERS + seq_printf(seq, "\n"); + seq_printf(seq, + " Total bytes: \trx:\t%lu\ttx:\t%lu\n", + rx_bytes, tx_bytes); + seq_printf(seq, + " thru radio: \trx:\t%lu\ttx:\t%lu\n", + rx_rbytes, tx_rbytes); + seq_printf(seq, + " thru serial port: \trx:\t%lu\ttx:\t%lu\n", + rx_sbytes, tx_sbytes); + seq_printf(seq, + " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", + rx_ebytes, tx_ebytes); +#endif + strip_seq_neighbours(seq, &strip_info->poletops, + "Poletops:"); + strip_seq_neighbours(seq, &strip_info->portables, + "Portables:"); + } +} + +/* + * This function is exports status information from the STRIP driver through + * the /proc file system. + */ +static int strip_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "strip_version: %s\n", StripVersion); + else + strip_seq_status_info(seq, (const struct strip *)v); + return 0; +} + + +static const struct seq_operations strip_seq_ops = { + .start = strip_seq_start, + .next = strip_seq_next, + .stop = strip_seq_stop, + .show = strip_seq_show, +}; + +static int strip_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &strip_seq_ops); +} + +static const struct file_operations strip_seq_fops = { + .owner = THIS_MODULE, + .open = strip_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + + + +/************************************************************************/ +/* Sending routines */ + +static void ResetRadio(struct strip *strip_info) +{ + struct tty_struct *tty = strip_info->tty; + static const char init[] = "ate0q1dt**starmode\r**"; + StringDescriptor s = { init, sizeof(init) - 1 }; + + /* + * If the radio isn't working anymore, + * we should clear the old status information. + */ + if (strip_info->working) { + printk(KERN_INFO "%s: No response: Resetting radio.\n", + strip_info->dev->name); + strip_info->firmware_version.c[0] = '\0'; + strip_info->serial_number.c[0] = '\0'; + strip_info->battery_voltage.c[0] = '\0'; + strip_info->portables.num_nodes = 0; + do_gettimeofday(&strip_info->portables.timestamp); + strip_info->poletops.num_nodes = 0; + do_gettimeofday(&strip_info->poletops.timestamp); + } + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + strip_info->rx_average_pps = 0; + strip_info->tx_average_pps = 0; + strip_info->sx_average_pps = 0; + + /* Mark radio address as unknown */ + *(MetricomAddress *) & strip_info->true_dev_addr = zero_address; + if (!strip_info->manual_dev_addr) + *(MetricomAddress *) strip_info->dev->dev_addr = + zero_address; + strip_info->working = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; + strip_info->watchdog_doprobe = jiffies + 10 * HZ; + strip_info->watchdog_doreset = jiffies + 1 * HZ; + + /* If the user has selected a baud rate above 38.4 see what magic we have to do */ + if (strip_info->user_baud > 38400) { + /* + * Subtle stuff: Pay attention :-) + * If the serial port is currently at the user's selected (>38.4) rate, + * then we temporarily switch to 19.2 and issue the ATS304 command + * to tell the radio to switch to the user's selected rate. + * If the serial port is not currently at that rate, that means we just + * issued the ATS304 command last time through, so this time we restore + * the user's selected rate and issue the normal starmode reset string. + */ + if (strip_info->user_baud == tty_get_baud_rate(tty)) { + static const char b0[] = "ate0q1s304=57600\r"; + static const char b1[] = "ate0q1s304=115200\r"; + static const StringDescriptor baudstring[2] = + { {b0, sizeof(b0) - 1} + , {b1, sizeof(b1) - 1} + }; + set_baud(tty, 19200); + if (strip_info->user_baud == 57600) + s = baudstring[0]; + else if (strip_info->user_baud == 115200) + s = baudstring[1]; + else + s = baudstring[1]; /* For now */ + } else + set_baud(tty, strip_info->user_baud); + } + + tty->ops->write(tty, s.string, s.length); +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += s.length; +#endif +} + +/* + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + */ + +static void strip_write_some_more(struct tty_struct *tty) +{ + struct strip *strip_info = tty->disc_data; + + /* First make sure we're connected. */ + if (!strip_info || strip_info->magic != STRIP_MAGIC || + !netif_running(strip_info->dev)) + return; + + if (strip_info->tx_left > 0) { + int num_written = + tty->ops->write(tty, strip_info->tx_head, + strip_info->tx_left); + strip_info->tx_left -= num_written; + strip_info->tx_head += num_written; +#ifdef EXT_COUNTERS + strip_info->tx_sbytes += num_written; +#endif + } else { /* Else start transmission of another packet */ + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + strip_unlock(strip_info); + } +} + +static __u8 *add_checksum(__u8 * buffer, __u8 * end) +{ + __u16 sum = 0; + __u8 *p = buffer; + while (p < end) + sum += *p++; + end[3] = hextable[sum & 0xF]; + sum >>= 4; + end[2] = hextable[sum & 0xF]; + sum >>= 4; + end[1] = hextable[sum & 0xF]; + sum >>= 4; + end[0] = hextable[sum & 0xF]; + return (end + 4); +} + +static unsigned char *strip_make_packet(unsigned char *buffer, + struct strip *strip_info, + struct sk_buff *skb) +{ + __u8 *ptr = buffer; + __u8 *stuffstate = NULL; + STRIP_Header *header = (STRIP_Header *) skb->data; + MetricomAddress haddr = header->dst_addr; + int len = skb->len - sizeof(STRIP_Header); + MetricomKey key; + + /*HexDump("strip_make_packet", strip_info, skb->data, skb->data + skb->len); */ + + if (header->protocol == htons(ETH_P_IP)) + key = SIP0Key; + else if (header->protocol == htons(ETH_P_ARP)) + key = ARP0Key; + else { + printk(KERN_ERR + "%s: strip_make_packet: Unknown packet type 0x%04X\n", + strip_info->dev->name, ntohs(header->protocol)); + return (NULL); + } + + if (len > strip_info->mtu) { + printk(KERN_ERR + "%s: Dropping oversized transmit packet: %d bytes\n", + strip_info->dev->name, len); + return (NULL); + } + + /* + * If we're sending to ourselves, discard the packet. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) { + printk(KERN_ERR "%s: Dropping packet addressed to self\n", + strip_info->dev->name); + return (NULL); + } + + /* + * If this is a broadcast packet, send it to our designated Metricom + * 'broadcast hub' radio (First byte of address being 0xFF means broadcast) + */ + if (haddr.c[0] == 0xFF) { + __be32 brd = 0; + struct in_device *in_dev; + + rcu_read_lock(); + in_dev = __in_dev_get_rcu(strip_info->dev); + if (in_dev == NULL) { + rcu_read_unlock(); + return NULL; + } + if (in_dev->ifa_list) + brd = in_dev->ifa_list->ifa_broadcast; + rcu_read_unlock(); + + /* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */ + if (!arp_query(haddr.c, brd, strip_info->dev)) { + printk(KERN_ERR + "%s: Unable to send packet (no broadcast hub configured)\n", + strip_info->dev->name); + return (NULL); + } + /* + * If we are the broadcast hub, don't bother sending to ourselves. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp + (haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) + return (NULL); + } + + *ptr++ = 0x0D; + *ptr++ = '*'; + *ptr++ = hextable[haddr.c[2] >> 4]; + *ptr++ = hextable[haddr.c[2] & 0xF]; + *ptr++ = hextable[haddr.c[3] >> 4]; + *ptr++ = hextable[haddr.c[3] & 0xF]; + *ptr++ = '-'; + *ptr++ = hextable[haddr.c[4] >> 4]; + *ptr++ = hextable[haddr.c[4] & 0xF]; + *ptr++ = hextable[haddr.c[5] >> 4]; + *ptr++ = hextable[haddr.c[5] & 0xF]; + *ptr++ = '*'; + *ptr++ = key.c[0]; + *ptr++ = key.c[1]; + *ptr++ = key.c[2]; + *ptr++ = key.c[3]; + + ptr = + StuffData(skb->data + sizeof(STRIP_Header), len, ptr, + &stuffstate); + + if (strip_info->firmware_level >= ChecksummedMessages) + ptr = add_checksum(buffer + 1, ptr); + + *ptr++ = 0x0D; + return (ptr); +} + +static void strip_send(struct strip *strip_info, struct sk_buff *skb) +{ + MetricomAddress haddr; + unsigned char *ptr = strip_info->tx_buff; + int doreset = (long) jiffies - strip_info->watchdog_doreset >= 0; + int doprobe = (long) jiffies - strip_info->watchdog_doprobe >= 0 + && !doreset; + __be32 addr, brd; + + /* + * 1. If we have a packet, encapsulate it and put it in the buffer + */ + if (skb) { + char *newptr = strip_make_packet(ptr, strip_info, skb); + strip_info->tx_pps_count++; + if (!newptr) + strip_info->tx_dropped++; + else { + ptr = newptr; + strip_info->sx_pps_count++; + strip_info->tx_packets++; /* Count another successful packet */ +#ifdef EXT_COUNTERS + strip_info->tx_bytes += skb->len; + strip_info->tx_rbytes += ptr - strip_info->tx_buff; +#endif + /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr); */ + /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr); */ + } + } + + /* + * 2. If it is time for another tickle, tack it on, after the packet + */ + if (doprobe) { + StringDescriptor ts = CommandString[strip_info->next_command]; +#if TICKLE_TIMERS + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Sending tickle string %d at %02d.%06d\n", + strip_info->next_command, tv.tv_sec % 100, + tv.tv_usec); + } +#endif + if (ptr == strip_info->tx_buff) + *ptr++ = 0x0D; + + *ptr++ = '*'; /* First send "**" to provoke an error message */ + *ptr++ = '*'; + + /* Then add the command */ + memcpy(ptr, ts.string, ts.length); + + /* Add a checksum ? */ + if (strip_info->firmware_level < ChecksummedMessages) + ptr += ts.length; + else + ptr = add_checksum(ptr, ptr + ts.length); + + *ptr++ = 0x0D; /* Terminate the command with a */ + + /* Cycle to next periodic command? */ + if (strip_info->firmware_level >= StructuredMessages) + if (++strip_info->next_command >= + ARRAY_SIZE(CommandString)) + strip_info->next_command = 0; +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += ts.length; +#endif + strip_info->watchdog_doprobe = jiffies + 10 * HZ; + strip_info->watchdog_doreset = jiffies + 1 * HZ; + /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev->name); */ + } + + /* + * 3. Set up the strip_info ready to send the data (if any). + */ + strip_info->tx_head = strip_info->tx_buff; + strip_info->tx_left = ptr - strip_info->tx_buff; + set_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags); + /* + * 4. Debugging check to make sure we're not overflowing the buffer. + */ + if (strip_info->tx_size - strip_info->tx_left < 20) + printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", + strip_info->dev->name, strip_info->tx_left, + strip_info->tx_size - strip_info->tx_left); + + /* + * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in + * the buffer, strip_write_some_more will send it after the reset has finished + */ + if (doreset) { + ResetRadio(strip_info); + return; + } + + if (1) { + struct in_device *in_dev; + + brd = addr = 0; + rcu_read_lock(); + in_dev = __in_dev_get_rcu(strip_info->dev); + if (in_dev) { + if (in_dev->ifa_list) { + brd = in_dev->ifa_list->ifa_broadcast; + addr = in_dev->ifa_list->ifa_local; + } + } + rcu_read_unlock(); + } + + + /* + * 6. If it is time for a periodic ARP, queue one up to be sent. + * We only do this if: + * 1. The radio is working + * 2. It's time to send another periodic ARP + * 3. We really know what our address is (and it is not manually set to zero) + * 4. We have a designated broadcast address configured + * If we queue up an ARP packet when we don't have a designated broadcast + * address configured, then the packet will just have to be discarded in + * strip_make_packet. This is not fatal, but it causes misleading information + * to be displayed in tcpdump. tcpdump will report that periodic APRs are + * being sent, when in fact they are not, because they are all being dropped + * in the strip_make_packet routine. + */ + if (strip_info->working + && (long) jiffies - strip_info->gratuitous_arp >= 0 + && memcmp(strip_info->dev->dev_addr, zero_address.c, + sizeof(zero_address)) + && arp_query(haddr.c, brd, strip_info->dev)) { + /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", + strip_info->dev->name, strip_info->arp_interval / HZ); */ + strip_info->gratuitous_arp = + jiffies + strip_info->arp_interval; + strip_info->arp_interval *= 2; + if (strip_info->arp_interval > MaxARPInterval) + strip_info->arp_interval = MaxARPInterval; + if (addr) + arp_send(ARPOP_REPLY, ETH_P_ARP, addr, /* Target address of ARP packet is our address */ + strip_info->dev, /* Device to send packet on */ + addr, /* Source IP address this ARP packet comes from */ + NULL, /* Destination HW address is NULL (broadcast it) */ + strip_info->dev->dev_addr, /* Source HW address is our HW address */ + strip_info->dev->dev_addr); /* Target HW address is our HW address (redundant) */ + } + + /* + * 7. All ready. Start the transmission + */ + strip_write_some_more(strip_info->tty); +} + +/* Encapsulate a datagram and kick it into a TTY queue. */ +static netdev_tx_t strip_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct strip *strip_info = netdev_priv(dev); + + if (!netif_running(dev)) { + printk(KERN_ERR "%s: xmit call when iface is down\n", + dev->name); + return NETDEV_TX_BUSY; + } + + netif_stop_queue(dev); + + del_timer(&strip_info->idle_timer); + + + if (time_after(jiffies, strip_info->pps_timer + HZ)) { + unsigned long t = jiffies - strip_info->pps_timer; + unsigned long rx_pps_count = + DIV_ROUND_CLOSEST(strip_info->rx_pps_count*HZ*8, t); + unsigned long tx_pps_count = + DIV_ROUND_CLOSEST(strip_info->tx_pps_count*HZ*8, t); + unsigned long sx_pps_count = + DIV_ROUND_CLOSEST(strip_info->sx_pps_count*HZ*8, t); + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + + strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2; + strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2; + strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2; + + if (rx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n", + strip_info->dev->name, rx_pps_count / 8); + if (tx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Tx %ld packets per second.\n", + strip_info->dev->name, tx_pps_count / 8); + if (sx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Sending %ld packets per second.\n", + strip_info->dev->name, sx_pps_count / 8); + } + + spin_lock_bh(&strip_lock); + + strip_send(strip_info, skb); + + spin_unlock_bh(&strip_lock); + + if (skb) + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* + * IdleTask periodically calls strip_xmit, so even when we have no IP packets + * to send for an extended period of time, the watchdog processing still gets + * done to ensure that the radio stays in Starmode + */ + +static void strip_IdleTask(unsigned long parameter) +{ + strip_xmit(NULL, (struct net_device *) parameter); +} + +/* + * Create the MAC header for an arbitrary protocol layer + * + * saddr!=NULL means use this specific address (n/a for Metricom) + * saddr==NULL means use default device source address + * daddr!=NULL means use this destination address + * daddr==NULL means leave destination address alone + * (e.g. unresolved arp -- kernel will call + * rebuild_header later to fill in the address) + */ + +static int strip_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) +{ + struct strip *strip_info = netdev_priv(dev); + STRIP_Header *header = (STRIP_Header *) skb_push(skb, sizeof(STRIP_Header)); + + /*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type, + type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : ""); */ + + header->src_addr = strip_info->true_dev_addr; + header->protocol = htons(type); + + /*HexDump("strip_header", netdev_priv(dev), skb->data, skb->data + skb->len); */ + + if (!daddr) + return (-dev->hard_header_len); + + header->dst_addr = *(MetricomAddress *) daddr; + return (dev->hard_header_len); +} + +/* + * Rebuild the MAC header. This is called after an ARP + * (or in future other address resolution) has completed on this + * sk_buff. We now let ARP fill in the other fields. + * I think this should return zero if packet is ready to send, + * or non-zero if it needs more time to do an address lookup + */ + +static int strip_rebuild_header(struct sk_buff *skb) +{ +#ifdef CONFIG_INET + STRIP_Header *header = (STRIP_Header *) skb->data; + + /* Arp find returns zero if if knows the address, */ + /* or if it doesn't know the address it sends an ARP packet and returns non-zero */ + return arp_find(header->dst_addr.c, skb) ? 1 : 0; +#else + return 0; +#endif +} + + +/************************************************************************/ +/* Receiving routines */ + +/* + * This function parses the response to the ATS300? command, + * extracting the radio version and serial number. + */ +static void get_radio_version(struct strip *strip_info, __u8 * ptr, __u8 * end) +{ + __u8 *p, *value_begin, *value_end; + int len; + + /* Determine the beginning of the second line of the payload */ + p = ptr; + while (p < end && *p != 10) + p++; + if (p >= end) + return; + p++; + value_begin = p; + + /* Determine the end of line */ + while (p < end && *p != 10) + p++; + if (p >= end) + return; + value_end = p; + p++; + + len = value_end - value_begin; + len = min_t(int, len, sizeof(FirmwareVersion) - 1); + if (strip_info->firmware_version.c[0] == 0) + printk(KERN_INFO "%s: Radio Firmware: %.*s\n", + strip_info->dev->name, len, value_begin); + sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); + + /* Look for the first colon */ + while (p < end && *p != ':') + p++; + if (p >= end) + return; + /* Skip over the space */ + p += 2; + len = sizeof(SerialNumber) - 1; + if (p + len <= end) { + sprintf(strip_info->serial_number.c, "%.*s", len, p); + } else { + printk(KERN_DEBUG + "STRIP: radio serial number shorter (%zd) than expected (%d)\n", + end - p, len); + } +} + +/* + * This function parses the response to the ATS325? command, + * extracting the radio battery voltage. + */ +static void get_radio_voltage(struct strip *strip_info, __u8 * ptr, __u8 * end) +{ + int len; + + len = sizeof(BatteryVoltage) - 1; + if (ptr + len <= end) { + sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); + } else { + printk(KERN_DEBUG + "STRIP: radio voltage string shorter (%zd) than expected (%d)\n", + end - ptr, len); + } +} + +/* + * This function parses the responses to the AT~LA and ATS311 commands, + * which list the radio's neighbours. + */ +static void get_radio_neighbours(MetricomNodeTable * table, __u8 * ptr, __u8 * end) +{ + table->num_nodes = 0; + while (ptr < end && table->num_nodes < NODE_TABLE_SIZE) { + MetricomNode *node = &table->node[table->num_nodes++]; + char *dst = node->c, *limit = dst + sizeof(*node) - 1; + while (ptr < end && *ptr <= 32) + ptr++; + while (ptr < end && dst < limit && *ptr != 10) + *dst++ = *ptr++; + *dst++ = 0; + while (ptr < end && ptr[-1] != 10) + ptr++; + } + do_gettimeofday(&table->timestamp); +} + +static int get_radio_address(struct strip *strip_info, __u8 * p) +{ + MetricomAddress addr; + + if (string_to_radio_address(&addr, p)) + return (1); + + /* See if our radio address has changed */ + if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr))) { + MetricomAddressString addr_string; + radio_address_to_string(&addr, &addr_string); + printk(KERN_INFO "%s: Radio address = %s\n", + strip_info->dev->name, addr_string.c); + strip_info->true_dev_addr = addr; + if (!strip_info->manual_dev_addr) + *(MetricomAddress *) strip_info->dev->dev_addr = + addr; + /* Give the radio a few seconds to get its head straight, then send an arp */ + strip_info->gratuitous_arp = jiffies + 15 * HZ; + strip_info->arp_interval = 1 * HZ; + } + return (0); +} + +static int verify_checksum(struct strip *strip_info) +{ + __u8 *p = strip_info->sx_buff; + __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4; + u_short sum = + (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) | + (READHEX16(end[2]) << 4) | (READHEX16(end[3])); + while (p < end) + sum -= *p++; + if (sum == 0 && strip_info->firmware_level == StructuredMessages) { + strip_info->firmware_level = ChecksummedMessages; + printk(KERN_INFO "%s: Radio provides message checksums\n", + strip_info->dev->name); + } + return (sum == 0); +} + +static void RecvErr(char *msg, struct strip *strip_info) +{ + __u8 *ptr = strip_info->sx_buff; + __u8 *end = strip_info->sx_buff + strip_info->sx_count; + DumpData(msg, strip_info, ptr, end); + strip_info->rx_errors++; +} + +static void RecvErr_Message(struct strip *strip_info, __u8 * sendername, + const __u8 * msg, u_long len) +{ + if (has_prefix(msg, len, "001")) { /* Not in StarMode! */ + RecvErr("Error Msg:", strip_info); + printk(KERN_INFO "%s: Radio %s is not in StarMode\n", + strip_info->dev->name, sendername); + } + + else if (has_prefix(msg, len, "002")) { /* Remap handle */ + /* We ignore "Remap handle" messages for now */ + } + + else if (has_prefix(msg, len, "003")) { /* Can't resolve name */ + RecvErr("Error Msg:", strip_info); + printk(KERN_INFO "%s: Destination radio name is unknown\n", + strip_info->dev->name); + } + + else if (has_prefix(msg, len, "004")) { /* Name too small or missing */ + strip_info->watchdog_doreset = jiffies + LongTime; +#if TICKLE_TIMERS + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO + "**** Got ERR_004 response at %02d.%06d\n", + tv.tv_sec % 100, tv.tv_usec); + } +#endif + if (!strip_info->working) { + strip_info->working = TRUE; + printk(KERN_INFO "%s: Radio now in starmode\n", + strip_info->dev->name); + /* + * If the radio has just entered a working state, we should do our first + * probe ASAP, so that we find out our radio address etc. without delay. + */ + strip_info->watchdog_doprobe = jiffies; + } + if (strip_info->firmware_level == NoStructure && sendername) { + strip_info->firmware_level = StructuredMessages; + strip_info->next_command = 0; /* Try to enable checksums ASAP */ + printk(KERN_INFO + "%s: Radio provides structured messages\n", + strip_info->dev->name); + } + if (strip_info->firmware_level >= StructuredMessages) { + /* + * If this message has a valid checksum on the end, then the call to verify_checksum + * will elevate the firmware_level to ChecksummedMessages for us. (The actual return + * code from verify_checksum is ignored here.) + */ + verify_checksum(strip_info); + /* + * If the radio has structured messages but we don't yet have all our information about it, + * we should do probes without delay, until we have gathered all the information + */ + if (!GOT_ALL_RADIO_INFO(strip_info)) + strip_info->watchdog_doprobe = jiffies; + } + } + + else if (has_prefix(msg, len, "005")) /* Bad count specification */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, len, "006")) /* Header too big */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, len, "007")) { /* Body too big */ + RecvErr("Error Msg:", strip_info); + printk(KERN_ERR + "%s: Error! Packet size too big for radio.\n", + strip_info->dev->name); + } + + else if (has_prefix(msg, len, "008")) { /* Bad character in name */ + RecvErr("Error Msg:", strip_info); + printk(KERN_ERR + "%s: Radio name contains illegal character\n", + strip_info->dev->name); + } + + else if (has_prefix(msg, len, "009")) /* No count or line terminator */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, len, "010")) /* Invalid checksum */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, len, "011")) /* Checksum didn't match */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, len, "012")) /* Failed to transmit packet */ + RecvErr("Error Msg:", strip_info); + + else + RecvErr("Error Msg:", strip_info); +} + +static void process_AT_response(struct strip *strip_info, __u8 * ptr, + __u8 * end) +{ + u_long len; + __u8 *p = ptr; + while (p < end && p[-1] != 10) + p++; /* Skip past first newline character */ + /* Now ptr points to the AT command, and p points to the text of the response. */ + len = p - ptr; + +#if TICKLE_TIMERS + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Got AT response %.7s at %02d.%06d\n", + ptr, tv.tv_sec % 100, tv.tv_usec); + } +#endif + + if (has_prefix(ptr, len, "ATS300?")) + get_radio_version(strip_info, p, end); + else if (has_prefix(ptr, len, "ATS305?")) + get_radio_address(strip_info, p); + else if (has_prefix(ptr, len, "ATS311?")) + get_radio_neighbours(&strip_info->poletops, p, end); + else if (has_prefix(ptr, len, "ATS319=7")) + verify_checksum(strip_info); + else if (has_prefix(ptr, len, "ATS325?")) + get_radio_voltage(strip_info, p, end); + else if (has_prefix(ptr, len, "AT~LA")) + get_radio_neighbours(&strip_info->portables, p, end); + else + RecvErr("Unknown AT Response:", strip_info); +} + +static void process_ACK(struct strip *strip_info, __u8 * ptr, __u8 * end) +{ + /* Currently we don't do anything with ACKs from the radio */ +} + +static void process_Info(struct strip *strip_info, __u8 * ptr, __u8 * end) +{ + if (ptr + 16 > end) + RecvErr("Bad Info Msg:", strip_info); +} + +static struct net_device *get_strip_dev(struct strip *strip_info) +{ + /* If our hardware address is *manually set* to zero, and we know our */ + /* real radio hardware address, try to find another strip device that has been */ + /* manually set to that address that we can 'transfer ownership' of this packet to */ + if (strip_info->manual_dev_addr && + !memcmp(strip_info->dev->dev_addr, zero_address.c, + sizeof(zero_address)) + && memcmp(&strip_info->true_dev_addr, zero_address.c, + sizeof(zero_address))) { + struct net_device *dev; + read_lock_bh(&dev_base_lock); + for_each_netdev(&init_net, dev) { + if (dev->type == strip_info->dev->type && + !memcmp(dev->dev_addr, + &strip_info->true_dev_addr, + sizeof(MetricomAddress))) { + printk(KERN_INFO + "%s: Transferred packet ownership to %s.\n", + strip_info->dev->name, dev->name); + read_unlock_bh(&dev_base_lock); + return (dev); + } + } + read_unlock_bh(&dev_base_lock); + } + return (strip_info->dev); +} + +/* + * Send one completely decapsulated datagram to the next layer. + */ + +static void deliver_packet(struct strip *strip_info, STRIP_Header * header, + __u16 packetlen) +{ + struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen); + if (!skb) { + printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", + strip_info->dev->name); + strip_info->rx_dropped++; + } else { + memcpy(skb_put(skb, sizeof(STRIP_Header)), header, + sizeof(STRIP_Header)); + memcpy(skb_put(skb, packetlen), strip_info->rx_buff, + packetlen); + skb->dev = get_strip_dev(strip_info); + skb->protocol = header->protocol; + skb_reset_mac_header(skb); + + /* Having put a fake header on the front of the sk_buff for the */ + /* benefit of tools like tcpdump, skb_pull now 'consumes' that */ + /* fake header before we hand the packet up to the next layer. */ + skb_pull(skb, sizeof(STRIP_Header)); + + /* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */ + strip_info->rx_packets++; + strip_info->rx_pps_count++; +#ifdef EXT_COUNTERS + strip_info->rx_bytes += packetlen; +#endif + netif_rx(skb); + } +} + +static void process_IP_packet(struct strip *strip_info, + STRIP_Header * header, __u8 * ptr, + __u8 * end) +{ + __u16 packetlen; + + /* Decode start of the IP packet header */ + ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4); + if (!ptr) { + RecvErr("IP Packet too short", strip_info); + return; + } + + packetlen = ((__u16) strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3]; + + if (packetlen > MAX_RECV_MTU) { + printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n", + strip_info->dev->name, packetlen); + strip_info->rx_dropped++; + return; + } + + /*printk(KERN_INFO "%s: Got %d byte IP packet\n", strip_info->dev->name, packetlen); */ + + /* Decode remainder of the IP packet */ + ptr = + UnStuffData(ptr, end, strip_info->rx_buff + 4, packetlen - 4); + if (!ptr) { + RecvErr("IP Packet too short", strip_info); + return; + } + + if (ptr < end) { + RecvErr("IP Packet too long", strip_info); + return; + } + + header->protocol = htons(ETH_P_IP); + + deliver_packet(strip_info, header, packetlen); +} + +static void process_ARP_packet(struct strip *strip_info, + STRIP_Header * header, __u8 * ptr, + __u8 * end) +{ + __u16 packetlen; + struct arphdr *arphdr = (struct arphdr *) strip_info->rx_buff; + + /* Decode start of the ARP packet */ + ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8); + if (!ptr) { + RecvErr("ARP Packet too short", strip_info); + return; + } + + packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2; + + if (packetlen > MAX_RECV_MTU) { + printk(KERN_INFO + "%s: Dropping oversized received ARP packet: %d bytes\n", + strip_info->dev->name, packetlen); + strip_info->rx_dropped++; + return; + } + + /*printk(KERN_INFO "%s: Got %d byte ARP %s\n", + strip_info->dev->name, packetlen, + ntohs(arphdr->ar_op) == ARPOP_REQUEST ? "request" : "reply"); */ + + /* Decode remainder of the ARP packet */ + ptr = + UnStuffData(ptr, end, strip_info->rx_buff + 8, packetlen - 8); + if (!ptr) { + RecvErr("ARP Packet too short", strip_info); + return; + } + + if (ptr < end) { + RecvErr("ARP Packet too long", strip_info); + return; + } + + header->protocol = htons(ETH_P_ARP); + + deliver_packet(strip_info, header, packetlen); +} + +/* + * process_text_message processes a -terminated block of data received + * from the radio that doesn't begin with a '*' character. All normal + * Starmode communication messages with the radio begin with a '*', + * so any text that does not indicates a serial port error, a radio that + * is in Hayes command mode instead of Starmode, or a radio with really + * old firmware that doesn't frame its Starmode responses properly. + */ +static void process_text_message(struct strip *strip_info) +{ + __u8 *msg = strip_info->sx_buff; + int len = strip_info->sx_count; + + /* Check for anything that looks like it might be our radio name */ + /* (This is here for backwards compatibility with old firmware) */ + if (len == 9 && get_radio_address(strip_info, msg) == 0) + return; + + if (text_equal(msg, len, "OK")) + return; /* Ignore 'OK' responses from prior commands */ + if (text_equal(msg, len, "ERROR")) + return; /* Ignore 'ERROR' messages */ + if (has_prefix(msg, len, "ate0q1")) + return; /* Ignore character echo back from the radio */ + + /* Catch other error messages */ + /* (This is here for backwards compatibility with old firmware) */ + if (has_prefix(msg, len, "ERR_")) { + RecvErr_Message(strip_info, NULL, &msg[4], len - 4); + return; + } + + RecvErr("No initial *", strip_info); +} + +/* + * process_message processes a -terminated block of data received + * from the radio. If the radio is not in Starmode or has old firmware, + * it may be a line of text in response to an AT command. Ideally, with + * a current radio that's properly in Starmode, all data received should + * be properly framed and checksummed radio message blocks, containing + * either a starmode packet, or a other communication from the radio + * firmware, like "INF_" Info messages and &COMMAND responses. + */ +static void process_message(struct strip *strip_info) +{ + STRIP_Header header = { zero_address, zero_address, 0 }; + __u8 *ptr = strip_info->sx_buff; + __u8 *end = strip_info->sx_buff + strip_info->sx_count; + __u8 sendername[32], *sptr = sendername; + MetricomKey key; + + /*HexDump("Receiving", strip_info, ptr, end); */ + + /* Check for start of address marker, and then skip over it */ + if (*ptr == '*') + ptr++; + else { + process_text_message(strip_info); + return; + } + + /* Copy out the return address */ + while (ptr < end && *ptr != '*' + && sptr < ARRAY_END(sendername) - 1) + *sptr++ = *ptr++; + *sptr = 0; /* Null terminate the sender name */ + + /* Check for end of address marker, and skip over it */ + if (ptr >= end || *ptr != '*') { + RecvErr("No second *", strip_info); + return; + } + ptr++; /* Skip the second '*' */ + + /* If the sender name is "&COMMAND", ignore this 'packet' */ + /* (This is here for backwards compatibility with old firmware) */ + if (!strcmp(sendername, "&COMMAND")) { + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; + return; + } + + if (ptr + 4 > end) { + RecvErr("No proto key", strip_info); + return; + } + + /* Get the protocol key out of the buffer */ + key.c[0] = *ptr++; + key.c[1] = *ptr++; + key.c[2] = *ptr++; + key.c[3] = *ptr++; + + /* If we're using checksums, verify the checksum at the end of the packet */ + if (strip_info->firmware_level >= ChecksummedMessages) { + end -= 4; /* Chop the last four bytes off the packet (they're the checksum) */ + if (ptr > end) { + RecvErr("Missing Checksum", strip_info); + return; + } + if (!verify_checksum(strip_info)) { + RecvErr("Bad Checksum", strip_info); + return; + } + } + + /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev->name, sendername); */ + + /* + * Fill in (pseudo) source and destination addresses in the packet. + * We assume that the destination address was our address (the radio does not + * tell us this). If the radio supplies a source address, then we use it. + */ + header.dst_addr = strip_info->true_dev_addr; + string_to_radio_address(&header.src_addr, sendername); + +#ifdef EXT_COUNTERS + if (key.l == SIP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_IP_packet(strip_info, &header, ptr, end); + } else if (key.l == ARP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_ARP_packet(strip_info, &header, ptr, end); + } else if (key.l == ATR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_AT_response(strip_info, ptr, end); + } else if (key.l == ACK_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_ACK(strip_info, ptr, end); + } else if (key.l == INF_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_Info(strip_info, ptr, end); + } else if (key.l == ERR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + RecvErr_Message(strip_info, sendername, ptr, end - ptr); + } else + RecvErr("Unrecognized protocol key", strip_info); +#else + if (key.l == SIP0Key.l) + process_IP_packet(strip_info, &header, ptr, end); + else if (key.l == ARP0Key.l) + process_ARP_packet(strip_info, &header, ptr, end); + else if (key.l == ATR_Key.l) + process_AT_response(strip_info, ptr, end); + else if (key.l == ACK_Key.l) + process_ACK(strip_info, ptr, end); + else if (key.l == INF_Key.l) + process_Info(strip_info, ptr, end); + else if (key.l == ERR_Key.l) + RecvErr_Message(strip_info, sendername, ptr, end - ptr); + else + RecvErr("Unrecognized protocol key", strip_info); +#endif +} + +#define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \ + (X) == TTY_FRAME ? "Framing Error" : \ + (X) == TTY_PARITY ? "Parity Error" : \ + (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error") + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of STRIP data has been received, which can now be decapsulated + * and sent on to some IP layer for further processing. + */ + +static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct strip *strip_info = tty->disc_data; + const unsigned char *end = cp + count; + + if (!strip_info || strip_info->magic != STRIP_MAGIC + || !netif_running(strip_info->dev)) + return; + + spin_lock_bh(&strip_lock); +#if 0 + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO + "**** strip_receive_buf: %3d bytes at %02d.%06d\n", + count, tv.tv_sec % 100, tv.tv_usec); + } +#endif + +#ifdef EXT_COUNTERS + strip_info->rx_sbytes += count; +#endif + + /* Read the characters out of the buffer */ + while (cp < end) { + if (fp && *fp) + printk(KERN_INFO "%s: %s on serial port\n", + strip_info->dev->name, TTYERROR(*fp)); + if (fp && *fp++ && !strip_info->discard) { /* If there's a serial error, record it */ + /* If we have some characters in the buffer, discard them */ + strip_info->discard = strip_info->sx_count; + strip_info->rx_errors++; + } + + /* Leading control characters (CR, NL, Tab, etc.) are ignored */ + if (strip_info->sx_count > 0 || *cp >= ' ') { + if (*cp == 0x0D) { /* If end of packet, decide what to do with it */ + if (strip_info->sx_count > 3000) + printk(KERN_INFO + "%s: Cut a %d byte packet (%zd bytes remaining)%s\n", + strip_info->dev->name, + strip_info->sx_count, + end - cp - 1, + strip_info-> + discard ? " (discarded)" : + ""); + if (strip_info->sx_count > + strip_info->sx_size) { + strip_info->rx_over_errors++; + printk(KERN_INFO + "%s: sx_buff overflow (%d bytes total)\n", + strip_info->dev->name, + strip_info->sx_count); + } else if (strip_info->discard) + printk(KERN_INFO + "%s: Discarding bad packet (%d/%d)\n", + strip_info->dev->name, + strip_info->discard, + strip_info->sx_count); + else + process_message(strip_info); + strip_info->discard = 0; + strip_info->sx_count = 0; + } else { + /* Make sure we have space in the buffer */ + if (strip_info->sx_count < + strip_info->sx_size) + strip_info->sx_buff[strip_info-> + sx_count] = + *cp; + strip_info->sx_count++; + } + } + cp++; + } + spin_unlock_bh(&strip_lock); +} + + +/************************************************************************/ +/* General control routines */ + +static int set_mac_address(struct strip *strip_info, + MetricomAddress * addr) +{ + /* + * We're using a manually specified address if the address is set + * to anything other than all ones. Setting the address to all ones + * disables manual mode and goes back to automatic address determination + * (tracking the true address that the radio has). + */ + strip_info->manual_dev_addr = + memcmp(addr->c, broadcast_address.c, + sizeof(broadcast_address)); + if (strip_info->manual_dev_addr) + *(MetricomAddress *) strip_info->dev->dev_addr = *addr; + else + *(MetricomAddress *) strip_info->dev->dev_addr = + strip_info->true_dev_addr; + return 0; +} + +static int strip_set_mac_address(struct net_device *dev, void *addr) +{ + struct strip *strip_info = netdev_priv(dev); + struct sockaddr *sa = addr; + printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name); + set_mac_address(strip_info, (MetricomAddress *) sa->sa_data); + return 0; +} + +static struct net_device_stats *strip_get_stats(struct net_device *dev) +{ + struct strip *strip_info = netdev_priv(dev); + static struct net_device_stats stats; + + memset(&stats, 0, sizeof(struct net_device_stats)); + + stats.rx_packets = strip_info->rx_packets; + stats.tx_packets = strip_info->tx_packets; + stats.rx_dropped = strip_info->rx_dropped; + stats.tx_dropped = strip_info->tx_dropped; + stats.tx_errors = strip_info->tx_errors; + stats.rx_errors = strip_info->rx_errors; + stats.rx_over_errors = strip_info->rx_over_errors; + return (&stats); +} + + +/************************************************************************/ +/* Opening and closing */ + +/* + * Here's the order things happen: + * When the user runs "slattach -p strip ..." + * 1. The TTY module calls strip_open;; + * 2. strip_open calls strip_alloc + * 3. strip_alloc calls register_netdev + * 4. register_netdev calls strip_dev_init + * 5. then strip_open finishes setting up the strip_info + * + * When the user runs "ifconfig st up address netmask ..." + * 6. strip_open_low gets called + * + * When the user runs "ifconfig st down" + * 7. strip_close_low gets called + * + * When the user kills the slattach process + * 8. strip_close gets called + * 9. strip_close calls dev_close + * 10. if the device is still up, then dev_close calls strip_close_low + * 11. strip_close calls strip_free + */ + +/* Open the low-level part of the STRIP channel. Easy! */ + +static int strip_open_low(struct net_device *dev) +{ + struct strip *strip_info = netdev_priv(dev); + + if (strip_info->tty == NULL) + return (-ENODEV); + + if (!allocate_buffers(strip_info, dev->mtu)) + return (-ENOMEM); + + strip_info->sx_count = 0; + strip_info->tx_left = 0; + + strip_info->discard = 0; + strip_info->working = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; + strip_info->user_baud = tty_get_baud_rate(strip_info->tty); + + printk(KERN_INFO "%s: Initializing Radio.\n", + strip_info->dev->name); + ResetRadio(strip_info); + strip_info->idle_timer.expires = jiffies + 1 * HZ; + add_timer(&strip_info->idle_timer); + netif_wake_queue(dev); + return (0); +} + + +/* + * Close the low-level part of the STRIP channel. Easy! + */ + +static int strip_close_low(struct net_device *dev) +{ + struct strip *strip_info = netdev_priv(dev); + + if (strip_info->tty == NULL) + return -EBUSY; + clear_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags); + netif_stop_queue(dev); + + /* + * Free all STRIP frame buffers. + */ + kfree(strip_info->rx_buff); + strip_info->rx_buff = NULL; + kfree(strip_info->sx_buff); + strip_info->sx_buff = NULL; + kfree(strip_info->tx_buff); + strip_info->tx_buff = NULL; + + del_timer(&strip_info->idle_timer); + return 0; +} + +static const struct header_ops strip_header_ops = { + .create = strip_header, + .rebuild = strip_rebuild_header, +}; + + +static const struct net_device_ops strip_netdev_ops = { + .ndo_open = strip_open_low, + .ndo_stop = strip_close_low, + .ndo_start_xmit = strip_xmit, + .ndo_set_mac_address = strip_set_mac_address, + .ndo_get_stats = strip_get_stats, + .ndo_change_mtu = strip_change_mtu, +}; + +/* + * This routine is called by DDI when the + * (dynamically assigned) device is registered + */ + +static void strip_dev_setup(struct net_device *dev) +{ + /* + * Finish setting up the DEVICE info. + */ + + dev->trans_start = 0; + dev->tx_queue_len = 30; /* Drop after 30 frames queued */ + + dev->flags = 0; + dev->mtu = DEFAULT_STRIP_MTU; + dev->type = ARPHRD_METRICOM; /* dtang */ + dev->hard_header_len = sizeof(STRIP_Header); + /* + * netdev_priv(dev) Already holds a pointer to our struct strip + */ + + *(MetricomAddress *)dev->broadcast = broadcast_address; + dev->dev_addr[0] = 0; + dev->addr_len = sizeof(MetricomAddress); + + dev->header_ops = &strip_header_ops, + dev->netdev_ops = &strip_netdev_ops; +} + +/* + * Free a STRIP channel. + */ + +static void strip_free(struct strip *strip_info) +{ + spin_lock_bh(&strip_lock); + list_del_rcu(&strip_info->list); + spin_unlock_bh(&strip_lock); + + strip_info->magic = 0; + + free_netdev(strip_info->dev); +} + + +/* + * Allocate a new free STRIP channel + */ +static struct strip *strip_alloc(void) +{ + struct list_head *n; + struct net_device *dev; + struct strip *strip_info; + + dev = alloc_netdev(sizeof(struct strip), "st%d", + strip_dev_setup); + + if (!dev) + return NULL; /* If no more memory, return */ + + + strip_info = netdev_priv(dev); + strip_info->dev = dev; + + strip_info->magic = STRIP_MAGIC; + strip_info->tty = NULL; + + strip_info->gratuitous_arp = jiffies + LongTime; + strip_info->arp_interval = 0; + init_timer(&strip_info->idle_timer); + strip_info->idle_timer.data = (long) dev; + strip_info->idle_timer.function = strip_IdleTask; + + + spin_lock_bh(&strip_lock); + rescan: + /* + * Search the list to find where to put our new entry + * (and in the process decide what channel number it is + * going to be) + */ + list_for_each(n, &strip_list) { + struct strip *s = hlist_entry(n, struct strip, list); + + if (s->dev->base_addr == dev->base_addr) { + ++dev->base_addr; + goto rescan; + } + } + + sprintf(dev->name, "st%ld", dev->base_addr); + + list_add_tail_rcu(&strip_info->list, &strip_list); + spin_unlock_bh(&strip_lock); + + return strip_info; +} + +/* + * Open the high-level part of the STRIP channel. + * This function is called by the TTY module when the + * STRIP line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free STRIP channel... + */ + +static int strip_open(struct tty_struct *tty) +{ + struct strip *strip_info = tty->disc_data; + + /* + * First make sure we're not already connected. + */ + + if (strip_info && strip_info->magic == STRIP_MAGIC) + return -EEXIST; + + /* + * We need a write method. + */ + + if (tty->ops->write == NULL || tty->ops->set_termios == NULL) + return -EOPNOTSUPP; + + /* + * OK. Find a free STRIP channel to use. + */ + if ((strip_info = strip_alloc()) == NULL) + return -ENFILE; + + /* + * Register our newly created device so it can be ifconfig'd + * strip_dev_init() will be called as a side-effect + */ + + if (register_netdev(strip_info->dev) != 0) { + printk(KERN_ERR "strip: register_netdev() failed.\n"); + strip_free(strip_info); + return -ENFILE; + } + + strip_info->tty = tty; + tty->disc_data = strip_info; + tty->receive_room = 65536; + + tty_driver_flush_buffer(tty); + + /* + * Restore default settings + */ + + strip_info->dev->type = ARPHRD_METRICOM; /* dtang */ + + /* + * Set tty options + */ + + tty->termios->c_iflag |= IGNBRK | IGNPAR; /* Ignore breaks and parity errors. */ + tty->termios->c_cflag |= CLOCAL; /* Ignore modem control signals. */ + tty->termios->c_cflag &= ~HUPCL; /* Don't close on hup */ + + printk(KERN_INFO "STRIP: device \"%s\" activated\n", + strip_info->dev->name); + + /* + * Done. We have linked the TTY line to a channel. + */ + return (strip_info->dev->base_addr); +} + +/* + * Close down a STRIP channel. + * This means flushing out any pending queues, and then restoring the + * TTY line discipline to what it was before it got hooked to STRIP + * (which usually is TTY again). + */ + +static void strip_close(struct tty_struct *tty) +{ + struct strip *strip_info = tty->disc_data; + + /* + * First make sure we're connected. + */ + + if (!strip_info || strip_info->magic != STRIP_MAGIC) + return; + + unregister_netdev(strip_info->dev); + + tty->disc_data = NULL; + strip_info->tty = NULL; + printk(KERN_INFO "STRIP: device \"%s\" closed down\n", + strip_info->dev->name); + strip_free(strip_info); + tty->disc_data = NULL; +} + + +/************************************************************************/ +/* Perform I/O control calls on an active STRIP channel. */ + +static int strip_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct strip *strip_info = tty->disc_data; + + /* + * First make sure we're connected. + */ + + if (!strip_info || strip_info->magic != STRIP_MAGIC) + return -EINVAL; + + switch (cmd) { + case SIOCGIFNAME: + if(copy_to_user((void __user *) arg, strip_info->dev->name, strlen(strip_info->dev->name) + 1)) + return -EFAULT; + break; + case SIOCSIFHWADDR: + { + MetricomAddress addr; + //printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev->name); + if(copy_from_user(&addr, (void __user *) arg, sizeof(MetricomAddress))) + return -EFAULT; + return set_mac_address(strip_info, &addr); + } + default: + return tty_mode_ioctl(tty, file, cmd, arg); + break; + } + return 0; +} + + +/************************************************************************/ +/* Initialization */ + +static struct tty_ldisc_ops strip_ldisc = { + .magic = TTY_LDISC_MAGIC, + .name = "strip", + .owner = THIS_MODULE, + .open = strip_open, + .close = strip_close, + .ioctl = strip_ioctl, + .receive_buf = strip_receive_buf, + .write_wakeup = strip_write_some_more, +}; + +/* + * Initialize the STRIP driver. + * This routine is called at boot time, to bootstrap the multi-channel + * STRIP driver + */ + +static char signon[] __initdata = + KERN_INFO "STRIP: Version %s (unlimited channels)\n"; + +static int __init strip_init_driver(void) +{ + int status; + + printk(signon, StripVersion); + + + /* + * Fill in our line protocol discipline, and register it + */ + if ((status = tty_register_ldisc(N_STRIP, &strip_ldisc))) + printk(KERN_ERR "STRIP: can't register line discipline (err = %d)\n", + status); + + /* + * Register the status file with /proc + */ + proc_net_fops_create(&init_net, "strip", S_IFREG | S_IRUGO, &strip_seq_fops); + + return status; +} + +module_init(strip_init_driver); + +static const char signoff[] __exitdata = + KERN_INFO "STRIP: Module Unloaded\n"; + +static void __exit strip_exit_driver(void) +{ + int i; + struct list_head *p,*n; + + /* module ref count rules assure that all entries are unregistered */ + list_for_each_safe(p, n, &strip_list) { + struct strip *s = list_entry(p, struct strip, list); + strip_free(s); + } + + /* Unregister with the /proc/net file here. */ + proc_net_remove(&init_net, "strip"); + + if ((i = tty_unregister_ldisc(N_STRIP))) + printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i); + + printk(signoff); +} + +module_exit(strip_exit_driver); + +MODULE_AUTHOR("Stuart Cheshire "); +MODULE_DESCRIPTION("Starmode Radio IP (STRIP) Device Driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +MODULE_SUPPORTED_DEVICE("Starmode Radio IP (STRIP) modem"); -- cgit v1.2.3-59-g8ed1b From e38879efd336fb78b288dcebdc9ca030fd24f449 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 20 Oct 2009 13:53:08 +0900 Subject: arlan: move driver to staging Move the arlan driver to drivers/staging. This is another pre-802.11 driver that has seen virtually no non-API-fixup activity in years, and for which no active hardware is likely to still exist. This driver represents unnecessary ongoing maintenance for no clear benefit. This patch brought to you by the "hacking" session at the 2009 Kernel Summit in Tokyo, Japan... Acked-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 16 - drivers/net/wireless/Makefile | 4 - drivers/net/wireless/arlan-main.c | 1887 ------------------------------------ drivers/net/wireless/arlan-proc.c | 1253 ------------------------ drivers/net/wireless/arlan.h | 539 ---------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/arlan/Kconfig | 15 + drivers/staging/arlan/Makefile | 3 + drivers/staging/arlan/arlan-main.c | 1887 ++++++++++++++++++++++++++++++++++++ drivers/staging/arlan/arlan-proc.c | 1253 ++++++++++++++++++++++++ drivers/staging/arlan/arlan.h | 539 ++++++++++ 12 files changed, 3700 insertions(+), 3699 deletions(-) delete mode 100644 drivers/net/wireless/arlan-main.c delete mode 100644 drivers/net/wireless/arlan-proc.c delete mode 100644 drivers/net/wireless/arlan.h create mode 100644 drivers/staging/arlan/Kconfig create mode 100644 drivers/staging/arlan/Makefile create mode 100644 drivers/staging/arlan/arlan-main.c create mode 100644 drivers/staging/arlan/arlan-proc.c create mode 100644 drivers/staging/arlan/arlan.h (limited to 'drivers/staging') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 5df47486e358..d50b3bee9a9b 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -25,22 +25,6 @@ menuconfig WLAN_PRE80211 This option does not affect the kernel build, it only lets you choose drivers. -config ARLAN - tristate "Aironet Arlan 655 & IC2200 DS support" - depends on ISA && !64BIT && WLAN_PRE80211 - select WIRELESS_EXT - ---help--- - Aironet makes Arlan, a class of wireless LAN adapters. These use the - www.Telxon.com chip, which is also used on several similar cards. - This driver is tested on the 655 and IC2200 series cards. Look at - for the latest information. - - The driver is built as two modules, arlan and arlan-proc. The latter - is the /proc interface and is not needed most of time. - - On some computers the card ends up in non-valid state after some - time. Use a ping-reset script to clear it. - config WAVELAN tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" depends on ISA && WLAN_PRE80211 diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 527c272aa1af..b56c70f4ca75 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -5,10 +5,6 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ -obj-$(CONFIG_ARLAN) += arlan.o - -arlan-objs := arlan-main.o arlan-proc.o - # Obsolete cards obj-$(CONFIG_WAVELAN) += wavelan.o obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c deleted file mode 100644 index 921a082487a1..000000000000 --- a/drivers/net/wireless/arlan-main.c +++ /dev/null @@ -1,1887 +0,0 @@ -/* - * Copyright (C) 1997 Cullen Jennings - * Copyright (C) 1998 Elmer Joandiu, elmer@ylenurme.ee - * GNU General Public License applies - * This module provides support for the Arlan 655 card made by Aironet - */ - -#include "arlan.h" - -#if BITS_PER_LONG != 32 -# error FIXME: this driver requires a 32-bit platform -#endif - -static const char *arlan_version = "C.Jennigs 97 & Elmer.Joandi@ut.ee Oct'98, http://www.ylenurme.ee/~elmer/655/"; - -struct net_device *arlan_device[MAX_ARLANS]; - -static int SID = SIDUNKNOWN; -static int radioNodeId = radioNodeIdUNKNOWN; -static char encryptionKey[12] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; -int arlan_debug = debugUNKNOWN; -static int spreadingCode = spreadingCodeUNKNOWN; -static int channelNumber = channelNumberUNKNOWN; -static int channelSet = channelSetUNKNOWN; -static int systemId = systemIdUNKNOWN; -static int registrationMode = registrationModeUNKNOWN; -static int keyStart; -static int tx_delay_ms; -static int retries = 5; -static int tx_queue_len = 1; -static int arlan_EEPROM_bad; - -#ifdef ARLAN_DEBUGGING - -static int testMemory = testMemoryUNKNOWN; -static int irq = irqUNKNOWN; -static int txScrambled = 1; -static int mdebug; - -module_param(irq, int, 0); -module_param(mdebug, int, 0); -module_param(testMemory, int, 0); -module_param(txScrambled, int, 0); -MODULE_PARM_DESC(irq, "(unused)"); -MODULE_PARM_DESC(testMemory, "(unused)"); -MODULE_PARM_DESC(mdebug, "Arlan multicast debugging (0-1)"); -#endif - -module_param_named(debug, arlan_debug, int, 0); -module_param(spreadingCode, int, 0); -module_param(channelNumber, int, 0); -module_param(channelSet, int, 0); -module_param(systemId, int, 0); -module_param(registrationMode, int, 0); -module_param(radioNodeId, int, 0); -module_param(SID, int, 0); -module_param(keyStart, int, 0); -module_param(tx_delay_ms, int, 0); -module_param(retries, int, 0); -module_param(tx_queue_len, int, 0); -module_param_named(EEPROM_bad, arlan_EEPROM_bad, int, 0); -MODULE_PARM_DESC(debug, "Arlan debug enable (0-1)"); -MODULE_PARM_DESC(retries, "Arlan maximum packet retransmisions"); -#ifdef ARLAN_ENTRY_EXIT_DEBUGGING -static int arlan_entry_debug; -static int arlan_exit_debug; -static int arlan_entry_and_exit_debug; -module_param_named(entry_debug, arlan_entry_debug, int, 0); -module_param_named(exit_debug, arlan_exit_debug, int, 0); -module_param_named(entry_and_exit_debug, arlan_entry_and_exit_debug, int, 0); -MODULE_PARM_DESC(entry_debug, "Arlan driver function entry debugging"); -MODULE_PARM_DESC(exit_debug, "Arlan driver function exit debugging"); -MODULE_PARM_DESC(entry_and_exit_debug, "Arlan driver function entry and exit debugging"); -#endif - -struct arlan_conf_stru arlan_conf[MAX_ARLANS]; -static int arlans_found; - -static int arlan_open(struct net_device *dev); -static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t arlan_interrupt(int irq, void *dev_id); -static int arlan_close(struct net_device *dev); -static struct net_device_stats * - arlan_statistics (struct net_device *dev); -static void arlan_set_multicast (struct net_device *dev); -static int arlan_hw_tx (struct net_device* dev, char *buf, int length ); -static int arlan_hw_config (struct net_device * dev); -static void arlan_tx_done_interrupt (struct net_device * dev, int status); -static void arlan_rx_interrupt (struct net_device * dev, u_char rxStatus, u_short, u_short); -static void arlan_process_interrupt (struct net_device * dev); -static void arlan_tx_timeout (struct net_device *dev); - -static inline long us2ticks(int us) -{ - return us * (1000000 / HZ); -} - - -#ifdef ARLAN_ENTRY_EXIT_DEBUGGING -#define ARLAN_DEBUG_ENTRY(name) \ - {\ - struct timeval timev;\ - do_gettimeofday(&timev);\ - if (arlan_entry_debug || arlan_entry_and_exit_debug)\ - printk("--->>>" name " %ld " "\n",((long int) timev.tv_sec * 1000000 + timev.tv_usec));\ - } -#define ARLAN_DEBUG_EXIT(name) \ - {\ - struct timeval timev;\ - do_gettimeofday(&timev);\ - if (arlan_exit_debug || arlan_entry_and_exit_debug)\ - printk("<<<---" name " %ld " "\n",((long int) timev.tv_sec * 1000000 + timev.tv_usec) );\ - } -#else -#define ARLAN_DEBUG_ENTRY(name) -#define ARLAN_DEBUG_EXIT(name) -#endif - - -#define arlan_interrupt_ack(dev)\ - clearClearInterrupt(dev);\ - setClearInterrupt(dev); - -static inline int arlan_drop_tx(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - dev->stats.tx_errors++; - if (priv->Conf->tx_delay_ms) - { - priv->tx_done_delayed = jiffies + priv->Conf->tx_delay_ms * HZ / 1000 + 1; - } - else - { - priv->waiting_command_mask &= ~ARLAN_COMMAND_TX; - TXHEAD(dev).offset = 0; - TXTAIL(dev).offset = 0; - priv->txLast = 0; - priv->bad = 0; - if (!priv->under_reset && !priv->under_config) - netif_wake_queue (dev); - } - return 1; -} - - -int arlan_command(struct net_device *dev, int command_p) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - int udelayed = 0; - int i = 0; - unsigned long flags; - - ARLAN_DEBUG_ENTRY("arlan_command"); - - if (priv->card_polling_interval) - priv->card_polling_interval = 1; - - if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) - printk(KERN_DEBUG "arlan_command, %lx commandByte %x waiting %lx incoming %x \n", - jiffies, READSHMB(arlan->commandByte), - priv->waiting_command_mask, command_p); - - priv->waiting_command_mask |= command_p; - - if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) - if (time_after(jiffies, priv->lastReset + 5 * HZ)) - priv->waiting_command_mask &= ~ARLAN_COMMAND_RESET; - - if (priv->waiting_command_mask & ARLAN_COMMAND_INT_ACK) - { - arlan_interrupt_ack(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_ACK; - } - if (priv->waiting_command_mask & ARLAN_COMMAND_INT_ENABLE) - { - setInterruptEnable(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_ENABLE; - } - - /* Card access serializing lock */ - spin_lock_irqsave(&priv->lock, flags); - - /* Check cards status and waiting */ - - if (priv->waiting_command_mask & (ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW)) - { - while (priv->waiting_command_mask & (ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW)) - { - if (READSHMB(arlan->resetFlag) || - READSHMB(arlan->commandByte)) /* || - (readControlRegister(dev) & ARLAN_ACCESS)) - */ - udelay(40); - else - priv->waiting_command_mask &= ~(ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW); - - udelayed++; - - if (priv->waiting_command_mask & ARLAN_COMMAND_LONG_WAIT_NOW) - { - if (udelayed * 40 > 1000000) - { - printk(KERN_ERR "%s long wait too long \n", dev->name); - priv->waiting_command_mask |= ARLAN_COMMAND_RESET; - break; - } - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_WAIT_NOW) - { - if (udelayed * 40 > 1000) - { - printk(KERN_ERR "%s short wait too long \n", dev->name); - goto bad_end; - } - } - } - } - else - { - i = 0; - while ((READSHMB(arlan->resetFlag) || - READSHMB(arlan->commandByte)) && - conf->pre_Command_Wait > (i++) * 10) - udelay(10); - - - if ((READSHMB(arlan->resetFlag) || - READSHMB(arlan->commandByte)) && - !(priv->waiting_command_mask & ARLAN_COMMAND_RESET)) - { - goto card_busy_end; - } - } - if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) - priv->under_reset = 1; - if (priv->waiting_command_mask & ARLAN_COMMAND_CONF) - priv->under_config = 1; - - /* Issuing command */ - arlan_lock_card_access(dev); - if (priv->waiting_command_mask & ARLAN_COMMAND_POWERUP) - { - // if (readControlRegister(dev) & (ARLAN_ACCESS && ARLAN_POWER)) - setPowerOn(dev); - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_POWERUP; - priv->waiting_command_mask |= ARLAN_COMMAND_RESET; - priv->card_polling_interval = HZ / 10; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_ACTIVATE) - { - WRITESHMB(arlan->commandByte, ARLAN_COM_ACTIVATE); - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_ACTIVATE; - priv->card_polling_interval = HZ / 10; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_RX_ABORT) - { - if (priv->rx_command_given) - { - WRITESHMB(arlan->commandByte, ARLAN_COM_RX_ABORT); - arlan_interrupt_lancpu(dev); - priv->rx_command_given = 0; - } - priv->waiting_command_mask &= ~ARLAN_COMMAND_RX_ABORT; - priv->card_polling_interval = 1; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_TX_ABORT) - { - if (priv->tx_command_given) - { - WRITESHMB(arlan->commandByte, ARLAN_COM_TX_ABORT); - arlan_interrupt_lancpu(dev); - priv->tx_command_given = 0; - } - priv->waiting_command_mask &= ~ARLAN_COMMAND_TX_ABORT; - priv->card_polling_interval = 1; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) - { - priv->under_reset=1; - netif_stop_queue (dev); - - arlan_drop_tx(dev); - if (priv->tx_command_given || priv->rx_command_given) - { - printk(KERN_ERR "%s: Reset under tx or rx command \n", dev->name); - } - netif_stop_queue (dev); - if (arlan_debug & ARLAN_DEBUG_RESET) - printk(KERN_ERR "%s: Doing chip reset\n", dev->name); - priv->lastReset = jiffies; - WRITESHM(arlan->commandByte, 0, u_char); - /* hold card in reset state */ - setHardwareReset(dev); - /* set reset flag and then release reset */ - WRITESHM(arlan->resetFlag, 0xff, u_char); - clearChannelAttention(dev); - clearHardwareReset(dev); - priv->card_polling_interval = HZ / 4; - priv->waiting_command_mask &= ~ARLAN_COMMAND_RESET; - priv->waiting_command_mask |= ARLAN_COMMAND_INT_RACK; -// priv->waiting_command_mask |= ARLAN_COMMAND_INT_RENABLE; -// priv->waiting_command_mask |= ARLAN_COMMAND_RX; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_INT_RACK) - { - clearHardwareReset(dev); - clearClearInterrupt(dev); - setClearInterrupt(dev); - setInterruptEnable(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_RACK; - priv->waiting_command_mask |= ARLAN_COMMAND_CONF; - priv->under_config = 1; - priv->under_reset = 0; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_INT_RENABLE) - { - setInterruptEnable(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_RENABLE; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_CONF) - { - if (priv->tx_command_given || priv->rx_command_given) - { - printk(KERN_ERR "%s: Reset under tx or rx command \n", dev->name); - } - arlan_drop_tx(dev); - setInterruptEnable(dev); - arlan_hw_config(dev); - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_CONF; - priv->card_polling_interval = HZ / 10; -// priv->waiting_command_mask |= ARLAN_COMMAND_INT_RACK; -// priv->waiting_command_mask |= ARLAN_COMMAND_INT_ENABLE; - priv->waiting_command_mask |= ARLAN_COMMAND_CONF_WAIT; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_CONF_WAIT) - { - if (READSHMB(arlan->configuredStatusFlag) != 0 && - READSHMB(arlan->diagnosticInfo) == 0xff) - { - priv->waiting_command_mask &= ~ARLAN_COMMAND_CONF_WAIT; - priv->waiting_command_mask |= ARLAN_COMMAND_RX; - priv->waiting_command_mask |= ARLAN_COMMAND_TBUSY_CLEAR; - priv->card_polling_interval = HZ / 10; - priv->tx_command_given = 0; - priv->under_config = 0; - } - else - { - priv->card_polling_interval = 1; - if (arlan_debug & ARLAN_DEBUG_TIMING) - printk(KERN_ERR "configure delayed \n"); - } - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_RX) - { - if (!registrationBad(dev)) - { - setInterruptEnable(dev); - memset_io(arlan->commandParameter, 0, 0xf); - WRITESHMB(arlan->commandByte, ARLAN_COM_INT | ARLAN_COM_RX_ENABLE); - WRITESHMB(arlan->commandParameter[0], conf->rxParameter); - arlan_interrupt_lancpu(dev); - priv->rx_command_given = 0; // mnjah, bad - priv->waiting_command_mask &= ~ARLAN_COMMAND_RX; - priv->card_polling_interval = 1; - } - else - priv->card_polling_interval = 2; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_TBUSY_CLEAR) - { - if ( !registrationBad(dev) && - (netif_queue_stopped(dev) || !netif_running(dev)) ) - { - priv->waiting_command_mask &= ~ARLAN_COMMAND_TBUSY_CLEAR; - netif_wake_queue (dev); - } - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_TX) - { - if (!test_and_set_bit(0, (void *) &priv->tx_command_given)) - { - if (time_after(jiffies, - priv->tx_last_sent + us2ticks(conf->rx_tweak1)) - || time_before(jiffies, - priv->last_rx_int_ack_time + us2ticks(conf->rx_tweak2))) - { - setInterruptEnable(dev); - memset_io(arlan->commandParameter, 0, 0xf); - WRITESHMB(arlan->commandByte, ARLAN_COM_TX_ENABLE | ARLAN_COM_INT); - memcpy_toio(arlan->commandParameter, &TXLAST(dev), 14); -// for ( i=1 ; i < 15 ; i++) printk("%02x:",READSHMB(arlan->commandParameter[i])); - priv->tx_last_sent = jiffies; - arlan_interrupt_lancpu(dev); - priv->tx_command_given = 1; - priv->waiting_command_mask &= ~ARLAN_COMMAND_TX; - priv->card_polling_interval = 1; - } - else - { - priv->tx_command_given = 0; - priv->card_polling_interval = 1; - } - } - else if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) - printk(KERN_ERR "tx command when tx chain locked \n"); - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_NOOPINT) - { - { - WRITESHMB(arlan->commandByte, ARLAN_COM_NOP | ARLAN_COM_INT); - } - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_NOOPINT; - priv->card_polling_interval = HZ / 3; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_NOOP) - { - WRITESHMB(arlan->commandByte, ARLAN_COM_NOP); - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_NOOP; - priv->card_polling_interval = HZ / 3; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_SLOW_POLL) - { - WRITESHMB(arlan->commandByte, ARLAN_COM_GOTO_SLOW_POLL); - arlan_interrupt_lancpu(dev); - priv->waiting_command_mask &= ~ARLAN_COMMAND_SLOW_POLL; - priv->card_polling_interval = HZ / 3; - } - else if (priv->waiting_command_mask & ARLAN_COMMAND_POWERDOWN) - { - setPowerOff(dev); - if (arlan_debug & ARLAN_DEBUG_CARD_STATE) - printk(KERN_WARNING "%s: Arlan Going Standby\n", dev->name); - priv->waiting_command_mask &= ~ARLAN_COMMAND_POWERDOWN; - priv->card_polling_interval = 3 * HZ; - } - arlan_unlock_card_access(dev); - for (i = 0; READSHMB(arlan->commandByte) && i < 20; i++) - udelay(10); - if (READSHMB(arlan->commandByte)) - if (arlan_debug & ARLAN_DEBUG_CARD_STATE) - printk(KERN_ERR "card busy leaving command %lx\n", priv->waiting_command_mask); - - spin_unlock_irqrestore(&priv->lock, flags); - ARLAN_DEBUG_EXIT("arlan_command"); - priv->last_command_buff_free_time = jiffies; - return 0; - -card_busy_end: - if (time_after(jiffies, priv->last_command_buff_free_time + HZ)) - priv->waiting_command_mask |= ARLAN_COMMAND_CLEAN_AND_RESET; - - if (arlan_debug & ARLAN_DEBUG_CARD_STATE) - printk(KERN_ERR "%s arlan_command card busy end \n", dev->name); - spin_unlock_irqrestore(&priv->lock, flags); - ARLAN_DEBUG_EXIT("arlan_command"); - return 1; - -bad_end: - printk(KERN_ERR "%s arlan_command bad end \n", dev->name); - - spin_unlock_irqrestore(&priv->lock, flags); - ARLAN_DEBUG_EXIT("arlan_command"); - - return -1; -} - -static inline void arlan_command_process(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - int times = 0; - while (priv->waiting_command_mask && times < 8) - { - if (priv->waiting_command_mask) - { - if (arlan_command(dev, 0)) - break; - times++; - } - /* if long command, we won't repeat trying */ ; - if (priv->card_polling_interval > 1) - break; - times++; - } -} - - -static inline void arlan_retransmit_now(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - - ARLAN_DEBUG_ENTRY("arlan_retransmit_now"); - if (TXLAST(dev).offset == 0) - { - if (TXHEAD(dev).offset) - { - priv->txLast = 0; - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_DEBUG "TX buff switch to head \n"); - - } - else if (TXTAIL(dev).offset) - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_DEBUG "TX buff switch to tail \n"); - priv->txLast = 1; - } - else - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_ERR "ReTransmit buff empty"); - netif_wake_queue (dev); - return; - - } - arlan_command(dev, ARLAN_COMMAND_TX); - - priv->Conf->driverRetransmissions++; - priv->retransmissions++; - - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk("Retransmit %d bytes \n", TXLAST(dev).length); - - ARLAN_DEBUG_EXIT("arlan_retransmit_now"); -} - - - -static void arlan_registration_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *) data; - struct arlan_private *priv = netdev_priv(dev); - int bh_mark_needed = 0; - int next_tick = 1; - long lostTime = ((long)jiffies - (long)priv->registrationLastSeen) - * (1000/HZ); - - if (registrationBad(dev)) - { - priv->registrationLostCount++; - if (lostTime > 7000 && lostTime < 7200) - { - printk(KERN_NOTICE "%s registration Lost \n", dev->name); - } - if (lostTime / priv->reRegisterExp > 2000) - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_CONF); - if (lostTime / (priv->reRegisterExp) > 3500) - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); - if (priv->reRegisterExp < 400) - priv->reRegisterExp += 2; - if (lostTime > 7200) - { - next_tick = HZ; - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); - } - } - else - { - if (priv->Conf->registrationMode && lostTime > 10000 && - priv->registrationLostCount) - { - printk(KERN_NOTICE "%s registration is back after %ld milliseconds\n", - dev->name, lostTime); - } - priv->registrationLastSeen = jiffies; - priv->registrationLostCount = 0; - priv->reRegisterExp = 1; - if (!netif_running(dev) ) - netif_wake_queue(dev); - if (time_after(priv->tx_last_sent,priv->tx_last_cleared) && - time_after(jiffies, priv->tx_last_sent * 5*HZ) ){ - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); - priv->tx_last_cleared = jiffies; - } - } - - - if (!registrationBad(dev) && priv->ReTransmitRequested) - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk(KERN_ERR "Retransmit from timer \n"); - priv->ReTransmitRequested = 0; - arlan_retransmit_now(dev); - } - if (!registrationBad(dev) && - time_after(jiffies, priv->tx_done_delayed) && - priv->tx_done_delayed != 0) - { - TXLAST(dev).offset = 0; - if (priv->txLast) - priv->txLast = 0; - else if (TXTAIL(dev).offset) - priv->txLast = 1; - if (TXLAST(dev).offset) - { - arlan_retransmit_now(dev); - dev->trans_start = jiffies; - } - if (!(TXHEAD(dev).offset && TXTAIL(dev).offset)) - { - netif_wake_queue (dev); - } - priv->tx_done_delayed = 0; - bh_mark_needed = 1; - } - if (bh_mark_needed) - { - netif_wake_queue (dev); - } - arlan_process_interrupt(dev); - - if (next_tick < priv->card_polling_interval) - next_tick = priv->card_polling_interval; - - priv->timer.expires = jiffies + next_tick; - - add_timer(&priv->timer); -} - - -#ifdef ARLAN_DEBUGGING - -static void arlan_print_registers(struct net_device *dev, int line) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem *arlan = priv->card; - - u_char hostcpuLock, lancpuLock, controlRegister, cntrlRegImage, - txStatus, rxStatus, interruptInProgress, commandByte; - - - ARLAN_DEBUG_ENTRY("arlan_print_registers"); - READSHM(interruptInProgress, arlan->interruptInProgress, u_char); - READSHM(hostcpuLock, arlan->hostcpuLock, u_char); - READSHM(lancpuLock, arlan->lancpuLock, u_char); - READSHM(controlRegister, arlan->controlRegister, u_char); - READSHM(cntrlRegImage, arlan->cntrlRegImage, u_char); - READSHM(txStatus, arlan->txStatus, u_char); - READSHM(rxStatus, arlan->rxStatus, u_char); - READSHM(commandByte, arlan->commandByte, u_char); - - printk(KERN_WARNING "line %04d IP %02x HL %02x LL %02x CB %02x CR %02x CRI %02x TX %02x RX %02x\n", - line, interruptInProgress, hostcpuLock, lancpuLock, commandByte, - controlRegister, cntrlRegImage, txStatus, rxStatus); - - ARLAN_DEBUG_EXIT("arlan_print_registers"); -} -#endif - - -static int arlan_hw_tx(struct net_device *dev, char *buf, int length) -{ - int i; - - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - - int tailStarts = 0x800; - int headEnds = 0x0; - - - ARLAN_DEBUG_ENTRY("arlan_hw_tx"); - if (TXHEAD(dev).offset) - headEnds = (((TXHEAD(dev).offset + TXHEAD(dev).length - offsetof(struct arlan_shmem, txBuffer)) / 64) + 1) * 64; - if (TXTAIL(dev).offset) - tailStarts = 0x800 - (((TXTAIL(dev).offset - offsetof(struct arlan_shmem, txBuffer)) / 64) + 2) * 64; - - - if (!TXHEAD(dev).offset && length < tailStarts) - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk(KERN_ERR "TXHEAD insert, tailStart %d\n", tailStarts); - - TXHEAD(dev).offset = - offsetof(struct arlan_shmem, txBuffer); - TXHEAD(dev).length = length - ARLAN_FAKE_HDR_LEN; - for (i = 0; i < 6; i++) - TXHEAD(dev).dest[i] = buf[i]; - TXHEAD(dev).clear = conf->txClear; - TXHEAD(dev).retries = conf->txRetries; /* 0 is use default */ - TXHEAD(dev).routing = conf->txRouting; - TXHEAD(dev).scrambled = conf->txScrambled; - memcpy_toio((char __iomem *)arlan + TXHEAD(dev).offset, buf + ARLAN_FAKE_HDR_LEN, TXHEAD(dev).length); - } - else if (!TXTAIL(dev).offset && length < (0x800 - headEnds)) - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk(KERN_ERR "TXTAIL insert, headEnd %d\n", headEnds); - - TXTAIL(dev).offset = - offsetof(struct arlan_shmem, txBuffer) + 0x800 - (length / 64 + 2) * 64; - TXTAIL(dev).length = length - ARLAN_FAKE_HDR_LEN; - for (i = 0; i < 6; i++) - TXTAIL(dev).dest[i] = buf[i]; - TXTAIL(dev).clear = conf->txClear; - TXTAIL(dev).retries = conf->txRetries; - TXTAIL(dev).routing = conf->txRouting; - TXTAIL(dev).scrambled = conf->txScrambled; - memcpy_toio(((char __iomem *)arlan + TXTAIL(dev).offset), buf + ARLAN_FAKE_HDR_LEN, TXTAIL(dev).length); - } - else - { - netif_stop_queue (dev); - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk(KERN_ERR "TX TAIL & HEAD full, return, tailStart %d headEnd %d\n", tailStarts, headEnds); - return -1; - } - priv->out_bytes += length; - priv->out_bytes10 += length; - if (conf->measure_rate < 1) - conf->measure_rate = 1; - if (time_after(jiffies, priv->out_time + conf->measure_rate * HZ)) - { - conf->out_speed = priv->out_bytes / conf->measure_rate; - priv->out_bytes = 0; - priv->out_time = jiffies; - } - if (time_after(jiffies, priv->out_time10 + conf->measure_rate * 10*HZ)) - { - conf->out_speed10 = priv->out_bytes10 / (10 * conf->measure_rate); - priv->out_bytes10 = 0; - priv->out_time10 = jiffies; - } - if (TXHEAD(dev).offset && TXTAIL(dev).offset) - { - netif_stop_queue (dev); - return 0; - } - else - netif_start_queue (dev); - - - IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) - printk(KERN_WARNING "%s Transmit t %2x:%2x:%2x:%2x:%2x:%2x f %2x:%2x:%2x:%2x:%2x:%2x \n", dev->name, - (unsigned char) buf[0], (unsigned char) buf[1], (unsigned char) buf[2], (unsigned char) buf[3], - (unsigned char) buf[4], (unsigned char) buf[5], (unsigned char) buf[6], (unsigned char) buf[7], - (unsigned char) buf[8], (unsigned char) buf[9], (unsigned char) buf[10], (unsigned char) buf[11]); - - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_ERR "TX command prepare for buffer %d\n", priv->txLast); - - arlan_command(dev, ARLAN_COMMAND_TX); - - priv->tx_last_sent = jiffies; - - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk("%s TX Qued %d bytes \n", dev->name, length); - - ARLAN_DEBUG_EXIT("arlan_hw_tx"); - - return 0; -} - - -static int arlan_hw_config(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - - ARLAN_DEBUG_ENTRY("arlan_hw_config"); - - printk(KERN_NOTICE "%s arlan configure called \n", dev->name); - if (arlan_EEPROM_bad) - printk(KERN_NOTICE "arlan configure with eeprom bad option \n"); - - - WRITESHM(arlan->spreadingCode, conf->spreadingCode, u_char); - WRITESHM(arlan->channelSet, conf->channelSet, u_char); - - if (arlan_EEPROM_bad) - WRITESHM(arlan->defaultChannelSet, conf->channelSet, u_char); - - WRITESHM(arlan->channelNumber, conf->channelNumber, u_char); - - WRITESHM(arlan->scramblingDisable, conf->scramblingDisable, u_char); - WRITESHM(arlan->txAttenuation, conf->txAttenuation, u_char); - - WRITESHM(arlan->systemId, conf->systemId, u_int); - - WRITESHM(arlan->maxRetries, conf->maxRetries, u_char); - WRITESHM(arlan->receiveMode, conf->receiveMode, u_char); - WRITESHM(arlan->priority, conf->priority, u_char); - WRITESHM(arlan->rootOrRepeater, conf->rootOrRepeater, u_char); - WRITESHM(arlan->SID, conf->SID, u_int); - - WRITESHM(arlan->registrationMode, conf->registrationMode, u_char); - - WRITESHM(arlan->registrationFill, conf->registrationFill, u_char); - WRITESHM(arlan->localTalkAddress, conf->localTalkAddress, u_char); - WRITESHM(arlan->codeFormat, conf->codeFormat, u_char); - WRITESHM(arlan->numChannels, conf->numChannels, u_char); - WRITESHM(arlan->channel1, conf->channel1, u_char); - WRITESHM(arlan->channel2, conf->channel2, u_char); - WRITESHM(arlan->channel3, conf->channel3, u_char); - WRITESHM(arlan->channel4, conf->channel4, u_char); - WRITESHM(arlan->radioNodeId, conf->radioNodeId, u_short); - WRITESHM(arlan->SID, conf->SID, u_int); - WRITESHM(arlan->waitTime, conf->waitTime, u_short); - WRITESHM(arlan->lParameter, conf->lParameter, u_short); - memcpy_toio(&(arlan->_15), &(conf->_15), 3); - WRITESHM(arlan->_15, conf->_15, u_short); - WRITESHM(arlan->headerSize, conf->headerSize, u_short); - if (arlan_EEPROM_bad) - WRITESHM(arlan->hardwareType, conf->hardwareType, u_char); - WRITESHM(arlan->radioType, conf->radioType, u_char); - if (arlan_EEPROM_bad) - WRITESHM(arlan->radioModule, conf->radioType, u_char); - - memcpy_toio(arlan->encryptionKey + keyStart, encryptionKey, 8); - memcpy_toio(arlan->name, conf->siteName, 16); - - WRITESHMB(arlan->commandByte, ARLAN_COM_INT | ARLAN_COM_CONF); /* do configure */ - memset_io(arlan->commandParameter, 0, 0xf); /* 0xf */ - memset_io(arlan->commandParameter + 1, 0, 2); - if (conf->writeEEPROM) - { - memset_io(arlan->commandParameter, conf->writeEEPROM, 1); -// conf->writeEEPROM=0; - } - if (conf->registrationMode && conf->registrationInterrupts) - memset_io(arlan->commandParameter + 3, 1, 1); - else - memset_io(arlan->commandParameter + 3, 0, 1); - - priv->irq_test_done = 0; - - if (conf->tx_queue_len) - dev->tx_queue_len = conf->tx_queue_len; - udelay(100); - - ARLAN_DEBUG_EXIT("arlan_hw_config"); - return 0; -} - - -static int arlan_read_card_configuration(struct net_device *dev) -{ - u_char tlx415; - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - - ARLAN_DEBUG_ENTRY("arlan_read_card_configuration"); - - if (radioNodeId == radioNodeIdUNKNOWN) - { - READSHM(conf->radioNodeId, arlan->radioNodeId, u_short); - } - else - conf->radioNodeId = radioNodeId; - - if (SID == SIDUNKNOWN) - { - READSHM(conf->SID, arlan->SID, u_int); - } - else conf->SID = SID; - - if (spreadingCode == spreadingCodeUNKNOWN) - { - READSHM(conf->spreadingCode, arlan->spreadingCode, u_char); - } - else - conf->spreadingCode = spreadingCode; - - if (channelSet == channelSetUNKNOWN) - { - READSHM(conf->channelSet, arlan->channelSet, u_char); - } - else conf->channelSet = channelSet; - - if (channelNumber == channelNumberUNKNOWN) - { - READSHM(conf->channelNumber, arlan->channelNumber, u_char); - } - else conf->channelNumber = channelNumber; - - READSHM(conf->scramblingDisable, arlan->scramblingDisable, u_char); - READSHM(conf->txAttenuation, arlan->txAttenuation, u_char); - - if (systemId == systemIdUNKNOWN) - { - READSHM(conf->systemId, arlan->systemId, u_int); - } - else conf->systemId = systemId; - - READSHM(conf->maxDatagramSize, arlan->maxDatagramSize, u_short); - READSHM(conf->maxFrameSize, arlan->maxFrameSize, u_short); - READSHM(conf->maxRetries, arlan->maxRetries, u_char); - READSHM(conf->receiveMode, arlan->receiveMode, u_char); - READSHM(conf->priority, arlan->priority, u_char); - READSHM(conf->rootOrRepeater, arlan->rootOrRepeater, u_char); - - if (SID == SIDUNKNOWN) - { - READSHM(conf->SID, arlan->SID, u_int); - } - else conf->SID = SID; - - if (registrationMode == registrationModeUNKNOWN) - { - READSHM(conf->registrationMode, arlan->registrationMode, u_char); - } - else conf->registrationMode = registrationMode; - - READSHM(conf->registrationFill, arlan->registrationFill, u_char); - READSHM(conf->localTalkAddress, arlan->localTalkAddress, u_char); - READSHM(conf->codeFormat, arlan->codeFormat, u_char); - READSHM(conf->numChannels, arlan->numChannels, u_char); - READSHM(conf->channel1, arlan->channel1, u_char); - READSHM(conf->channel2, arlan->channel2, u_char); - READSHM(conf->channel3, arlan->channel3, u_char); - READSHM(conf->channel4, arlan->channel4, u_char); - READSHM(conf->waitTime, arlan->waitTime, u_short); - READSHM(conf->lParameter, arlan->lParameter, u_short); - READSHM(conf->_15, arlan->_15, u_short); - READSHM(conf->headerSize, arlan->headerSize, u_short); - READSHM(conf->hardwareType, arlan->hardwareType, u_char); - READSHM(conf->radioType, arlan->radioModule, u_char); - - if (conf->radioType == 0) - conf->radioType = 0xc; - - WRITESHM(arlan->configStatus, 0xA5, u_char); - READSHM(tlx415, arlan->configStatus, u_char); - - if (tlx415 != 0xA5) - printk(KERN_INFO "%s tlx415 chip \n", dev->name); - - conf->txClear = 0; - conf->txRetries = 1; - conf->txRouting = 1; - conf->txScrambled = 0; - conf->rxParameter = 1; - conf->txTimeoutMs = 4000; - conf->waitCardTimeout = 100000; - conf->receiveMode = ARLAN_RCV_CLEAN; - memcpy_fromio(conf->siteName, arlan->name, 16); - conf->siteName[16] = '\0'; - conf->retries = retries; - conf->tx_delay_ms = tx_delay_ms; - conf->ReTransmitPacketMaxSize = 200; - conf->waitReTransmitPacketMaxSize = 200; - conf->txAckTimeoutMs = 900; - conf->fastReTransCount = 3; - - ARLAN_DEBUG_EXIT("arlan_read_card_configuration"); - - return 0; -} - - -static int lastFoundAt = 0xbe000; - - -/* - * This is the real probe routine. Linux has a history of friendly device - * probes on the ISA bus. A good device probes avoids doing writes, and - * verifies that the correct device exists and functions. - */ -#define ARLAN_SHMEM_SIZE 0x2000 -static int __init arlan_check_fingerprint(unsigned long memaddr) -{ - static const char probeText[] = "TELESYSTEM SLW INC. ARLAN \0"; - volatile struct arlan_shmem __iomem *arlan = (struct arlan_shmem *) memaddr; - unsigned long paddr = virt_to_phys((void *) memaddr); - char tempBuf[49]; - - ARLAN_DEBUG_ENTRY("arlan_check_fingerprint"); - - if (!request_mem_region(paddr, ARLAN_SHMEM_SIZE, "arlan")) { - // printk(KERN_WARNING "arlan: memory region %lx excluded from probing \n",paddr); - return -ENODEV; - } - - memcpy_fromio(tempBuf, arlan->textRegion, 29); - tempBuf[30] = 0; - - /* check for card at this address */ - if (0 != strncmp(tempBuf, probeText, 29)){ - release_mem_region(paddr, ARLAN_SHMEM_SIZE); - return -ENODEV; - } - -// printk(KERN_INFO "arlan found at 0x%x \n",memaddr); - ARLAN_DEBUG_EXIT("arlan_check_fingerprint"); - - return 0; -} - -static int arlan_change_mtu(struct net_device *dev, int new_mtu) -{ - struct arlan_private *priv = netdev_priv(dev); - struct arlan_conf_stru *conf = priv->Conf; - - ARLAN_DEBUG_ENTRY("arlan_change_mtu"); - if (new_mtu > 2032) - return -EINVAL; - dev->mtu = new_mtu; - if (new_mtu < 256) - new_mtu = 256; /* cards book suggests 1600 */ - conf->maxDatagramSize = new_mtu; - conf->maxFrameSize = new_mtu + 48; - - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_CONF); - printk(KERN_NOTICE "%s mtu changed to %d \n", dev->name, new_mtu); - - ARLAN_DEBUG_EXIT("arlan_change_mtu"); - - return 0; -} - -static int arlan_mac_addr(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - - ARLAN_DEBUG_ENTRY("arlan_mac_addr"); - return -EINVAL; - - if (netif_running(dev)) - return -EBUSY; - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - - ARLAN_DEBUG_EXIT("arlan_mac_addr"); - return 0; -} - -static const struct net_device_ops arlan_netdev_ops = { - .ndo_open = arlan_open, - .ndo_stop = arlan_close, - .ndo_start_xmit = arlan_tx, - .ndo_get_stats = arlan_statistics, - .ndo_set_multicast_list = arlan_set_multicast, - .ndo_change_mtu = arlan_change_mtu, - .ndo_set_mac_address = arlan_mac_addr, - .ndo_tx_timeout = arlan_tx_timeout, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init arlan_setup_device(struct net_device *dev, int num) -{ - struct arlan_private *ap = netdev_priv(dev); - int err; - - ARLAN_DEBUG_ENTRY("arlan_setup_device"); - - ap->conf = (struct arlan_shmem *)(ap+1); - - dev->tx_queue_len = tx_queue_len; - dev->netdev_ops = &arlan_netdev_ops; - dev->watchdog_timeo = 3*HZ; - - ap->irq_test_done = 0; - ap->Conf = &arlan_conf[num]; - - ap->Conf->pre_Command_Wait = 40; - ap->Conf->rx_tweak1 = 30; - ap->Conf->rx_tweak2 = 0; - - - err = register_netdev(dev); - if (err) { - release_mem_region(virt_to_phys((void *) dev->mem_start), - ARLAN_SHMEM_SIZE); - free_netdev(dev); - return err; - } - arlan_device[num] = dev; - ARLAN_DEBUG_EXIT("arlan_setup_device"); - return 0; -} - -static int __init arlan_probe_here(struct net_device *dev, - unsigned long memaddr) -{ - struct arlan_private *ap = netdev_priv(dev); - - ARLAN_DEBUG_ENTRY("arlan_probe_here"); - - if (arlan_check_fingerprint(memaddr)) - return -ENODEV; - - printk(KERN_NOTICE "%s: Arlan found at %llx, \n ", dev->name, - (u64) virt_to_phys((void*)memaddr)); - - ap->card = (void *) memaddr; - dev->mem_start = memaddr; - dev->mem_end = memaddr + ARLAN_SHMEM_SIZE-1; - - if (dev->irq < 2) - { - READSHM(dev->irq, ap->card->irqLevel, u_char); - } else if (dev->irq == 2) - dev->irq = 9; - - arlan_read_card_configuration(dev); - - ARLAN_DEBUG_EXIT("arlan_probe_here"); - return 0; -} - - -static int arlan_open(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - int ret = 0; - - ARLAN_DEBUG_ENTRY("arlan_open"); - - ret = request_irq(dev->irq, &arlan_interrupt, 0, dev->name, dev); - if (ret) - { - printk(KERN_ERR "%s: unable to get IRQ %d .\n", - dev->name, dev->irq); - return ret; - } - - - priv->bad = 0; - priv->lastReset = 0; - priv->reset = 0; - memcpy_fromio(dev->dev_addr, arlan->lanCardNodeId, 6); - memset(dev->broadcast, 0xff, 6); - dev->tx_queue_len = tx_queue_len; - priv->interrupt_processing_active = 0; - spin_lock_init(&priv->lock); - - netif_start_queue (dev); - - priv->registrationLostCount = 0; - priv->registrationLastSeen = jiffies; - priv->txLast = 0; - priv->tx_command_given = 0; - priv->rx_command_given = 0; - - priv->reRegisterExp = 1; - priv->tx_last_sent = jiffies - 1; - priv->tx_last_cleared = jiffies; - priv->Conf->writeEEPROM = 0; - priv->Conf->registrationInterrupts = 1; - - init_timer(&priv->timer); - priv->timer.expires = jiffies + HZ / 10; - priv->timer.data = (unsigned long) dev; - priv->timer.function = &arlan_registration_timer; /* timer handler */ - - arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW); - mdelay(200); - add_timer(&priv->timer); - - ARLAN_DEBUG_EXIT("arlan_open"); - return 0; -} - - -static void arlan_tx_timeout (struct net_device *dev) -{ - printk(KERN_ERR "%s: arlan transmit timed out, kernel decided\n", dev->name); - /* Try to restart the adaptor. */ - arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); - // dev->trans_start = jiffies; - // netif_start_queue (dev); -} - - -static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev) -{ - short length; - unsigned char *buf; - - ARLAN_DEBUG_ENTRY("arlan_tx"); - - length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - buf = skb->data; - - if (length + 0x12 > 0x800) { - printk(KERN_ERR "TX RING overflow \n"); - netif_stop_queue (dev); - } - - if (arlan_hw_tx(dev, buf, length) == -1) - goto bad_end; - - dev->trans_start = jiffies; - - dev_kfree_skb(skb); - - arlan_process_interrupt(dev); - ARLAN_DEBUG_EXIT("arlan_tx"); - return NETDEV_TX_OK; - -bad_end: - arlan_process_interrupt(dev); - netif_stop_queue (dev); - ARLAN_DEBUG_EXIT("arlan_tx"); - return NETDEV_TX_BUSY; -} - - -static inline int DoNotReTransmitCrap(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - if (TXLAST(dev).length < priv->Conf->ReTransmitPacketMaxSize) - return 1; - return 0; - -} - -static inline int DoNotWaitReTransmitCrap(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - if (TXLAST(dev).length < priv->Conf->waitReTransmitPacketMaxSize) - return 1; - return 0; -} - -static inline void arlan_queue_retransmit(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - ARLAN_DEBUG_ENTRY("arlan_queue_retransmit"); - - if (DoNotWaitReTransmitCrap(dev)) - { - arlan_drop_tx(dev); - } else - priv->ReTransmitRequested++; - - ARLAN_DEBUG_EXIT("arlan_queue_retransmit"); -} - -static inline void RetryOrFail(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - ARLAN_DEBUG_ENTRY("RetryOrFail"); - - if (priv->retransmissions > priv->Conf->retries || - DoNotReTransmitCrap(dev)) - { - arlan_drop_tx(dev); - } - else if (priv->bad <= priv->Conf->fastReTransCount) - { - arlan_retransmit_now(dev); - } - else arlan_queue_retransmit(dev); - - ARLAN_DEBUG_EXIT("RetryOrFail"); -} - - -static void arlan_tx_done_interrupt(struct net_device *dev, int status) -{ - struct arlan_private *priv = netdev_priv(dev); - - ARLAN_DEBUG_ENTRY("arlan_tx_done_interrupt"); - - priv->tx_last_cleared = jiffies; - priv->tx_command_given = 0; - switch (status) - { - case 1: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit OK\n"); - dev->stats.tx_packets++; - priv->bad = 0; - priv->reset = 0; - priv->retransmissions = 0; - if (priv->Conf->tx_delay_ms) - { - priv->tx_done_delayed = jiffies + (priv->Conf->tx_delay_ms * HZ) / 1000 + 1; - } - else - { - TXLAST(dev).offset = 0; - if (priv->txLast) - priv->txLast = 0; - else if (TXTAIL(dev).offset) - priv->txLast = 1; - if (TXLAST(dev).offset) - { - arlan_retransmit_now(dev); - dev->trans_start = jiffies; - } - if (!TXHEAD(dev).offset || !TXTAIL(dev).offset) - { - netif_wake_queue (dev); - } - } - } - break; - - case 2: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit timed out\n"); - priv->bad += 1; - //arlan_queue_retransmit(dev); - RetryOrFail(dev); - } - break; - - case 3: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit max retries\n"); - priv->bad += 1; - priv->reset = 0; - //arlan_queue_retransmit(dev); - RetryOrFail(dev); - } - break; - - case 4: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit aborted\n"); - priv->bad += 1; - arlan_queue_retransmit(dev); - //RetryOrFail(dev); - } - break; - - case 5: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit not registered\n"); - priv->bad += 1; - //debug=101; - arlan_queue_retransmit(dev); - } - break; - - case 6: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit destination full\n"); - priv->bad += 1; - priv->reset = 0; - //arlan_drop_tx(dev); - arlan_queue_retransmit(dev); - } - break; - - case 7: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit unknown ack\n"); - priv->bad += 1; - priv->reset = 0; - arlan_queue_retransmit(dev); - } - break; - - case 8: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit dest mail box full\n"); - priv->bad += 1; - priv->reset = 0; - //arlan_drop_tx(dev); - arlan_queue_retransmit(dev); - } - break; - - case 9: - { - IFDEBUG(ARLAN_DEBUG_TX_CHAIN) - printk("arlan intr: transmit root dest not reg.\n"); - priv->bad += 1; - priv->reset = 1; - //arlan_drop_tx(dev); - arlan_queue_retransmit(dev); - } - break; - - default: - { - printk(KERN_ERR "arlan intr: transmit status unknown\n"); - priv->bad += 1; - priv->reset = 1; - arlan_drop_tx(dev); - } - } - - ARLAN_DEBUG_EXIT("arlan_tx_done_interrupt"); -} - - -static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short rxOffset, u_short pkt_len) -{ - char *skbtmp; - int i = 0; - - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - - - ARLAN_DEBUG_ENTRY("arlan_rx_interrupt"); - // by spec, not WRITESHMB(arlan->rxStatus,0x00); - // prohibited here arlan_command(dev, ARLAN_COMMAND_RX); - - if (pkt_len < 10 || pkt_len > 2048) - { - printk(KERN_WARNING "%s: got too short or long packet, len %d \n", dev->name, pkt_len); - return; - } - if (rxOffset + pkt_len > 0x2000) - { - printk("%s: got too long packet, len %d offset %x\n", dev->name, pkt_len, rxOffset); - return; - } - priv->in_bytes += pkt_len; - priv->in_bytes10 += pkt_len; - if (conf->measure_rate < 1) - conf->measure_rate = 1; - if (time_after(jiffies, priv->in_time + conf->measure_rate * HZ)) - { - conf->in_speed = priv->in_bytes / conf->measure_rate; - priv->in_bytes = 0; - priv->in_time = jiffies; - } - if (time_after(jiffies, priv->in_time10 + conf->measure_rate * 10*HZ)) - { - conf->in_speed10 = priv->in_bytes10 / (10 * conf->measure_rate); - priv->in_bytes10 = 0; - priv->in_time10 = jiffies; - } - DEBUGSHM(1, "arlan rcv pkt rxStatus= %d ", arlan->rxStatus, u_char); - switch (rxStatus) - { - case 1: - case 2: - case 3: - { - /* Malloc up new buffer. */ - struct sk_buff *skb; - - DEBUGSHM(50, "arlan recv pkt offs=%d\n", arlan->rxOffset, u_short); - DEBUGSHM(1, "arlan rxFrmType = %d \n", arlan->rxFrmType, u_char); - DEBUGSHM(1, KERN_INFO "arlan rx scrambled = %d \n", arlan->scrambled, u_char); - - /* here we do multicast filtering to avoid slow 8-bit memcopy */ -#ifdef ARLAN_MULTICAST - if (!(dev->flags & IFF_ALLMULTI) && - !(dev->flags & IFF_PROMISC) && - dev->mc_list) - { - char hw_dst_addr[6]; - struct dev_mc_list *dmi = dev->mc_list; - int i; - - memcpy_fromio(hw_dst_addr, arlan->ultimateDestAddress, 6); - if (hw_dst_addr[0] == 0x01) - { - if (mdebug) - if (hw_dst_addr[1] == 0x00) - printk(KERN_ERR "%s mcast 0x0100 \n", dev->name); - else if (hw_dst_addr[1] == 0x40) - printk(KERN_ERR "%s m/bcast 0x0140 \n", dev->name); - while (dmi) - { - if (dmi->dmi_addrlen == 6) { - if (arlan_debug & ARLAN_DEBUG_HEADER_DUMP) - printk(KERN_ERR "%s mcl %pM\n", - dev->name, dmi->dmi_addr); - for (i = 0; i < 6; i++) - if (dmi->dmi_addr[i] != hw_dst_addr[i]) - break; - if (i == 6) - break; - } else - printk(KERN_ERR "%s: invalid multicast address length given.\n", dev->name); - dmi = dmi->next; - } - /* we reach here if multicast filtering is on and packet - * is multicast and not for receive */ - goto end_of_interrupt; - } - } -#endif // ARLAN_MULTICAST - /* multicast filtering ends here */ - pkt_len += ARLAN_FAKE_HDR_LEN; - - skb = dev_alloc_skb(pkt_len + 4); - if (skb == NULL) - { - printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name); - dev->stats.rx_dropped++; - break; - } - skb_reserve(skb, 2); - skbtmp = skb_put(skb, pkt_len); - - memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN); - memcpy_fromio(skbtmp, arlan->ultimateDestAddress, 6); - memcpy_fromio(skbtmp + 6, arlan->rxSrc, 6); - WRITESHMB(arlan->rxStatus, 0x00); - arlan_command(dev, ARLAN_COMMAND_RX); - - IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) - { - char immedDestAddress[6]; - char immedSrcAddress[6]; - memcpy_fromio(immedDestAddress, arlan->immedDestAddress, 6); - memcpy_fromio(immedSrcAddress, arlan->immedSrcAddress, 6); - - printk(KERN_WARNING "%s t %pM f %pM imd %pM ims %pM\n", - dev->name, skbtmp, - &skbtmp[6], - immedDestAddress, - immedSrcAddress); - } - skb->protocol = eth_type_trans(skb, dev); - IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) - if (skb->protocol != 0x608 && skb->protocol != 0x8) - { - for (i = 0; i <= 22; i++) - printk("%02x:", (u_char) skbtmp[i + 12]); - printk(KERN_ERR "\n"); - printk(KERN_WARNING "arlan kernel pkt type trans %x \n", skb->protocol); - } - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - break; - - default: - printk(KERN_ERR "arlan intr: received unknown status\n"); - dev->stats.rx_crc_errors++; - break; - } - ARLAN_DEBUG_EXIT("arlan_rx_interrupt"); -} - -static void arlan_process_interrupt(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - u_char rxStatus = READSHMB(arlan->rxStatus); - u_char txStatus = READSHMB(arlan->txStatus); - u_short rxOffset = READSHMS(arlan->rxOffset); - u_short pkt_len = READSHMS(arlan->rxLength); - int interrupt_count = 0; - - ARLAN_DEBUG_ENTRY("arlan_process_interrupt"); - - if (test_and_set_bit(0, (void *) &priv->interrupt_processing_active)) - { - if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) - printk(KERN_ERR "interrupt chain reentering \n"); - goto end_int_process; - } - while ((rxStatus || txStatus || priv->interrupt_ack_requested) - && (interrupt_count < 5)) - { - if (rxStatus) - priv->last_rx_int_ack_time = jiffies; - - arlan_command(dev, ARLAN_COMMAND_INT_ACK); - arlan_command(dev, ARLAN_COMMAND_INT_ENABLE); - - IFDEBUG(ARLAN_DEBUG_INTERRUPT) - printk(KERN_ERR "%s: got IRQ rx %x tx %x comm %x rxOff %x rxLen %x \n", - dev->name, rxStatus, txStatus, READSHMB(arlan->commandByte), - rxOffset, pkt_len); - - if (rxStatus == 0 && txStatus == 0) - { - if (priv->irq_test_done) - { - if (!registrationBad(dev)) - IFDEBUG(ARLAN_DEBUG_INTERRUPT) printk(KERN_ERR "%s unknown interrupt(nop? regLost ?) reason tx %d rx %d ", - dev->name, txStatus, rxStatus); - } else { - IFDEBUG(ARLAN_DEBUG_INTERRUPT) - printk(KERN_INFO "%s irq $%d test OK \n", dev->name, dev->irq); - - } - priv->interrupt_ack_requested = 0; - goto ends; - } - if (txStatus != 0) - { - WRITESHMB(arlan->txStatus, 0x00); - arlan_tx_done_interrupt(dev, txStatus); - goto ends; - } - if (rxStatus == 1 || rxStatus == 2) - { /* a packet waiting */ - arlan_rx_interrupt(dev, rxStatus, rxOffset, pkt_len); - goto ends; - } - if (rxStatus > 2 && rxStatus < 0xff) - { - WRITESHMB(arlan->rxStatus, 0x00); - printk(KERN_ERR "%s unknown rxStatus reason tx %d rx %d ", - dev->name, txStatus, rxStatus); - goto ends; - } - if (rxStatus == 0xff) - { - WRITESHMB(arlan->rxStatus, 0x00); - arlan_command(dev, ARLAN_COMMAND_RX); - if (registrationBad(dev)) - netif_device_detach(dev); - if (!registrationBad(dev)) - { - priv->registrationLastSeen = jiffies; - if (!netif_queue_stopped(dev) && !priv->under_reset && !priv->under_config) - netif_wake_queue (dev); - } - goto ends; - } -ends: - - arlan_command_process(dev); - - rxStatus = READSHMB(arlan->rxStatus); - txStatus = READSHMB(arlan->txStatus); - rxOffset = READSHMS(arlan->rxOffset); - pkt_len = READSHMS(arlan->rxLength); - - - priv->irq_test_done = 1; - - interrupt_count++; - } - priv->interrupt_processing_active = 0; - -end_int_process: - arlan_command_process(dev); - - ARLAN_DEBUG_EXIT("arlan_process_interrupt"); - return; -} - -static irqreturn_t arlan_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - u_char rxStatus = READSHMB(arlan->rxStatus); - u_char txStatus = READSHMB(arlan->txStatus); - - ARLAN_DEBUG_ENTRY("arlan_interrupt"); - - - if (!rxStatus && !txStatus) - priv->interrupt_ack_requested++; - - arlan_process_interrupt(dev); - - priv->irq_test_done = 1; - - ARLAN_DEBUG_EXIT("arlan_interrupt"); - return IRQ_HANDLED; - -} - - -static int arlan_close(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - - ARLAN_DEBUG_ENTRY("arlan_close"); - - del_timer_sync(&priv->timer); - - arlan_command(dev, ARLAN_COMMAND_POWERDOWN); - - IFDEBUG(ARLAN_DEBUG_STARTUP) - printk(KERN_NOTICE "%s: Closing device\n", dev->name); - - netif_stop_queue(dev); - free_irq(dev->irq, dev); - - ARLAN_DEBUG_EXIT("arlan_close"); - return 0; -} - -#ifdef ARLAN_DEBUGGING -static long alignLong(volatile u_char * ptr) -{ - long ret; - memcpy_fromio(&ret, (void *) ptr, 4); - return ret; -} -#endif - -/* - * Get the current statistics. - * This may be called with the card open or closed. - */ - -static struct net_device_stats *arlan_statistics(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - - - ARLAN_DEBUG_ENTRY("arlan_statistics"); - - /* Update the statistics from the device registers. */ - - READSHM(dev->stats.collisions, arlan->numReTransmissions, u_int); - READSHM(dev->stats.rx_crc_errors, arlan->numCRCErrors, u_int); - READSHM(dev->stats.rx_dropped, arlan->numFramesDiscarded, u_int); - READSHM(dev->stats.rx_fifo_errors, arlan->numRXBufferOverflows, u_int); - READSHM(dev->stats.rx_frame_errors, arlan->numReceiveFramesLost, u_int); - READSHM(dev->stats.rx_over_errors, arlan->numRXOverruns, u_int); - READSHM(dev->stats.rx_packets, arlan->numDatagramsReceived, u_int); - READSHM(dev->stats.tx_aborted_errors, arlan->numAbortErrors, u_int); - READSHM(dev->stats.tx_carrier_errors, arlan->numStatusTimeouts, u_int); - READSHM(dev->stats.tx_dropped, arlan->numDatagramsDiscarded, u_int); - READSHM(dev->stats.tx_fifo_errors, arlan->numTXUnderruns, u_int); - READSHM(dev->stats.tx_packets, arlan->numDatagramsTransmitted, u_int); - READSHM(dev->stats.tx_window_errors, arlan->numHoldOffs, u_int); - - ARLAN_DEBUG_EXIT("arlan_statistics"); - - return &dev->stats; -} - - -static void arlan_set_multicast(struct net_device *dev) -{ - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - struct arlan_conf_stru *conf = priv->Conf; - int board_conf_needed = 0; - - - ARLAN_DEBUG_ENTRY("arlan_set_multicast"); - - if (dev->flags & IFF_PROMISC) - { - unsigned char recMode; - READSHM(recMode, arlan->receiveMode, u_char); - conf->receiveMode = (ARLAN_RCV_PROMISC | ARLAN_RCV_CONTROL); - if (conf->receiveMode != recMode) - board_conf_needed = 1; - } - else - { - /* turn off promiscuous mode */ - unsigned char recMode; - READSHM(recMode, arlan->receiveMode, u_char); - conf->receiveMode = ARLAN_RCV_CLEAN | ARLAN_RCV_CONTROL; - if (conf->receiveMode != recMode) - board_conf_needed = 1; - } - if (board_conf_needed) - arlan_command(dev, ARLAN_COMMAND_CONF); - - ARLAN_DEBUG_EXIT("arlan_set_multicast"); -} - - -struct net_device * __init arlan_probe(int unit) -{ - struct net_device *dev; - int err; - int m; - - ARLAN_DEBUG_ENTRY("arlan_probe"); - - if (arlans_found == MAX_ARLANS) - return ERR_PTR(-ENODEV); - - /* - * Reserve space for local data and a copy of the shared memory - * that is used by the /proc interface. - */ - dev = alloc_etherdev(sizeof(struct arlan_private) - + sizeof(struct arlan_shmem)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - if (dev->mem_start) { - if (arlan_probe_here(dev, dev->mem_start) == 0) - goto found; - goto not_found; - } - - } - - - for (m = (int)phys_to_virt(lastFoundAt) + ARLAN_SHMEM_SIZE; - m <= (int)phys_to_virt(0xDE000); - m += ARLAN_SHMEM_SIZE) - { - if (arlan_probe_here(dev, m) == 0) - { - lastFoundAt = (int)virt_to_phys((void*)m); - goto found; - } - } - - if (lastFoundAt == 0xbe000) - printk(KERN_ERR "arlan: No Arlan devices found \n"); - - not_found: - free_netdev(dev); - return ERR_PTR(-ENODEV); - - found: - err = arlan_setup_device(dev, arlans_found); - if (err) - dev = ERR_PTR(err); - else if (!arlans_found++) - printk(KERN_INFO "Arlan driver %s\n", arlan_version); - - return dev; -} - -#ifdef MODULE -int __init init_module(void) -{ - int i = 0; - - ARLAN_DEBUG_ENTRY("init_module"); - - if (channelSet != channelSetUNKNOWN || channelNumber != channelNumberUNKNOWN || systemId != systemIdUNKNOWN) - return -EINVAL; - - for (i = 0; i < MAX_ARLANS; i++) { - struct net_device *dev = arlan_probe(i); - - if (IS_ERR(dev)) - return PTR_ERR(dev); - } - init_arlan_proc(); - printk(KERN_INFO "Arlan driver %s\n", arlan_version); - ARLAN_DEBUG_EXIT("init_module"); - return 0; -} - - -void __exit cleanup_module(void) -{ - int i = 0; - struct net_device *dev; - - ARLAN_DEBUG_ENTRY("cleanup_module"); - - IFDEBUG(ARLAN_DEBUG_SHUTDOWN) - printk(KERN_INFO "arlan: unloading module\n"); - - cleanup_arlan_proc(); - - for (i = 0; i < MAX_ARLANS; i++) - { - dev = arlan_device[i]; - if (dev) { - arlan_command(dev, ARLAN_COMMAND_POWERDOWN ); - - unregister_netdev(dev); - release_mem_region(virt_to_phys((void *) dev->mem_start), - ARLAN_SHMEM_SIZE); - free_netdev(dev); - arlan_device[i] = NULL; - } - } - - ARLAN_DEBUG_EXIT("cleanup_module"); -} - - -#endif -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/arlan-proc.c b/drivers/net/wireless/arlan-proc.c deleted file mode 100644 index a8b689635a3b..000000000000 --- a/drivers/net/wireless/arlan-proc.c +++ /dev/null @@ -1,1253 +0,0 @@ -#include "arlan.h" - -#include - -#ifdef CONFIG_PROC_FS - -/* void enableReceive(struct net_device* dev); -*/ - - - -#define ARLAN_STR_SIZE 0x2ff0 -#define DEV_ARLAN_INFO 1 -#define DEV_ARLAN 1 -#define SARLG(type,var) {\ - pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n", #var, READSHMB(priva->card->var)); \ - } - -#define SARLBN(type,var,nn) {\ - pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x",#var);\ - for (i=0; i < nn; i++ ) pos += sprintf(arlan_drive_info+pos, "%02x",READSHMB(priva->card->var[i]));\ - pos += sprintf(arlan_drive_info+pos, "\n"); \ - } - -#define SARLBNpln(type,var,nn) {\ - for (i=0; i < nn; i++ ) pos += sprintf(arlan_drive_info+pos, "%02x",READSHMB(priva->card->var[i]));\ - } - -#define SARLSTR(var,nn) {\ - char tmpStr[400];\ - int tmpLn = nn;\ - if (nn > 399 ) tmpLn = 399; \ - memcpy(tmpStr,(char *) priva->conf->var,tmpLn);\ - tmpStr[tmpLn] = 0; \ - pos += sprintf(arlan_drive_info+pos, "%s\t=\t%s \n",#var,priva->conf->var);\ - } - -#define SARLUC(var) SARLG(u_char, var) -#define SARLUCN(var,nn) SARLBN(u_char,var, nn) -#define SARLUS(var) SARLG(u_short, var) -#define SARLUSN(var,nn) SARLBN(u_short,var, nn) -#define SARLUI(var) SARLG(u_int, var) - -#define SARLUSA(var) {\ - u_short tmpVar;\ - memcpy(&tmpVar, (short *) priva->conf->var,2); \ - pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n",#var, tmpVar);\ -} - -#define SARLUIA(var) {\ - u_int tmpVar;\ - memcpy(&tmpVar, (int* )priva->conf->var,4); \ - pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n",#var, tmpVar);\ -} - - -static const char *arlan_diagnostic_info_string(struct net_device *dev) -{ - - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - u_char diagnosticInfo; - - READSHM(diagnosticInfo, arlan->diagnosticInfo, u_char); - - switch (diagnosticInfo) - { - case 0xFF: - return "Diagnostic info is OK"; - case 0xFE: - return "ERROR EPROM Checksum error "; - case 0xFD: - return "ERROR Local Ram Test Failed "; - case 0xFC: - return "ERROR SCC failure "; - case 0xFB: - return "ERROR BackBone failure "; - case 0xFA: - return "ERROR transceiver not found "; - case 0xF9: - return "ERROR no more address space "; - case 0xF8: - return "ERROR Checksum error "; - case 0xF7: - return "ERROR Missing SS Code"; - case 0xF6: - return "ERROR Invalid config format"; - case 0xF5: - return "ERROR Reserved errorcode F5"; - case 0xF4: - return "ERROR Invalid spreading code/channel number"; - case 0xF3: - return "ERROR Load Code Error"; - case 0xF2: - return "ERROR Reserver errorcode F2 "; - case 0xF1: - return "ERROR Invalid command receivec by LAN card "; - case 0xF0: - return "ERROR Invalid parameter found in command "; - case 0xEF: - return "ERROR On-chip timer failure "; - case 0xEE: - return "ERROR T410 timer failure "; - case 0xED: - return "ERROR Too Many TxEnable commands "; - case 0xEC: - return "ERROR EEPROM error on radio module "; - default: - return "ERROR unknown Diagnostic info reply code "; - } -} - -static const char *arlan_hardware_type_string(struct net_device *dev) -{ - u_char hardwareType; - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - - READSHM(hardwareType, arlan->hardwareType, u_char); - switch (hardwareType) - { - case 0x00: - return "type A450"; - case 0x01: - return "type A650 "; - case 0x04: - return "type TMA coproc"; - case 0x0D: - return "type A650E "; - case 0x18: - return "type TMA coproc Australian"; - case 0x19: - return "type A650A "; - case 0x26: - return "type TMA coproc European"; - case 0x2E: - return "type A655 "; - case 0x2F: - return "type A655A "; - case 0x30: - return "type A655E "; - case 0x0B: - return "type A670 "; - case 0x0C: - return "type A670E "; - case 0x2D: - return "type A670A "; - case 0x0F: - return "type A411T"; - case 0x16: - return "type A411TA"; - case 0x1B: - return "type A440T"; - case 0x1C: - return "type A412T"; - case 0x1E: - return "type A412TA"; - case 0x22: - return "type A411TE"; - case 0x24: - return "type A412TE"; - case 0x27: - return "type A671T "; - case 0x29: - return "type A671TA "; - case 0x2B: - return "type A671TE "; - case 0x31: - return "type A415T "; - case 0x33: - return "type A415TA "; - case 0x35: - return "type A415TE "; - case 0x37: - return "type A672"; - case 0x39: - return "type A672A "; - case 0x3B: - return "type A672T"; - case 0x6B: - return "type IC2200"; - default: - return "type A672T"; - } -} -#ifdef ARLAN_DEBUGGING -static void arlan_print_diagnostic_info(struct net_device *dev) -{ - int i; - u_char diagnosticInfo; - u_short diagnosticOffset; - u_char hardwareType; - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - - // ARLAN_DEBUG_ENTRY("arlan_print_diagnostic_info"); - - if (READSHMB(arlan->configuredStatusFlag) == 0) - printk("Arlan: Card NOT configured\n"); - else - printk("Arlan: Card is configured\n"); - - READSHM(diagnosticInfo, arlan->diagnosticInfo, u_char); - READSHM(diagnosticOffset, arlan->diagnosticOffset, u_short); - - printk(KERN_INFO "%s\n", arlan_diagnostic_info_string(dev)); - - if (diagnosticInfo != 0xff) - printk("%s arlan: Diagnostic Offset %d \n", dev->name, diagnosticOffset); - - printk("arlan: LAN CODE ID = "); - for (i = 0; i < 6; i++) - DEBUGSHM(1, "%03d:", arlan->lanCardNodeId[i], u_char); - printk("\n"); - - printk("arlan: Arlan BroadCast address = "); - for (i = 0; i < 6; i++) - DEBUGSHM(1, "%03d:", arlan->broadcastAddress[i], u_char); - printk("\n"); - - READSHM(hardwareType, arlan->hardwareType, u_char); - printk(KERN_INFO "%s\n", arlan_hardware_type_string(dev)); - - - DEBUGSHM(1, "arlan: channelNumber=%d\n", arlan->channelNumber, u_char); - DEBUGSHM(1, "arlan: channelSet=%d\n", arlan->channelSet, u_char); - DEBUGSHM(1, "arlan: spreadingCode=%d\n", arlan->spreadingCode, u_char); - DEBUGSHM(1, "arlan: radioNodeId=%d\n", arlan->radioNodeId, u_short); - DEBUGSHM(1, "arlan: SID =%d\n", arlan->SID, u_short); - DEBUGSHM(1, "arlan: rxOffset=%d\n", arlan->rxOffset, u_short); - - DEBUGSHM(1, "arlan: registration mode is %d\n", arlan->registrationMode, u_char); - - printk("arlan: name= "); - IFDEBUG(1) - - for (i = 0; i < 16; i++) - { - char c; - READSHM(c, arlan->name[i], char); - if (c) - printk("%c", c); - } - printk("\n"); - -// ARLAN_DEBUG_EXIT("arlan_print_diagnostic_info"); - -} - - -/****************************** TEST MEMORY **************/ - -static int arlan_hw_test_memory(struct net_device *dev) -{ - u_char *ptr; - int i; - int memlen = sizeof(struct arlan_shmem) - 0xF; /* avoid control register */ - volatile char *arlan_mem = (char *) (dev->mem_start); - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - char pattern; - - ptr = NULL; - - /* hold card in reset state */ - setHardwareReset(dev); - - /* test memory */ - pattern = 0; - for (i = 0; i < memlen; i++) - WRITESHM(arlan_mem[i], ((u_char) pattern++), u_char); - - pattern = 0; - for (i = 0; i < memlen; i++) - { - char res; - READSHM(res, arlan_mem[i], char); - if (res != pattern++) - { - printk(KERN_ERR "Arlan driver memory test 1 failed \n"); - return -1; - } - } - - pattern = 0; - for (i = 0; i < memlen; i++) - WRITESHM(arlan_mem[i], ~(pattern++), char); - - pattern = 0; - for (i = 0; i < memlen; i++) - { - char res; - READSHM(res, arlan_mem[i], char); - if (res != ~(pattern++)) - { - printk(KERN_ERR "Arlan driver memory test 2 failed \n"); - return -1; - } - } - - /* zero memory */ - for (i = 0; i < memlen; i++) - WRITESHM(arlan_mem[i], 0x00, char); - - IFDEBUG(1) printk(KERN_INFO "Arlan: memory tests ok\n"); - - /* set reset flag and then release reset */ - WRITESHM(arlan->resetFlag, 0xff, u_char); - - clearChannelAttention(dev); - clearHardwareReset(dev); - - /* wait for reset flag to become zero, we'll wait for two seconds */ - if (arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW)) - { - printk(KERN_ERR "%s arlan: failed to come back from memory test\n", dev->name); - return -1; - } - return 0; -} - -static int arlan_setup_card_by_book(struct net_device *dev) -{ - u_char irqLevel, configuredStatusFlag; - struct arlan_private *priv = netdev_priv(dev); - volatile struct arlan_shmem __iomem *arlan = priv->card; - -// ARLAN_DEBUG_ENTRY("arlan_setup_card"); - - READSHM(configuredStatusFlag, arlan->configuredStatusFlag, u_char); - - IFDEBUG(10) - if (configuredStatusFlag != 0) - IFDEBUG(10) printk("arlan: CARD IS CONFIGURED\n"); - else - IFDEBUG(10) printk("arlan: card is NOT configured\n"); - - if (testMemory || (READSHMB(arlan->diagnosticInfo) != 0xff)) - if (arlan_hw_test_memory(dev)) - return -1; - - DEBUGSHM(4, "arlan configuredStatus = %d \n", arlan->configuredStatusFlag, u_char); - DEBUGSHM(4, "arlan driver diagnostic: 0x%2x\n", arlan->diagnosticInfo, u_char); - - /* issue nop command - no interrupt */ - arlan_command(dev, ARLAN_COMMAND_NOOP); - if (arlan_command(dev, ARLAN_COMMAND_WAIT_NOW) != 0) - return -1; - - IFDEBUG(50) printk("1st Noop successfully executed !!\n"); - - /* try to turn on the arlan interrupts */ - clearClearInterrupt(dev); - setClearInterrupt(dev); - setInterruptEnable(dev); - - /* issue nop command - with interrupt */ - - arlan_command(dev, ARLAN_COMMAND_NOOPINT); - if (arlan_command(dev, ARLAN_COMMAND_WAIT_NOW) != 0) - return -1; - - - IFDEBUG(50) printk("2nd Noop successfully executed !!\n"); - - READSHM(irqLevel, arlan->irqLevel, u_char) - - if (irqLevel != dev->irq) - { - IFDEBUG(1) printk(KERN_WARNING "arlan dip switches set irq to %d\n", irqLevel); - printk(KERN_WARNING "device driver irq set to %d - does not match\n", dev->irq); - dev->irq = irqLevel; - } - else - IFDEBUG(2) printk("irq level is OK\n"); - - - IFDEBUG(3) arlan_print_diagnostic_info(dev); - - arlan_command(dev, ARLAN_COMMAND_CONF); - - READSHM(configuredStatusFlag, arlan->configuredStatusFlag, u_char); - if (configuredStatusFlag == 0) - { - printk(KERN_WARNING "arlan configure failed\n"); - return -1; - } - arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW); - arlan_command(dev, ARLAN_COMMAND_RX); - arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW); - printk(KERN_NOTICE "%s: arlan driver version %s loaded\n", - dev->name, arlan_version); - -// ARLAN_DEBUG_EXIT("arlan_setup_card"); - - return 0; /* no errors */ -} -#endif - -#ifdef ARLAN_PROC_INTERFACE -#ifdef ARLAN_PROC_SHM_DUMP - -static char arlan_drive_info[ARLAN_STR_SIZE] = "A655\n\0"; - -static int arlan_sysctl_info(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int i; - int retv, pos, devnum; - struct arlan_private *priva = NULL; - struct net_device *dev; - pos = 0; - if (write) - { - printk("wrirte: "); - for (i = 0; i < 100; i++) - printk("adi %x \n", arlan_drive_info[i]); - } - if (ctl->procname == NULL || arlan_drive_info == NULL) - { - printk(KERN_WARNING " procname is NULL in sysctl_table or arlan_drive_info is NULL \n at arlan module\n "); - return -1; - } - devnum = ctl->procname[5] - '0'; - if (devnum < 0 || devnum > MAX_ARLANS - 1) - { - printk(KERN_WARNING "too strange devnum in procfs parse\n "); - return -1; - } - else if (arlan_device[devnum] == NULL) - { - if (ctl->procname) - pos += sprintf(arlan_drive_info + pos, "\t%s\n\n", ctl->procname); - pos += sprintf(arlan_drive_info + pos, "No device found here \n"); - goto final; - } - else - priva = netdev_priv(arlan_device[devnum]); - - if (priva == NULL) - { - printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); - return -1; - } - dev = arlan_device[devnum]; - - memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); - - pos = sprintf(arlan_drive_info, "Arlan info \n"); - /* Header Signature */ - SARLSTR(textRegion, 48); - SARLUC(resetFlag); - pos += sprintf(arlan_drive_info + pos, "diagnosticInfo\t=\t%s \n", arlan_diagnostic_info_string(dev)); - SARLUC(diagnosticInfo); - SARLUS(diagnosticOffset); - SARLUCN(_1, 12); - SARLUCN(lanCardNodeId, 6); - SARLUCN(broadcastAddress, 6); - pos += sprintf(arlan_drive_info + pos, "hardwareType =\t %s \n", arlan_hardware_type_string(dev)); - SARLUC(hardwareType); - SARLUC(majorHardwareVersion); - SARLUC(minorHardwareVersion); - SARLUC(radioModule); - SARLUC(defaultChannelSet); - SARLUCN(_2, 47); - - /* Control/Status Block - 0x0080 */ - SARLUC(interruptInProgress); - SARLUC(cntrlRegImage); - - SARLUCN(_3, 14); - SARLUC(commandByte); - SARLUCN(commandParameter, 15); - - /* Receive Status - 0x00a0 */ - SARLUC(rxStatus); - SARLUC(rxFrmType); - SARLUS(rxOffset); - SARLUS(rxLength); - SARLUCN(rxSrc, 6); - SARLUC(rxBroadcastFlag); - SARLUC(rxQuality); - SARLUC(scrambled); - SARLUCN(_4, 1); - - /* Transmit Status - 0x00b0 */ - SARLUC(txStatus); - SARLUC(txAckQuality); - SARLUC(numRetries); - SARLUCN(_5, 14); - SARLUCN(registeredRouter, 6); - SARLUCN(backboneRouter, 6); - SARLUC(registrationStatus); - SARLUC(configuredStatusFlag); - SARLUCN(_6, 1); - SARLUCN(ultimateDestAddress, 6); - SARLUCN(immedDestAddress, 6); - SARLUCN(immedSrcAddress, 6); - SARLUS(rxSequenceNumber); - SARLUC(assignedLocaltalkAddress); - SARLUCN(_7, 27); - - /* System Parameter Block */ - - /* - Driver Parameters (Novell Specific) */ - - SARLUS(txTimeout); - SARLUS(transportTime); - SARLUCN(_8, 4); - - /* - Configuration Parameters */ - SARLUC(irqLevel); - SARLUC(spreadingCode); - SARLUC(channelSet); - SARLUC(channelNumber); - SARLUS(radioNodeId); - SARLUCN(_9, 2); - SARLUC(scramblingDisable); - SARLUC(radioType); - SARLUS(routerId); - SARLUCN(_10, 9); - SARLUC(txAttenuation); - SARLUIA(systemId); - SARLUS(globalChecksum); - SARLUCN(_11, 4); - SARLUS(maxDatagramSize); - SARLUS(maxFrameSize); - SARLUC(maxRetries); - SARLUC(receiveMode); - SARLUC(priority); - SARLUC(rootOrRepeater); - SARLUCN(specifiedRouter, 6); - SARLUS(fastPollPeriod); - SARLUC(pollDecay); - SARLUSA(fastPollDelay); - SARLUC(arlThreshold); - SARLUC(arlDecay); - SARLUCN(_12, 1); - SARLUS(specRouterTimeout); - SARLUCN(_13, 5); - - /* Scrambled Area */ - SARLUIA(SID); - SARLUCN(encryptionKey, 12); - SARLUIA(_14); - SARLUSA(waitTime); - SARLUSA(lParameter); - SARLUCN(_15, 3); - SARLUS(headerSize); - SARLUS(sectionChecksum); - - SARLUC(registrationMode); - SARLUC(registrationFill); - SARLUS(pollPeriod); - SARLUS(refreshPeriod); - SARLSTR(name, 16); - SARLUCN(NID, 6); - SARLUC(localTalkAddress); - SARLUC(codeFormat); - SARLUC(numChannels); - SARLUC(channel1); - SARLUC(channel2); - SARLUC(channel3); - SARLUC(channel4); - SARLUCN(SSCode, 59); - -/* SARLUCN( _16, 0x140); - */ - /* Statistics Block - 0x0300 */ - SARLUC(hostcpuLock); - SARLUC(lancpuLock); - SARLUCN(resetTime, 18); - SARLUIA(numDatagramsTransmitted); - SARLUIA(numReTransmissions); - SARLUIA(numFramesDiscarded); - SARLUIA(numDatagramsReceived); - SARLUIA(numDuplicateReceivedFrames); - SARLUIA(numDatagramsDiscarded); - SARLUS(maxNumReTransmitDatagram); - SARLUS(maxNumReTransmitFrames); - SARLUS(maxNumConsecutiveDuplicateFrames); - /* misaligned here so we have to go to characters */ - SARLUIA(numBytesTransmitted); - SARLUIA(numBytesReceived); - SARLUIA(numCRCErrors); - SARLUIA(numLengthErrors); - SARLUIA(numAbortErrors); - SARLUIA(numTXUnderruns); - SARLUIA(numRXOverruns); - SARLUIA(numHoldOffs); - SARLUIA(numFramesTransmitted); - SARLUIA(numFramesReceived); - SARLUIA(numReceiveFramesLost); - SARLUIA(numRXBufferOverflows); - SARLUIA(numFramesDiscardedAddrMismatch); - SARLUIA(numFramesDiscardedSIDMismatch); - SARLUIA(numPollsTransmistted); - SARLUIA(numPollAcknowledges); - SARLUIA(numStatusTimeouts); - SARLUIA(numNACKReceived); - SARLUS(auxCmd); - SARLUCN(dumpPtr, 4); - SARLUC(dumpVal); - SARLUC(wireTest); - - /* next 4 seems too long for procfs, over single page ? - SARLUCN( _17, 0x86); - SARLUCN( txBuffer, 0x800); - SARLUCN( rxBuffer, 0x800); - SARLUCN( _18, 0x0bff); - */ - - pos += sprintf(arlan_drive_info + pos, "rxRing\t=\t0x"); - for (i = 0; i < 0x50; i++) - pos += sprintf(arlan_drive_info + pos, "%02x", ((char *) priva->conf)[priva->conf->rxOffset + i]); - pos += sprintf(arlan_drive_info + pos, "\n"); - - SARLUC(configStatus); - SARLUC(_22); - SARLUC(progIOCtrl); - SARLUC(shareMBase); - SARLUC(controlRegister); - - pos += sprintf(arlan_drive_info + pos, " total %d chars\n", pos); - if (ctl) - if (ctl->procname) - pos += sprintf(arlan_drive_info + pos, " driver name : %s\n", ctl->procname); -final: - *lenp = pos; - - if (!write) - retv = proc_dostring(ctl, write, buffer, lenp, ppos); - else - { - *lenp = 0; - return -1; - } - return retv; -} - - -static int arlan_sysctl_info161719(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int i; - int retv, pos, devnum; - struct arlan_private *priva = NULL; - - pos = 0; - devnum = ctl->procname[5] - '0'; - if (arlan_device[devnum] == NULL) - { - pos += sprintf(arlan_drive_info + pos, "No device found here \n"); - goto final; - } - else - priva = netdev_priv(arlan_device[devnum]); - if (priva == NULL) - { - printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); - return -1; - } - memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); - SARLUCN(_16, 0xC0); - SARLUCN(_17, 0x6A); - SARLUCN(_18, 14); - SARLUCN(_19, 0x86); - SARLUCN(_21, 0x3fd); - -final: - *lenp = pos; - retv = proc_dostring(ctl, write, buffer, lenp, ppos); - return retv; -} - -static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int i; - int retv, pos, devnum; - struct arlan_private *priva = NULL; - - pos = 0; - devnum = ctl->procname[5] - '0'; - if (arlan_device[devnum] == NULL) - { - pos += sprintf(arlan_drive_info + pos, "No device found here \n"); - goto final; - } - else - priva = netdev_priv(arlan_device[devnum]); - if (priva == NULL) - { - printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); - return -1; - } - memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); - SARLBNpln(u_char, txBuffer, 0x800); -final: - *lenp = pos; - retv = proc_dostring(ctl, write, buffer, lenp, ppos); - return retv; -} - -static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int i; - int retv, pos, devnum; - struct arlan_private *priva = NULL; - - pos = 0; - devnum = ctl->procname[5] - '0'; - if (arlan_device[devnum] == NULL) - { - pos += sprintf(arlan_drive_info + pos, "No device found here \n"); - goto final; - } else - priva = netdev_priv(arlan_device[devnum]); - if (priva == NULL) - { - printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); - return -1; - } - memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); - SARLBNpln(u_char, rxBuffer, 0x800); -final: - *lenp = pos; - retv = proc_dostring(ctl, write, buffer, lenp, ppos); - return retv; -} - -static int arlan_sysctl_info18(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int i; - int retv, pos, devnum; - struct arlan_private *priva = NULL; - - pos = 0; - devnum = ctl->procname[5] - '0'; - if (arlan_device[devnum] == NULL) - { - pos += sprintf(arlan_drive_info + pos, "No device found here \n"); - goto final; - } - else - priva = netdev_priv(arlan_device[devnum]); - if (priva == NULL) - { - printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); - return -1; - } - memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); - SARLBNpln(u_char, _18, 0x800); - -final: - *lenp = pos; - retv = proc_dostring(ctl, write, buffer, lenp, ppos); - return retv; -} - - -#endif /* #ifdef ARLAN_PROC_SHM_DUMP */ - - -static char conf_reset_result[200]; - -static int arlan_configure(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int pos = 0; - int devnum = ctl->procname[6] - '0'; - struct arlan_private *priv; - - if (devnum < 0 || devnum > MAX_ARLANS - 1) - { - printk(KERN_WARNING "too strange devnum in procfs parse\n "); - return -1; - } - else if (arlan_device[devnum] != NULL) - { - priv = netdev_priv(arlan_device[devnum]); - - arlan_command(arlan_device[devnum], ARLAN_COMMAND_CLEAN_AND_CONF); - } - else - return -1; - - *lenp = pos; - return proc_dostring(ctl, write, buffer, lenp, ppos); -} - -static int arlan_sysctl_reset(ctl_table * ctl, int write, - void __user *buffer, size_t * lenp, loff_t *ppos) -{ - int pos = 0; - int devnum = ctl->procname[5] - '0'; - struct arlan_private *priv; - - if (devnum < 0 || devnum > MAX_ARLANS - 1) - { - printk(KERN_WARNING "too strange devnum in procfs parse\n "); - return -1; - } - else if (arlan_device[devnum] != NULL) - { - priv = netdev_priv(arlan_device[devnum]); - arlan_command(arlan_device[devnum], ARLAN_COMMAND_CLEAN_AND_RESET); - - } else - return -1; - *lenp = pos + 3; - return proc_dostring(ctl, write, buffer, lenp, ppos); -} - - -/* Place files in /proc/sys/dev/arlan */ -#define CTBLN(num,card,nam) \ - { .ctl_name = num,\ - .procname = #nam,\ - .data = &(arlan_conf[card].nam),\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec} -#ifdef ARLAN_DEBUGGING - -#define ARLAN_PROC_DEBUG_ENTRIES \ - { .ctl_name = 48, .procname = "entry_exit_debug",\ - .data = &arlan_entry_and_exit_debug,\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec},\ - { .ctl_name = 49, .procname = "debug", .data = &arlan_debug,\ - .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec}, -#else -#define ARLAN_PROC_DEBUG_ENTRIES -#endif - -#define ARLAN_SYSCTL_TABLE_TOTAL(cardNo)\ - CTBLN(1,cardNo,spreadingCode),\ - CTBLN(2,cardNo, channelNumber),\ - CTBLN(3,cardNo, scramblingDisable),\ - CTBLN(4,cardNo, txAttenuation),\ - CTBLN(5,cardNo, systemId), \ - CTBLN(6,cardNo, maxDatagramSize),\ - CTBLN(7,cardNo, maxFrameSize),\ - CTBLN(8,cardNo, maxRetries),\ - CTBLN(9,cardNo, receiveMode),\ - CTBLN(10,cardNo, priority),\ - CTBLN(11,cardNo, rootOrRepeater),\ - CTBLN(12,cardNo, SID),\ - CTBLN(13,cardNo, registrationMode),\ - CTBLN(14,cardNo, registrationFill),\ - CTBLN(15,cardNo, localTalkAddress),\ - CTBLN(16,cardNo, codeFormat),\ - CTBLN(17,cardNo, numChannels),\ - CTBLN(18,cardNo, channel1),\ - CTBLN(19,cardNo, channel2),\ - CTBLN(20,cardNo, channel3),\ - CTBLN(21,cardNo, channel4),\ - CTBLN(22,cardNo, txClear),\ - CTBLN(23,cardNo, txRetries),\ - CTBLN(24,cardNo, txRouting),\ - CTBLN(25,cardNo, txScrambled),\ - CTBLN(26,cardNo, rxParameter),\ - CTBLN(27,cardNo, txTimeoutMs),\ - CTBLN(28,cardNo, waitCardTimeout),\ - CTBLN(29,cardNo, channelSet), \ - {.ctl_name = 30, .procname = "name",\ - .data = arlan_conf[cardNo].siteName,\ - .maxlen = 16, .mode = 0600, .proc_handler = &proc_dostring},\ - CTBLN(31,cardNo,waitTime),\ - CTBLN(32,cardNo,lParameter),\ - CTBLN(33,cardNo,_15),\ - CTBLN(34,cardNo,headerSize),\ - CTBLN(36,cardNo,tx_delay_ms),\ - CTBLN(37,cardNo,retries),\ - CTBLN(38,cardNo,ReTransmitPacketMaxSize),\ - CTBLN(39,cardNo,waitReTransmitPacketMaxSize),\ - CTBLN(40,cardNo,fastReTransCount),\ - CTBLN(41,cardNo,driverRetransmissions),\ - CTBLN(42,cardNo,txAckTimeoutMs),\ - CTBLN(43,cardNo,registrationInterrupts),\ - CTBLN(44,cardNo,hardwareType),\ - CTBLN(45,cardNo,radioType),\ - CTBLN(46,cardNo,writeEEPROM),\ - CTBLN(47,cardNo,writeRadioType),\ - ARLAN_PROC_DEBUG_ENTRIES\ - CTBLN(50,cardNo,in_speed),\ - CTBLN(51,cardNo,out_speed),\ - CTBLN(52,cardNo,in_speed10),\ - CTBLN(53,cardNo,out_speed10),\ - CTBLN(54,cardNo,in_speed_max),\ - CTBLN(55,cardNo,out_speed_max),\ - CTBLN(56,cardNo,measure_rate),\ - CTBLN(57,cardNo,pre_Command_Wait),\ - CTBLN(58,cardNo,rx_tweak1),\ - CTBLN(59,cardNo,rx_tweak2),\ - CTBLN(60,cardNo,tx_queue_len),\ - - - -static ctl_table arlan_conf_table0[] = -{ - ARLAN_SYSCTL_TABLE_TOTAL(0) - -#ifdef ARLAN_PROC_SHM_DUMP - { - .ctl_name = 150, - .procname = "arlan0-txRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, - }, - { - .ctl_name = 151, - .procname = "arlan0-rxRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, - }, - { - .ctl_name = 152, - .procname = "arlan0-18", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info18, - }, - { - .ctl_name = 153, - .procname = "arlan0-ring", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, - }, - { - .ctl_name = 154, - .procname = "arlan0-shm-cpy", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info, - }, -#endif - { - .ctl_name = 155, - .procname = "config0", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_configure - }, - { - .ctl_name = 156, - .procname = "reset0", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_sysctl_reset, - }, - { .ctl_name = 0 } -}; - -static ctl_table arlan_conf_table1[] = -{ - - ARLAN_SYSCTL_TABLE_TOTAL(1) - -#ifdef ARLAN_PROC_SHM_DUMP - { - .ctl_name = 150, - .procname = "arlan1-txRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, - }, - { - .ctl_name = 151, - .procname = "arlan1-rxRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, - }, - { - .ctl_name = 152, - .procname = "arlan1-18", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info18, - }, - { - .ctl_name = 153, - .procname = "arlan1-ring", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, - }, - { - .ctl_name = 154, - .procname = "arlan1-shm-cpy", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info, - }, -#endif - { - .ctl_name = 155, - .procname = "config1", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_configure, - }, - { - .ctl_name = 156, - .procname = "reset1", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_sysctl_reset, - }, - { .ctl_name = 0 } -}; - -static ctl_table arlan_conf_table2[] = -{ - - ARLAN_SYSCTL_TABLE_TOTAL(2) - -#ifdef ARLAN_PROC_SHM_DUMP - { - .ctl_name = 150, - .procname = "arlan2-txRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, - }, - { - .ctl_name = 151, - .procname = "arlan2-rxRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, - }, - { - .ctl_name = 152, - .procname = "arlan2-18", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info18, - }, - { - .ctl_name = 153, - .procname = "arlan2-ring", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, - }, - { - .ctl_name = 154, - .procname = "arlan2-shm-cpy", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info, - }, -#endif - { - .ctl_name = 155, - .procname = "config2", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_configure, - }, - { - .ctl_name = 156, - .procname = "reset2", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_sysctl_reset, - }, - { .ctl_name = 0 } -}; - -static ctl_table arlan_conf_table3[] = -{ - - ARLAN_SYSCTL_TABLE_TOTAL(3) - -#ifdef ARLAN_PROC_SHM_DUMP - { - .ctl_name = 150, - .procname = "arlan3-txRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_infotxRing, - }, - { - .ctl_name = 151, - .procname = "arlan3-rxRing", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_inforxRing, - }, - { - .ctl_name = 152, - .procname = "arlan3-18", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info18, - }, - { - .ctl_name = 153, - .procname = "arlan3-ring", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info161719, - }, - { - .ctl_name = 154, - .procname = "arlan3-shm-cpy", - .data = &arlan_drive_info, - .maxlen = ARLAN_STR_SIZE, - .mode = 0400, - .proc_handler = &arlan_sysctl_info, - }, -#endif - { - .ctl_name = 155, - .procname = "config3", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_configure, - }, - { - .ctl_name = 156, - .procname = "reset3", - .data = &conf_reset_result, - .maxlen = 100, - .mode = 0400, - .proc_handler = &arlan_sysctl_reset, - }, - { .ctl_name = 0 } -}; - - - -static ctl_table arlan_table[] = -{ - { - .ctl_name = 0, - .procname = "arlan0", - .maxlen = 0, - .mode = 0600, - .child = arlan_conf_table0, - }, - { - .ctl_name = 0, - .procname = "arlan1", - .maxlen = 0, - .mode = 0600, - .child = arlan_conf_table1, - }, - { - .ctl_name = 0, - .procname = "arlan2", - .maxlen = 0, - .mode = 0600, - .child = arlan_conf_table2, - }, - { - .ctl_name = 0, - .procname = "arlan3", - .maxlen = 0, - .mode = 0600, - .child = arlan_conf_table3, - }, - { .ctl_name = 0 } -}; - -#else - -static ctl_table arlan_table[MAX_ARLANS + 1] = -{ - { .ctl_name = 0 } -}; -#endif - - -// static int mmtu = 1234; - -static ctl_table arlan_root_table[] = -{ - { - .ctl_name = CTL_ARLAN, - .procname = "arlan", - .maxlen = 0, - .mode = 0555, - .child = arlan_table, - }, - { .ctl_name = 0 } -}; - -/* Make sure that /proc/sys/dev is there */ -//static ctl_table arlan_device_root_table[] = -//{ -// {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table}, -// {0} -//}; - - -static struct ctl_table_header *arlan_device_sysctl_header; - -int __init init_arlan_proc(void) -{ - - int i = 0; - if (arlan_device_sysctl_header) - return 0; - for (i = 0; i < MAX_ARLANS && arlan_device[i]; i++) - arlan_table[i].ctl_name = i + 1; - arlan_device_sysctl_header = register_sysctl_table(arlan_root_table); - if (!arlan_device_sysctl_header) - return -1; - - return 0; - -} - -void __exit cleanup_arlan_proc(void) -{ - unregister_sysctl_table(arlan_device_sysctl_header); - arlan_device_sysctl_header = NULL; - -} -#endif diff --git a/drivers/net/wireless/arlan.h b/drivers/net/wireless/arlan.h deleted file mode 100644 index fb3ad51a1caf..000000000000 --- a/drivers/net/wireless/arlan.h +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (C) 1997 Cullen Jennings - * Copyright (C) 1998 Elmer.Joandi@ut.ee, +37-255-13500 - * GNU General Public License applies - */ - -#include -#include -#include -#include -#include /* For the statistics structure. */ -#include /* For ARPHRD_ETHER */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -//#define ARLAN_DEBUGGING 1 - -#define ARLAN_PROC_INTERFACE -#define MAX_ARLANS 4 /* not more than 4 ! */ -#define ARLAN_PROC_SHM_DUMP /* shows all card registers, makes driver way larger */ - -#define ARLAN_MAX_MULTICAST_ADDRS 16 -#define ARLAN_RCV_CLEAN 0 -#define ARLAN_RCV_PROMISC 1 -#define ARLAN_RCV_CONTROL 2 - -#ifdef CONFIG_PROC_FS -extern int init_arlan_proc(void); -extern void cleanup_arlan_proc(void); -#else -#define init_arlan_proc() ({ 0; }) -#define cleanup_arlan_proc() do { } while (0) -#endif - -extern struct net_device *arlan_device[MAX_ARLANS]; -extern int arlan_debug; -extern int arlan_entry_debug; -extern int arlan_exit_debug; -extern int testMemory; -extern int arlan_command(struct net_device * dev, int command); - -#define SIDUNKNOWN -1 -#define radioNodeIdUNKNOWN -1 -#define irqUNKNOWN 0 -#define debugUNKNOWN 0 -#define testMemoryUNKNOWN 1 -#define spreadingCodeUNKNOWN 0 -#define channelNumberUNKNOWN 0 -#define channelSetUNKNOWN 0 -#define systemIdUNKNOWN -1 -#define registrationModeUNKNOWN -1 - - -#define IFDEBUG( L ) if ( (L) & arlan_debug ) -#define ARLAN_FAKE_HDR_LEN 12 - -#ifdef ARLAN_DEBUGGING - #define DEBUG 1 - #define ARLAN_ENTRY_EXIT_DEBUGGING 1 - #define ARLAN_DEBUG(a,b) printk(KERN_DEBUG a, b) -#else - #define ARLAN_DEBUG(a,b) -#endif - -#define ARLAN_SHMEM_SIZE 0x2000 - -struct arlan_shmem -{ - /* Header Signature */ - volatile char textRegion[48]; - volatile u_char resetFlag; - volatile u_char diagnosticInfo; - volatile u_short diagnosticOffset; - volatile u_char _1[12]; - volatile u_char lanCardNodeId[6]; - volatile u_char broadcastAddress[6]; - volatile u_char hardwareType; - volatile u_char majorHardwareVersion; - volatile u_char minorHardwareVersion; - volatile u_char radioModule;// shows EEPROM, can be overridden at 0x111 - volatile u_char defaultChannelSet; // shows EEProm, can be overriiden at 0x10A - volatile u_char _2[47]; - - /* Control/Status Block - 0x0080 */ - volatile u_char interruptInProgress; /* not used by lancpu */ - volatile u_char cntrlRegImage; /* not used by lancpu */ - volatile u_char _3[13]; - volatile u_char dumpByte; - volatile u_char commandByte; /* non-zero = active */ - volatile u_char commandParameter[15]; - - /* Receive Status - 0x00a0 */ - volatile u_char rxStatus; /* 1- data, 2-control, 0xff - registr change */ - volatile u_char rxFrmType; - volatile u_short rxOffset; - volatile u_short rxLength; - volatile u_char rxSrc[6]; - volatile u_char rxBroadcastFlag; - volatile u_char rxQuality; - volatile u_char scrambled; - volatile u_char _4[1]; - - /* Transmit Status - 0x00b0 */ - volatile u_char txStatus; - volatile u_char txAckQuality; - volatile u_char numRetries; - volatile u_char _5[14]; - volatile u_char registeredRouter[6]; - volatile u_char backboneRouter[6]; - volatile u_char registrationStatus; - volatile u_char configuredStatusFlag; - volatile u_char _6[1]; - volatile u_char ultimateDestAddress[6]; - volatile u_char immedDestAddress[6]; - volatile u_char immedSrcAddress[6]; - volatile u_short rxSequenceNumber; - volatile u_char assignedLocaltalkAddress; - volatile u_char _7[27]; - - /* System Parameter Block */ - - /* - Driver Parameters (Novell Specific) */ - - volatile u_short txTimeout; - volatile u_short transportTime; - volatile u_char _8[4]; - - /* - Configuration Parameters */ - volatile u_char irqLevel; - volatile u_char spreadingCode; - volatile u_char channelSet; - volatile u_char channelNumber; - volatile u_short radioNodeId; - volatile u_char _9[2]; - volatile u_char scramblingDisable; - volatile u_char radioType; - volatile u_short routerId; - volatile u_char _10[9]; - volatile u_char txAttenuation; - volatile u_char systemId[4]; - volatile u_short globalChecksum; - volatile u_char _11[4]; - volatile u_short maxDatagramSize; - volatile u_short maxFrameSize; - volatile u_char maxRetries; - volatile u_char receiveMode; - volatile u_char priority; - volatile u_char rootOrRepeater; - volatile u_char specifiedRouter[6]; - volatile u_short fastPollPeriod; - volatile u_char pollDecay; - volatile u_char fastPollDelay[2]; - volatile u_char arlThreshold; - volatile u_char arlDecay; - volatile u_char _12[1]; - volatile u_short specRouterTimeout; - volatile u_char _13[5]; - - /* Scrambled Area */ - volatile u_char SID[4]; - volatile u_char encryptionKey[12]; - volatile u_char _14[2]; - volatile u_char waitTime[2]; - volatile u_char lParameter[2]; - volatile u_char _15[3]; - volatile u_short headerSize; - volatile u_short sectionChecksum; - - volatile u_char registrationMode; - volatile u_char registrationFill; - volatile u_short pollPeriod; - volatile u_short refreshPeriod; - volatile u_char name[16]; - volatile u_char NID[6]; - volatile u_char localTalkAddress; - volatile u_char codeFormat; - volatile u_char numChannels; - volatile u_char channel1; - volatile u_char channel2; - volatile u_char channel3; - volatile u_char channel4; - volatile u_char SSCode[59]; - - volatile u_char _16[0xC0]; - volatile u_short auxCmd; - volatile u_char dumpPtr[4]; - volatile u_char dumpVal; - volatile u_char _17[0x6A]; - volatile u_char wireTest; - volatile u_char _18[14]; - - /* Statistics Block - 0x0300 */ - volatile u_char hostcpuLock; - volatile u_char lancpuLock; - volatile u_char resetTime[18]; - - volatile u_char numDatagramsTransmitted[4]; - volatile u_char numReTransmissions[4]; - volatile u_char numFramesDiscarded[4]; - volatile u_char numDatagramsReceived[4]; - volatile u_char numDuplicateReceivedFrames[4]; - volatile u_char numDatagramsDiscarded[4]; - - volatile u_short maxNumReTransmitDatagram; - volatile u_short maxNumReTransmitFrames; - volatile u_short maxNumConsecutiveDuplicateFrames; - /* misaligned here so we have to go to characters */ - - volatile u_char numBytesTransmitted[4]; - volatile u_char numBytesReceived[4]; - volatile u_char numCRCErrors[4]; - volatile u_char numLengthErrors[4]; - volatile u_char numAbortErrors[4]; - volatile u_char numTXUnderruns[4]; - volatile u_char numRXOverruns[4]; - volatile u_char numHoldOffs[4]; - volatile u_char numFramesTransmitted[4]; - volatile u_char numFramesReceived[4]; - volatile u_char numReceiveFramesLost[4]; - volatile u_char numRXBufferOverflows[4]; - volatile u_char numFramesDiscardedAddrMismatch[4]; - volatile u_char numFramesDiscardedSIDMismatch[4]; - volatile u_char numPollsTransmistted[4]; - volatile u_char numPollAcknowledges[4]; - volatile u_char numStatusTimeouts[4]; - volatile u_char numNACKReceived[4]; - - volatile u_char _19[0x86]; - - volatile u_char txBuffer[0x800]; - volatile u_char rxBuffer[0x800]; - - volatile u_char _20[0x800]; - volatile u_char _21[0x3fb]; - volatile u_char configStatus; - volatile u_char _22; - volatile u_char progIOCtrl; - volatile u_char shareMBase; - volatile u_char controlRegister; -}; - -struct arlan_conf_stru { - int spreadingCode; - int channelSet; - int channelNumber; - int scramblingDisable; - int txAttenuation; - int systemId; - int maxDatagramSize; - int maxFrameSize; - int maxRetries; - int receiveMode; - int priority; - int rootOrRepeater; - int SID; - int radioNodeId; - int registrationMode; - int registrationFill; - int localTalkAddress; - int codeFormat; - int numChannels; - int channel1; - int channel2; - int channel3; - int channel4; - int txClear; - int txRetries; - int txRouting; - int txScrambled; - int rxParameter; - int txTimeoutMs; - int txAckTimeoutMs; - int waitCardTimeout; - int waitTime; - int lParameter; - int _15; - int headerSize; - int retries; - int tx_delay_ms; - int waitReTransmitPacketMaxSize; - int ReTransmitPacketMaxSize; - int fastReTransCount; - int driverRetransmissions; - int registrationInterrupts; - int hardwareType; - int radioType; - int writeRadioType; - int writeEEPROM; - char siteName[17]; - int measure_rate; - int in_speed; - int out_speed; - int in_speed10; - int out_speed10; - int in_speed_max; - int out_speed_max; - int pre_Command_Wait; - int rx_tweak1; - int rx_tweak2; - int tx_queue_len; -}; - -extern struct arlan_conf_stru arlan_conf[MAX_ARLANS]; - -struct TxParam -{ - volatile short offset; - volatile short length; - volatile u_char dest[6]; - volatile unsigned char clear; - volatile unsigned char retries; - volatile unsigned char routing; - volatile unsigned char scrambled; -}; - -#define TX_RING_SIZE 2 -/* Information that need to be kept for each board. */ -struct arlan_private { - struct arlan_shmem __iomem * card; - struct arlan_shmem * conf; - - struct arlan_conf_stru * Conf; - int bad; - int reset; - unsigned long lastReset; - struct timer_list timer; - struct timer_list tx_delay_timer; - struct timer_list tx_retry_timer; - struct timer_list rx_check_timer; - - int registrationLostCount; - int reRegisterExp; - int irq_test_done; - - struct TxParam txRing[TX_RING_SIZE]; - char reTransmitBuff[0x800]; - int txLast; - unsigned ReTransmitRequested; - unsigned long tx_done_delayed; - unsigned long registrationLastSeen; - - unsigned long tx_last_sent; - unsigned long tx_last_cleared; - unsigned long retransmissions; - unsigned long interrupt_ack_requested; - spinlock_t lock; - unsigned long waiting_command_mask; - unsigned long card_polling_interval; - unsigned long last_command_buff_free_time; - - int under_reset; - int under_config; - int rx_command_given; - int tx_command_given; - unsigned long interrupt_processing_active; - unsigned long last_rx_int_ack_time; - unsigned long in_bytes; - unsigned long out_bytes; - unsigned long in_time; - unsigned long out_time; - unsigned long in_time10; - unsigned long out_time10; - unsigned long in_bytes10; - unsigned long out_bytes10; - int init_etherdev_alloc; -}; - - - -#define ARLAN_CLEAR 0x00 -#define ARLAN_RESET 0x01 -#define ARLAN_CHANNEL_ATTENTION 0x02 -#define ARLAN_INTERRUPT_ENABLE 0x04 -#define ARLAN_CLEAR_INTERRUPT 0x08 -#define ARLAN_POWER 0x40 -#define ARLAN_ACCESS 0x80 - -#define ARLAN_COM_CONF 0x01 -#define ARLAN_COM_RX_ENABLE 0x03 -#define ARLAN_COM_RX_ABORT 0x04 -#define ARLAN_COM_TX_ENABLE 0x05 -#define ARLAN_COM_TX_ABORT 0x06 -#define ARLAN_COM_NOP 0x07 -#define ARLAN_COM_STANDBY 0x08 -#define ARLAN_COM_ACTIVATE 0x09 -#define ARLAN_COM_GOTO_SLOW_POLL 0x0a -#define ARLAN_COM_INT 0x80 - - -#define TXLAST(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[((struct arlan_private *)netdev_priv(dev))->txLast]) -#define TXHEAD(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[0]) -#define TXTAIL(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[1]) - -#define TXBuffStart(dev) offsetof(struct arlan_shmem, txBuffer) -#define TXBuffEnd(dev) offsetof(struct arlan_shmem, xxBuffer) - -#define READSHM(to,from,atype) {\ - atype tmp;\ - memcpy_fromio(&(tmp),&(from),sizeof(atype));\ - to = tmp;\ - } - -#define READSHMEM(from,atype)\ - atype from; \ - READSHM(from, arlan->from, atype); - -#define WRITESHM(to,from,atype) \ - { atype tmpSHM = from;\ - memcpy_toio(&(to),&tmpSHM,sizeof(atype));\ - } - -#define DEBUGSHM(levelSHM,stringSHM,stuff,atype) \ - { atype tmpSHM; \ - memcpy_fromio(&tmpSHM,&(stuff),sizeof(atype));\ - IFDEBUG(levelSHM) printk(stringSHM,tmpSHM);\ - } - -#define WRITESHMB(to, val) \ - writeb(val,&(to)) -#define READSHMB(to) \ - readb(&(to)) -#define WRITESHMS(to, val) \ - writew(val,&(to)) -#define READSHMS(to) \ - readw(&(to)) -#define WRITESHMI(to, val) \ - writel(val,&(to)) -#define READSHMI(to) \ - readl(&(to)) - - - - - -#define registrationBad(dev)\ - ( ( READSHMB(((struct arlan_private *)netdev_priv(dev))->card->registrationMode) > 0) && \ - ( READSHMB(((struct arlan_private *)netdev_priv(dev))->card->registrationStatus) == 0) ) - - -#define readControlRegister(dev)\ - READSHMB(((struct arlan_private *)netdev_priv(dev))->card->cntrlRegImage) - -#define writeControlRegister(dev, v){\ - WRITESHMB(((struct arlan_private *)netdev_priv(dev))->card->cntrlRegImage ,((v) &0xF) );\ - WRITESHMB(((struct arlan_private *)netdev_priv(dev))->card->controlRegister ,(v) );} - - -#define arlan_interrupt_lancpu(dev) {\ - int cr; \ - \ - cr = readControlRegister(dev);\ - if (cr & ARLAN_CHANNEL_ATTENTION){ \ - writeControlRegister(dev, (cr & ~ARLAN_CHANNEL_ATTENTION));\ - }else \ - writeControlRegister(dev, (cr | ARLAN_CHANNEL_ATTENTION));\ -} - -#define clearChannelAttention(dev){ \ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_CHANNEL_ATTENTION);} -#define setHardwareReset(dev) {\ - writeControlRegister(dev,readControlRegister(dev) | ARLAN_RESET);} -#define clearHardwareReset(dev) {\ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_RESET);} -#define setInterruptEnable(dev){\ - writeControlRegister(dev,readControlRegister(dev) | ARLAN_INTERRUPT_ENABLE) ;} -#define clearInterruptEnable(dev){\ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_INTERRUPT_ENABLE) ;} -#define setClearInterrupt(dev){\ - writeControlRegister(dev,readControlRegister(dev) | ARLAN_CLEAR_INTERRUPT) ;} -#define clearClearInterrupt(dev){\ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_CLEAR_INTERRUPT);} -#define setPowerOff(dev){\ - writeControlRegister(dev,readControlRegister(dev) | (ARLAN_POWER && ARLAN_ACCESS));\ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_ACCESS);} -#define setPowerOn(dev){\ - writeControlRegister(dev,readControlRegister(dev) & ~(ARLAN_POWER)); } -#define arlan_lock_card_access(dev){\ - writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_ACCESS);} -#define arlan_unlock_card_access(dev){\ - writeControlRegister(dev,readControlRegister(dev) | ARLAN_ACCESS ); } - - - - -#define ARLAN_COMMAND_RX 0x000001 -#define ARLAN_COMMAND_NOOP 0x000002 -#define ARLAN_COMMAND_NOOPINT 0x000004 -#define ARLAN_COMMAND_TX 0x000008 -#define ARLAN_COMMAND_CONF 0x000010 -#define ARLAN_COMMAND_RESET 0x000020 -#define ARLAN_COMMAND_TX_ABORT 0x000040 -#define ARLAN_COMMAND_RX_ABORT 0x000080 -#define ARLAN_COMMAND_POWERDOWN 0x000100 -#define ARLAN_COMMAND_POWERUP 0x000200 -#define ARLAN_COMMAND_SLOW_POLL 0x000400 -#define ARLAN_COMMAND_ACTIVATE 0x000800 -#define ARLAN_COMMAND_INT_ACK 0x001000 -#define ARLAN_COMMAND_INT_ENABLE 0x002000 -#define ARLAN_COMMAND_WAIT_NOW 0x004000 -#define ARLAN_COMMAND_LONG_WAIT_NOW 0x008000 -#define ARLAN_COMMAND_STANDBY 0x010000 -#define ARLAN_COMMAND_INT_RACK 0x020000 -#define ARLAN_COMMAND_INT_RENABLE 0x040000 -#define ARLAN_COMMAND_CONF_WAIT 0x080000 -#define ARLAN_COMMAND_TBUSY_CLEAR 0x100000 -#define ARLAN_COMMAND_CLEAN_AND_CONF (ARLAN_COMMAND_TX_ABORT\ - | ARLAN_COMMAND_RX_ABORT\ - | ARLAN_COMMAND_CONF) -#define ARLAN_COMMAND_CLEAN_AND_RESET (ARLAN_COMMAND_TX_ABORT\ - | ARLAN_COMMAND_RX_ABORT\ - | ARLAN_COMMAND_RESET) - - - -#define ARLAN_DEBUG_CHAIN_LOCKS 0x00001 -#define ARLAN_DEBUG_RESET 0x00002 -#define ARLAN_DEBUG_TIMING 0x00004 -#define ARLAN_DEBUG_CARD_STATE 0x00008 -#define ARLAN_DEBUG_TX_CHAIN 0x00010 -#define ARLAN_DEBUG_MULTICAST 0x00020 -#define ARLAN_DEBUG_HEADER_DUMP 0x00040 -#define ARLAN_DEBUG_INTERRUPT 0x00080 -#define ARLAN_DEBUG_STARTUP 0x00100 -#define ARLAN_DEBUG_SHUTDOWN 0x00200 - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7ddc5c8d8956..ca946ca3823b 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -133,5 +133,7 @@ source "drivers/staging/cowloop/Kconfig" source "drivers/staging/strip/Kconfig" +source "drivers/staging/arlan/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 4386a620e9b2..ed92324178bd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -48,3 +48,4 @@ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_COWLOOP) += cowloop/ obj-$(CONFIG_STRIP) += strip/ +obj-$(CONFIG_ARLAN) += arlan/ diff --git a/drivers/staging/arlan/Kconfig b/drivers/staging/arlan/Kconfig new file mode 100644 index 000000000000..0585ed8b4d3e --- /dev/null +++ b/drivers/staging/arlan/Kconfig @@ -0,0 +1,15 @@ +config ARLAN + tristate "Aironet Arlan 655 & IC2200 DS support" + depends on ISA && !64BIT + select WIRELESS_EXT + ---help--- + Aironet makes Arlan, a class of wireless LAN adapters. These use the + www.Telxon.com chip, which is also used on several similar cards. + This driver is tested on the 655 and IC2200 series cards. Look at + for the latest information. + + The driver is built as two modules, arlan and arlan-proc. The latter + is the /proc interface and is not needed most of time. + + On some computers the card ends up in non-valid state after some + time. Use a ping-reset script to clear it. diff --git a/drivers/staging/arlan/Makefile b/drivers/staging/arlan/Makefile new file mode 100644 index 000000000000..9e58e5fae7b9 --- /dev/null +++ b/drivers/staging/arlan/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_ARLAN) += arlan.o + +arlan-objs := arlan-main.o arlan-proc.o diff --git a/drivers/staging/arlan/arlan-main.c b/drivers/staging/arlan/arlan-main.c new file mode 100644 index 000000000000..921a082487a1 --- /dev/null +++ b/drivers/staging/arlan/arlan-main.c @@ -0,0 +1,1887 @@ +/* + * Copyright (C) 1997 Cullen Jennings + * Copyright (C) 1998 Elmer Joandiu, elmer@ylenurme.ee + * GNU General Public License applies + * This module provides support for the Arlan 655 card made by Aironet + */ + +#include "arlan.h" + +#if BITS_PER_LONG != 32 +# error FIXME: this driver requires a 32-bit platform +#endif + +static const char *arlan_version = "C.Jennigs 97 & Elmer.Joandi@ut.ee Oct'98, http://www.ylenurme.ee/~elmer/655/"; + +struct net_device *arlan_device[MAX_ARLANS]; + +static int SID = SIDUNKNOWN; +static int radioNodeId = radioNodeIdUNKNOWN; +static char encryptionKey[12] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; +int arlan_debug = debugUNKNOWN; +static int spreadingCode = spreadingCodeUNKNOWN; +static int channelNumber = channelNumberUNKNOWN; +static int channelSet = channelSetUNKNOWN; +static int systemId = systemIdUNKNOWN; +static int registrationMode = registrationModeUNKNOWN; +static int keyStart; +static int tx_delay_ms; +static int retries = 5; +static int tx_queue_len = 1; +static int arlan_EEPROM_bad; + +#ifdef ARLAN_DEBUGGING + +static int testMemory = testMemoryUNKNOWN; +static int irq = irqUNKNOWN; +static int txScrambled = 1; +static int mdebug; + +module_param(irq, int, 0); +module_param(mdebug, int, 0); +module_param(testMemory, int, 0); +module_param(txScrambled, int, 0); +MODULE_PARM_DESC(irq, "(unused)"); +MODULE_PARM_DESC(testMemory, "(unused)"); +MODULE_PARM_DESC(mdebug, "Arlan multicast debugging (0-1)"); +#endif + +module_param_named(debug, arlan_debug, int, 0); +module_param(spreadingCode, int, 0); +module_param(channelNumber, int, 0); +module_param(channelSet, int, 0); +module_param(systemId, int, 0); +module_param(registrationMode, int, 0); +module_param(radioNodeId, int, 0); +module_param(SID, int, 0); +module_param(keyStart, int, 0); +module_param(tx_delay_ms, int, 0); +module_param(retries, int, 0); +module_param(tx_queue_len, int, 0); +module_param_named(EEPROM_bad, arlan_EEPROM_bad, int, 0); +MODULE_PARM_DESC(debug, "Arlan debug enable (0-1)"); +MODULE_PARM_DESC(retries, "Arlan maximum packet retransmisions"); +#ifdef ARLAN_ENTRY_EXIT_DEBUGGING +static int arlan_entry_debug; +static int arlan_exit_debug; +static int arlan_entry_and_exit_debug; +module_param_named(entry_debug, arlan_entry_debug, int, 0); +module_param_named(exit_debug, arlan_exit_debug, int, 0); +module_param_named(entry_and_exit_debug, arlan_entry_and_exit_debug, int, 0); +MODULE_PARM_DESC(entry_debug, "Arlan driver function entry debugging"); +MODULE_PARM_DESC(exit_debug, "Arlan driver function exit debugging"); +MODULE_PARM_DESC(entry_and_exit_debug, "Arlan driver function entry and exit debugging"); +#endif + +struct arlan_conf_stru arlan_conf[MAX_ARLANS]; +static int arlans_found; + +static int arlan_open(struct net_device *dev); +static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t arlan_interrupt(int irq, void *dev_id); +static int arlan_close(struct net_device *dev); +static struct net_device_stats * + arlan_statistics (struct net_device *dev); +static void arlan_set_multicast (struct net_device *dev); +static int arlan_hw_tx (struct net_device* dev, char *buf, int length ); +static int arlan_hw_config (struct net_device * dev); +static void arlan_tx_done_interrupt (struct net_device * dev, int status); +static void arlan_rx_interrupt (struct net_device * dev, u_char rxStatus, u_short, u_short); +static void arlan_process_interrupt (struct net_device * dev); +static void arlan_tx_timeout (struct net_device *dev); + +static inline long us2ticks(int us) +{ + return us * (1000000 / HZ); +} + + +#ifdef ARLAN_ENTRY_EXIT_DEBUGGING +#define ARLAN_DEBUG_ENTRY(name) \ + {\ + struct timeval timev;\ + do_gettimeofday(&timev);\ + if (arlan_entry_debug || arlan_entry_and_exit_debug)\ + printk("--->>>" name " %ld " "\n",((long int) timev.tv_sec * 1000000 + timev.tv_usec));\ + } +#define ARLAN_DEBUG_EXIT(name) \ + {\ + struct timeval timev;\ + do_gettimeofday(&timev);\ + if (arlan_exit_debug || arlan_entry_and_exit_debug)\ + printk("<<<---" name " %ld " "\n",((long int) timev.tv_sec * 1000000 + timev.tv_usec) );\ + } +#else +#define ARLAN_DEBUG_ENTRY(name) +#define ARLAN_DEBUG_EXIT(name) +#endif + + +#define arlan_interrupt_ack(dev)\ + clearClearInterrupt(dev);\ + setClearInterrupt(dev); + +static inline int arlan_drop_tx(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + dev->stats.tx_errors++; + if (priv->Conf->tx_delay_ms) + { + priv->tx_done_delayed = jiffies + priv->Conf->tx_delay_ms * HZ / 1000 + 1; + } + else + { + priv->waiting_command_mask &= ~ARLAN_COMMAND_TX; + TXHEAD(dev).offset = 0; + TXTAIL(dev).offset = 0; + priv->txLast = 0; + priv->bad = 0; + if (!priv->under_reset && !priv->under_config) + netif_wake_queue (dev); + } + return 1; +} + + +int arlan_command(struct net_device *dev, int command_p) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + int udelayed = 0; + int i = 0; + unsigned long flags; + + ARLAN_DEBUG_ENTRY("arlan_command"); + + if (priv->card_polling_interval) + priv->card_polling_interval = 1; + + if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) + printk(KERN_DEBUG "arlan_command, %lx commandByte %x waiting %lx incoming %x \n", + jiffies, READSHMB(arlan->commandByte), + priv->waiting_command_mask, command_p); + + priv->waiting_command_mask |= command_p; + + if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) + if (time_after(jiffies, priv->lastReset + 5 * HZ)) + priv->waiting_command_mask &= ~ARLAN_COMMAND_RESET; + + if (priv->waiting_command_mask & ARLAN_COMMAND_INT_ACK) + { + arlan_interrupt_ack(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_ACK; + } + if (priv->waiting_command_mask & ARLAN_COMMAND_INT_ENABLE) + { + setInterruptEnable(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_ENABLE; + } + + /* Card access serializing lock */ + spin_lock_irqsave(&priv->lock, flags); + + /* Check cards status and waiting */ + + if (priv->waiting_command_mask & (ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW)) + { + while (priv->waiting_command_mask & (ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW)) + { + if (READSHMB(arlan->resetFlag) || + READSHMB(arlan->commandByte)) /* || + (readControlRegister(dev) & ARLAN_ACCESS)) + */ + udelay(40); + else + priv->waiting_command_mask &= ~(ARLAN_COMMAND_LONG_WAIT_NOW | ARLAN_COMMAND_WAIT_NOW); + + udelayed++; + + if (priv->waiting_command_mask & ARLAN_COMMAND_LONG_WAIT_NOW) + { + if (udelayed * 40 > 1000000) + { + printk(KERN_ERR "%s long wait too long \n", dev->name); + priv->waiting_command_mask |= ARLAN_COMMAND_RESET; + break; + } + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_WAIT_NOW) + { + if (udelayed * 40 > 1000) + { + printk(KERN_ERR "%s short wait too long \n", dev->name); + goto bad_end; + } + } + } + } + else + { + i = 0; + while ((READSHMB(arlan->resetFlag) || + READSHMB(arlan->commandByte)) && + conf->pre_Command_Wait > (i++) * 10) + udelay(10); + + + if ((READSHMB(arlan->resetFlag) || + READSHMB(arlan->commandByte)) && + !(priv->waiting_command_mask & ARLAN_COMMAND_RESET)) + { + goto card_busy_end; + } + } + if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) + priv->under_reset = 1; + if (priv->waiting_command_mask & ARLAN_COMMAND_CONF) + priv->under_config = 1; + + /* Issuing command */ + arlan_lock_card_access(dev); + if (priv->waiting_command_mask & ARLAN_COMMAND_POWERUP) + { + // if (readControlRegister(dev) & (ARLAN_ACCESS && ARLAN_POWER)) + setPowerOn(dev); + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_POWERUP; + priv->waiting_command_mask |= ARLAN_COMMAND_RESET; + priv->card_polling_interval = HZ / 10; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_ACTIVATE) + { + WRITESHMB(arlan->commandByte, ARLAN_COM_ACTIVATE); + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_ACTIVATE; + priv->card_polling_interval = HZ / 10; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_RX_ABORT) + { + if (priv->rx_command_given) + { + WRITESHMB(arlan->commandByte, ARLAN_COM_RX_ABORT); + arlan_interrupt_lancpu(dev); + priv->rx_command_given = 0; + } + priv->waiting_command_mask &= ~ARLAN_COMMAND_RX_ABORT; + priv->card_polling_interval = 1; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_TX_ABORT) + { + if (priv->tx_command_given) + { + WRITESHMB(arlan->commandByte, ARLAN_COM_TX_ABORT); + arlan_interrupt_lancpu(dev); + priv->tx_command_given = 0; + } + priv->waiting_command_mask &= ~ARLAN_COMMAND_TX_ABORT; + priv->card_polling_interval = 1; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_RESET) + { + priv->under_reset=1; + netif_stop_queue (dev); + + arlan_drop_tx(dev); + if (priv->tx_command_given || priv->rx_command_given) + { + printk(KERN_ERR "%s: Reset under tx or rx command \n", dev->name); + } + netif_stop_queue (dev); + if (arlan_debug & ARLAN_DEBUG_RESET) + printk(KERN_ERR "%s: Doing chip reset\n", dev->name); + priv->lastReset = jiffies; + WRITESHM(arlan->commandByte, 0, u_char); + /* hold card in reset state */ + setHardwareReset(dev); + /* set reset flag and then release reset */ + WRITESHM(arlan->resetFlag, 0xff, u_char); + clearChannelAttention(dev); + clearHardwareReset(dev); + priv->card_polling_interval = HZ / 4; + priv->waiting_command_mask &= ~ARLAN_COMMAND_RESET; + priv->waiting_command_mask |= ARLAN_COMMAND_INT_RACK; +// priv->waiting_command_mask |= ARLAN_COMMAND_INT_RENABLE; +// priv->waiting_command_mask |= ARLAN_COMMAND_RX; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_INT_RACK) + { + clearHardwareReset(dev); + clearClearInterrupt(dev); + setClearInterrupt(dev); + setInterruptEnable(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_RACK; + priv->waiting_command_mask |= ARLAN_COMMAND_CONF; + priv->under_config = 1; + priv->under_reset = 0; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_INT_RENABLE) + { + setInterruptEnable(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_INT_RENABLE; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_CONF) + { + if (priv->tx_command_given || priv->rx_command_given) + { + printk(KERN_ERR "%s: Reset under tx or rx command \n", dev->name); + } + arlan_drop_tx(dev); + setInterruptEnable(dev); + arlan_hw_config(dev); + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_CONF; + priv->card_polling_interval = HZ / 10; +// priv->waiting_command_mask |= ARLAN_COMMAND_INT_RACK; +// priv->waiting_command_mask |= ARLAN_COMMAND_INT_ENABLE; + priv->waiting_command_mask |= ARLAN_COMMAND_CONF_WAIT; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_CONF_WAIT) + { + if (READSHMB(arlan->configuredStatusFlag) != 0 && + READSHMB(arlan->diagnosticInfo) == 0xff) + { + priv->waiting_command_mask &= ~ARLAN_COMMAND_CONF_WAIT; + priv->waiting_command_mask |= ARLAN_COMMAND_RX; + priv->waiting_command_mask |= ARLAN_COMMAND_TBUSY_CLEAR; + priv->card_polling_interval = HZ / 10; + priv->tx_command_given = 0; + priv->under_config = 0; + } + else + { + priv->card_polling_interval = 1; + if (arlan_debug & ARLAN_DEBUG_TIMING) + printk(KERN_ERR "configure delayed \n"); + } + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_RX) + { + if (!registrationBad(dev)) + { + setInterruptEnable(dev); + memset_io(arlan->commandParameter, 0, 0xf); + WRITESHMB(arlan->commandByte, ARLAN_COM_INT | ARLAN_COM_RX_ENABLE); + WRITESHMB(arlan->commandParameter[0], conf->rxParameter); + arlan_interrupt_lancpu(dev); + priv->rx_command_given = 0; // mnjah, bad + priv->waiting_command_mask &= ~ARLAN_COMMAND_RX; + priv->card_polling_interval = 1; + } + else + priv->card_polling_interval = 2; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_TBUSY_CLEAR) + { + if ( !registrationBad(dev) && + (netif_queue_stopped(dev) || !netif_running(dev)) ) + { + priv->waiting_command_mask &= ~ARLAN_COMMAND_TBUSY_CLEAR; + netif_wake_queue (dev); + } + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_TX) + { + if (!test_and_set_bit(0, (void *) &priv->tx_command_given)) + { + if (time_after(jiffies, + priv->tx_last_sent + us2ticks(conf->rx_tweak1)) + || time_before(jiffies, + priv->last_rx_int_ack_time + us2ticks(conf->rx_tweak2))) + { + setInterruptEnable(dev); + memset_io(arlan->commandParameter, 0, 0xf); + WRITESHMB(arlan->commandByte, ARLAN_COM_TX_ENABLE | ARLAN_COM_INT); + memcpy_toio(arlan->commandParameter, &TXLAST(dev), 14); +// for ( i=1 ; i < 15 ; i++) printk("%02x:",READSHMB(arlan->commandParameter[i])); + priv->tx_last_sent = jiffies; + arlan_interrupt_lancpu(dev); + priv->tx_command_given = 1; + priv->waiting_command_mask &= ~ARLAN_COMMAND_TX; + priv->card_polling_interval = 1; + } + else + { + priv->tx_command_given = 0; + priv->card_polling_interval = 1; + } + } + else if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) + printk(KERN_ERR "tx command when tx chain locked \n"); + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_NOOPINT) + { + { + WRITESHMB(arlan->commandByte, ARLAN_COM_NOP | ARLAN_COM_INT); + } + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_NOOPINT; + priv->card_polling_interval = HZ / 3; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_NOOP) + { + WRITESHMB(arlan->commandByte, ARLAN_COM_NOP); + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_NOOP; + priv->card_polling_interval = HZ / 3; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_SLOW_POLL) + { + WRITESHMB(arlan->commandByte, ARLAN_COM_GOTO_SLOW_POLL); + arlan_interrupt_lancpu(dev); + priv->waiting_command_mask &= ~ARLAN_COMMAND_SLOW_POLL; + priv->card_polling_interval = HZ / 3; + } + else if (priv->waiting_command_mask & ARLAN_COMMAND_POWERDOWN) + { + setPowerOff(dev); + if (arlan_debug & ARLAN_DEBUG_CARD_STATE) + printk(KERN_WARNING "%s: Arlan Going Standby\n", dev->name); + priv->waiting_command_mask &= ~ARLAN_COMMAND_POWERDOWN; + priv->card_polling_interval = 3 * HZ; + } + arlan_unlock_card_access(dev); + for (i = 0; READSHMB(arlan->commandByte) && i < 20; i++) + udelay(10); + if (READSHMB(arlan->commandByte)) + if (arlan_debug & ARLAN_DEBUG_CARD_STATE) + printk(KERN_ERR "card busy leaving command %lx\n", priv->waiting_command_mask); + + spin_unlock_irqrestore(&priv->lock, flags); + ARLAN_DEBUG_EXIT("arlan_command"); + priv->last_command_buff_free_time = jiffies; + return 0; + +card_busy_end: + if (time_after(jiffies, priv->last_command_buff_free_time + HZ)) + priv->waiting_command_mask |= ARLAN_COMMAND_CLEAN_AND_RESET; + + if (arlan_debug & ARLAN_DEBUG_CARD_STATE) + printk(KERN_ERR "%s arlan_command card busy end \n", dev->name); + spin_unlock_irqrestore(&priv->lock, flags); + ARLAN_DEBUG_EXIT("arlan_command"); + return 1; + +bad_end: + printk(KERN_ERR "%s arlan_command bad end \n", dev->name); + + spin_unlock_irqrestore(&priv->lock, flags); + ARLAN_DEBUG_EXIT("arlan_command"); + + return -1; +} + +static inline void arlan_command_process(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + int times = 0; + while (priv->waiting_command_mask && times < 8) + { + if (priv->waiting_command_mask) + { + if (arlan_command(dev, 0)) + break; + times++; + } + /* if long command, we won't repeat trying */ ; + if (priv->card_polling_interval > 1) + break; + times++; + } +} + + +static inline void arlan_retransmit_now(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + + ARLAN_DEBUG_ENTRY("arlan_retransmit_now"); + if (TXLAST(dev).offset == 0) + { + if (TXHEAD(dev).offset) + { + priv->txLast = 0; + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_DEBUG "TX buff switch to head \n"); + + } + else if (TXTAIL(dev).offset) + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_DEBUG "TX buff switch to tail \n"); + priv->txLast = 1; + } + else + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_ERR "ReTransmit buff empty"); + netif_wake_queue (dev); + return; + + } + arlan_command(dev, ARLAN_COMMAND_TX); + + priv->Conf->driverRetransmissions++; + priv->retransmissions++; + + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk("Retransmit %d bytes \n", TXLAST(dev).length); + + ARLAN_DEBUG_EXIT("arlan_retransmit_now"); +} + + + +static void arlan_registration_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct arlan_private *priv = netdev_priv(dev); + int bh_mark_needed = 0; + int next_tick = 1; + long lostTime = ((long)jiffies - (long)priv->registrationLastSeen) + * (1000/HZ); + + if (registrationBad(dev)) + { + priv->registrationLostCount++; + if (lostTime > 7000 && lostTime < 7200) + { + printk(KERN_NOTICE "%s registration Lost \n", dev->name); + } + if (lostTime / priv->reRegisterExp > 2000) + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_CONF); + if (lostTime / (priv->reRegisterExp) > 3500) + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); + if (priv->reRegisterExp < 400) + priv->reRegisterExp += 2; + if (lostTime > 7200) + { + next_tick = HZ; + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); + } + } + else + { + if (priv->Conf->registrationMode && lostTime > 10000 && + priv->registrationLostCount) + { + printk(KERN_NOTICE "%s registration is back after %ld milliseconds\n", + dev->name, lostTime); + } + priv->registrationLastSeen = jiffies; + priv->registrationLostCount = 0; + priv->reRegisterExp = 1; + if (!netif_running(dev) ) + netif_wake_queue(dev); + if (time_after(priv->tx_last_sent,priv->tx_last_cleared) && + time_after(jiffies, priv->tx_last_sent * 5*HZ) ){ + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); + priv->tx_last_cleared = jiffies; + } + } + + + if (!registrationBad(dev) && priv->ReTransmitRequested) + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk(KERN_ERR "Retransmit from timer \n"); + priv->ReTransmitRequested = 0; + arlan_retransmit_now(dev); + } + if (!registrationBad(dev) && + time_after(jiffies, priv->tx_done_delayed) && + priv->tx_done_delayed != 0) + { + TXLAST(dev).offset = 0; + if (priv->txLast) + priv->txLast = 0; + else if (TXTAIL(dev).offset) + priv->txLast = 1; + if (TXLAST(dev).offset) + { + arlan_retransmit_now(dev); + dev->trans_start = jiffies; + } + if (!(TXHEAD(dev).offset && TXTAIL(dev).offset)) + { + netif_wake_queue (dev); + } + priv->tx_done_delayed = 0; + bh_mark_needed = 1; + } + if (bh_mark_needed) + { + netif_wake_queue (dev); + } + arlan_process_interrupt(dev); + + if (next_tick < priv->card_polling_interval) + next_tick = priv->card_polling_interval; + + priv->timer.expires = jiffies + next_tick; + + add_timer(&priv->timer); +} + + +#ifdef ARLAN_DEBUGGING + +static void arlan_print_registers(struct net_device *dev, int line) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem *arlan = priv->card; + + u_char hostcpuLock, lancpuLock, controlRegister, cntrlRegImage, + txStatus, rxStatus, interruptInProgress, commandByte; + + + ARLAN_DEBUG_ENTRY("arlan_print_registers"); + READSHM(interruptInProgress, arlan->interruptInProgress, u_char); + READSHM(hostcpuLock, arlan->hostcpuLock, u_char); + READSHM(lancpuLock, arlan->lancpuLock, u_char); + READSHM(controlRegister, arlan->controlRegister, u_char); + READSHM(cntrlRegImage, arlan->cntrlRegImage, u_char); + READSHM(txStatus, arlan->txStatus, u_char); + READSHM(rxStatus, arlan->rxStatus, u_char); + READSHM(commandByte, arlan->commandByte, u_char); + + printk(KERN_WARNING "line %04d IP %02x HL %02x LL %02x CB %02x CR %02x CRI %02x TX %02x RX %02x\n", + line, interruptInProgress, hostcpuLock, lancpuLock, commandByte, + controlRegister, cntrlRegImage, txStatus, rxStatus); + + ARLAN_DEBUG_EXIT("arlan_print_registers"); +} +#endif + + +static int arlan_hw_tx(struct net_device *dev, char *buf, int length) +{ + int i; + + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + + int tailStarts = 0x800; + int headEnds = 0x0; + + + ARLAN_DEBUG_ENTRY("arlan_hw_tx"); + if (TXHEAD(dev).offset) + headEnds = (((TXHEAD(dev).offset + TXHEAD(dev).length - offsetof(struct arlan_shmem, txBuffer)) / 64) + 1) * 64; + if (TXTAIL(dev).offset) + tailStarts = 0x800 - (((TXTAIL(dev).offset - offsetof(struct arlan_shmem, txBuffer)) / 64) + 2) * 64; + + + if (!TXHEAD(dev).offset && length < tailStarts) + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk(KERN_ERR "TXHEAD insert, tailStart %d\n", tailStarts); + + TXHEAD(dev).offset = + offsetof(struct arlan_shmem, txBuffer); + TXHEAD(dev).length = length - ARLAN_FAKE_HDR_LEN; + for (i = 0; i < 6; i++) + TXHEAD(dev).dest[i] = buf[i]; + TXHEAD(dev).clear = conf->txClear; + TXHEAD(dev).retries = conf->txRetries; /* 0 is use default */ + TXHEAD(dev).routing = conf->txRouting; + TXHEAD(dev).scrambled = conf->txScrambled; + memcpy_toio((char __iomem *)arlan + TXHEAD(dev).offset, buf + ARLAN_FAKE_HDR_LEN, TXHEAD(dev).length); + } + else if (!TXTAIL(dev).offset && length < (0x800 - headEnds)) + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk(KERN_ERR "TXTAIL insert, headEnd %d\n", headEnds); + + TXTAIL(dev).offset = + offsetof(struct arlan_shmem, txBuffer) + 0x800 - (length / 64 + 2) * 64; + TXTAIL(dev).length = length - ARLAN_FAKE_HDR_LEN; + for (i = 0; i < 6; i++) + TXTAIL(dev).dest[i] = buf[i]; + TXTAIL(dev).clear = conf->txClear; + TXTAIL(dev).retries = conf->txRetries; + TXTAIL(dev).routing = conf->txRouting; + TXTAIL(dev).scrambled = conf->txScrambled; + memcpy_toio(((char __iomem *)arlan + TXTAIL(dev).offset), buf + ARLAN_FAKE_HDR_LEN, TXTAIL(dev).length); + } + else + { + netif_stop_queue (dev); + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk(KERN_ERR "TX TAIL & HEAD full, return, tailStart %d headEnd %d\n", tailStarts, headEnds); + return -1; + } + priv->out_bytes += length; + priv->out_bytes10 += length; + if (conf->measure_rate < 1) + conf->measure_rate = 1; + if (time_after(jiffies, priv->out_time + conf->measure_rate * HZ)) + { + conf->out_speed = priv->out_bytes / conf->measure_rate; + priv->out_bytes = 0; + priv->out_time = jiffies; + } + if (time_after(jiffies, priv->out_time10 + conf->measure_rate * 10*HZ)) + { + conf->out_speed10 = priv->out_bytes10 / (10 * conf->measure_rate); + priv->out_bytes10 = 0; + priv->out_time10 = jiffies; + } + if (TXHEAD(dev).offset && TXTAIL(dev).offset) + { + netif_stop_queue (dev); + return 0; + } + else + netif_start_queue (dev); + + + IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) + printk(KERN_WARNING "%s Transmit t %2x:%2x:%2x:%2x:%2x:%2x f %2x:%2x:%2x:%2x:%2x:%2x \n", dev->name, + (unsigned char) buf[0], (unsigned char) buf[1], (unsigned char) buf[2], (unsigned char) buf[3], + (unsigned char) buf[4], (unsigned char) buf[5], (unsigned char) buf[6], (unsigned char) buf[7], + (unsigned char) buf[8], (unsigned char) buf[9], (unsigned char) buf[10], (unsigned char) buf[11]); + + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk(KERN_ERR "TX command prepare for buffer %d\n", priv->txLast); + + arlan_command(dev, ARLAN_COMMAND_TX); + + priv->tx_last_sent = jiffies; + + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) printk("%s TX Qued %d bytes \n", dev->name, length); + + ARLAN_DEBUG_EXIT("arlan_hw_tx"); + + return 0; +} + + +static int arlan_hw_config(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + + ARLAN_DEBUG_ENTRY("arlan_hw_config"); + + printk(KERN_NOTICE "%s arlan configure called \n", dev->name); + if (arlan_EEPROM_bad) + printk(KERN_NOTICE "arlan configure with eeprom bad option \n"); + + + WRITESHM(arlan->spreadingCode, conf->spreadingCode, u_char); + WRITESHM(arlan->channelSet, conf->channelSet, u_char); + + if (arlan_EEPROM_bad) + WRITESHM(arlan->defaultChannelSet, conf->channelSet, u_char); + + WRITESHM(arlan->channelNumber, conf->channelNumber, u_char); + + WRITESHM(arlan->scramblingDisable, conf->scramblingDisable, u_char); + WRITESHM(arlan->txAttenuation, conf->txAttenuation, u_char); + + WRITESHM(arlan->systemId, conf->systemId, u_int); + + WRITESHM(arlan->maxRetries, conf->maxRetries, u_char); + WRITESHM(arlan->receiveMode, conf->receiveMode, u_char); + WRITESHM(arlan->priority, conf->priority, u_char); + WRITESHM(arlan->rootOrRepeater, conf->rootOrRepeater, u_char); + WRITESHM(arlan->SID, conf->SID, u_int); + + WRITESHM(arlan->registrationMode, conf->registrationMode, u_char); + + WRITESHM(arlan->registrationFill, conf->registrationFill, u_char); + WRITESHM(arlan->localTalkAddress, conf->localTalkAddress, u_char); + WRITESHM(arlan->codeFormat, conf->codeFormat, u_char); + WRITESHM(arlan->numChannels, conf->numChannels, u_char); + WRITESHM(arlan->channel1, conf->channel1, u_char); + WRITESHM(arlan->channel2, conf->channel2, u_char); + WRITESHM(arlan->channel3, conf->channel3, u_char); + WRITESHM(arlan->channel4, conf->channel4, u_char); + WRITESHM(arlan->radioNodeId, conf->radioNodeId, u_short); + WRITESHM(arlan->SID, conf->SID, u_int); + WRITESHM(arlan->waitTime, conf->waitTime, u_short); + WRITESHM(arlan->lParameter, conf->lParameter, u_short); + memcpy_toio(&(arlan->_15), &(conf->_15), 3); + WRITESHM(arlan->_15, conf->_15, u_short); + WRITESHM(arlan->headerSize, conf->headerSize, u_short); + if (arlan_EEPROM_bad) + WRITESHM(arlan->hardwareType, conf->hardwareType, u_char); + WRITESHM(arlan->radioType, conf->radioType, u_char); + if (arlan_EEPROM_bad) + WRITESHM(arlan->radioModule, conf->radioType, u_char); + + memcpy_toio(arlan->encryptionKey + keyStart, encryptionKey, 8); + memcpy_toio(arlan->name, conf->siteName, 16); + + WRITESHMB(arlan->commandByte, ARLAN_COM_INT | ARLAN_COM_CONF); /* do configure */ + memset_io(arlan->commandParameter, 0, 0xf); /* 0xf */ + memset_io(arlan->commandParameter + 1, 0, 2); + if (conf->writeEEPROM) + { + memset_io(arlan->commandParameter, conf->writeEEPROM, 1); +// conf->writeEEPROM=0; + } + if (conf->registrationMode && conf->registrationInterrupts) + memset_io(arlan->commandParameter + 3, 1, 1); + else + memset_io(arlan->commandParameter + 3, 0, 1); + + priv->irq_test_done = 0; + + if (conf->tx_queue_len) + dev->tx_queue_len = conf->tx_queue_len; + udelay(100); + + ARLAN_DEBUG_EXIT("arlan_hw_config"); + return 0; +} + + +static int arlan_read_card_configuration(struct net_device *dev) +{ + u_char tlx415; + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + + ARLAN_DEBUG_ENTRY("arlan_read_card_configuration"); + + if (radioNodeId == radioNodeIdUNKNOWN) + { + READSHM(conf->radioNodeId, arlan->radioNodeId, u_short); + } + else + conf->radioNodeId = radioNodeId; + + if (SID == SIDUNKNOWN) + { + READSHM(conf->SID, arlan->SID, u_int); + } + else conf->SID = SID; + + if (spreadingCode == spreadingCodeUNKNOWN) + { + READSHM(conf->spreadingCode, arlan->spreadingCode, u_char); + } + else + conf->spreadingCode = spreadingCode; + + if (channelSet == channelSetUNKNOWN) + { + READSHM(conf->channelSet, arlan->channelSet, u_char); + } + else conf->channelSet = channelSet; + + if (channelNumber == channelNumberUNKNOWN) + { + READSHM(conf->channelNumber, arlan->channelNumber, u_char); + } + else conf->channelNumber = channelNumber; + + READSHM(conf->scramblingDisable, arlan->scramblingDisable, u_char); + READSHM(conf->txAttenuation, arlan->txAttenuation, u_char); + + if (systemId == systemIdUNKNOWN) + { + READSHM(conf->systemId, arlan->systemId, u_int); + } + else conf->systemId = systemId; + + READSHM(conf->maxDatagramSize, arlan->maxDatagramSize, u_short); + READSHM(conf->maxFrameSize, arlan->maxFrameSize, u_short); + READSHM(conf->maxRetries, arlan->maxRetries, u_char); + READSHM(conf->receiveMode, arlan->receiveMode, u_char); + READSHM(conf->priority, arlan->priority, u_char); + READSHM(conf->rootOrRepeater, arlan->rootOrRepeater, u_char); + + if (SID == SIDUNKNOWN) + { + READSHM(conf->SID, arlan->SID, u_int); + } + else conf->SID = SID; + + if (registrationMode == registrationModeUNKNOWN) + { + READSHM(conf->registrationMode, arlan->registrationMode, u_char); + } + else conf->registrationMode = registrationMode; + + READSHM(conf->registrationFill, arlan->registrationFill, u_char); + READSHM(conf->localTalkAddress, arlan->localTalkAddress, u_char); + READSHM(conf->codeFormat, arlan->codeFormat, u_char); + READSHM(conf->numChannels, arlan->numChannels, u_char); + READSHM(conf->channel1, arlan->channel1, u_char); + READSHM(conf->channel2, arlan->channel2, u_char); + READSHM(conf->channel3, arlan->channel3, u_char); + READSHM(conf->channel4, arlan->channel4, u_char); + READSHM(conf->waitTime, arlan->waitTime, u_short); + READSHM(conf->lParameter, arlan->lParameter, u_short); + READSHM(conf->_15, arlan->_15, u_short); + READSHM(conf->headerSize, arlan->headerSize, u_short); + READSHM(conf->hardwareType, arlan->hardwareType, u_char); + READSHM(conf->radioType, arlan->radioModule, u_char); + + if (conf->radioType == 0) + conf->radioType = 0xc; + + WRITESHM(arlan->configStatus, 0xA5, u_char); + READSHM(tlx415, arlan->configStatus, u_char); + + if (tlx415 != 0xA5) + printk(KERN_INFO "%s tlx415 chip \n", dev->name); + + conf->txClear = 0; + conf->txRetries = 1; + conf->txRouting = 1; + conf->txScrambled = 0; + conf->rxParameter = 1; + conf->txTimeoutMs = 4000; + conf->waitCardTimeout = 100000; + conf->receiveMode = ARLAN_RCV_CLEAN; + memcpy_fromio(conf->siteName, arlan->name, 16); + conf->siteName[16] = '\0'; + conf->retries = retries; + conf->tx_delay_ms = tx_delay_ms; + conf->ReTransmitPacketMaxSize = 200; + conf->waitReTransmitPacketMaxSize = 200; + conf->txAckTimeoutMs = 900; + conf->fastReTransCount = 3; + + ARLAN_DEBUG_EXIT("arlan_read_card_configuration"); + + return 0; +} + + +static int lastFoundAt = 0xbe000; + + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ +#define ARLAN_SHMEM_SIZE 0x2000 +static int __init arlan_check_fingerprint(unsigned long memaddr) +{ + static const char probeText[] = "TELESYSTEM SLW INC. ARLAN \0"; + volatile struct arlan_shmem __iomem *arlan = (struct arlan_shmem *) memaddr; + unsigned long paddr = virt_to_phys((void *) memaddr); + char tempBuf[49]; + + ARLAN_DEBUG_ENTRY("arlan_check_fingerprint"); + + if (!request_mem_region(paddr, ARLAN_SHMEM_SIZE, "arlan")) { + // printk(KERN_WARNING "arlan: memory region %lx excluded from probing \n",paddr); + return -ENODEV; + } + + memcpy_fromio(tempBuf, arlan->textRegion, 29); + tempBuf[30] = 0; + + /* check for card at this address */ + if (0 != strncmp(tempBuf, probeText, 29)){ + release_mem_region(paddr, ARLAN_SHMEM_SIZE); + return -ENODEV; + } + +// printk(KERN_INFO "arlan found at 0x%x \n",memaddr); + ARLAN_DEBUG_EXIT("arlan_check_fingerprint"); + + return 0; +} + +static int arlan_change_mtu(struct net_device *dev, int new_mtu) +{ + struct arlan_private *priv = netdev_priv(dev); + struct arlan_conf_stru *conf = priv->Conf; + + ARLAN_DEBUG_ENTRY("arlan_change_mtu"); + if (new_mtu > 2032) + return -EINVAL; + dev->mtu = new_mtu; + if (new_mtu < 256) + new_mtu = 256; /* cards book suggests 1600 */ + conf->maxDatagramSize = new_mtu; + conf->maxFrameSize = new_mtu + 48; + + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_CONF); + printk(KERN_NOTICE "%s mtu changed to %d \n", dev->name, new_mtu); + + ARLAN_DEBUG_EXIT("arlan_change_mtu"); + + return 0; +} + +static int arlan_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + + ARLAN_DEBUG_ENTRY("arlan_mac_addr"); + return -EINVAL; + + if (netif_running(dev)) + return -EBUSY; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + ARLAN_DEBUG_EXIT("arlan_mac_addr"); + return 0; +} + +static const struct net_device_ops arlan_netdev_ops = { + .ndo_open = arlan_open, + .ndo_stop = arlan_close, + .ndo_start_xmit = arlan_tx, + .ndo_get_stats = arlan_statistics, + .ndo_set_multicast_list = arlan_set_multicast, + .ndo_change_mtu = arlan_change_mtu, + .ndo_set_mac_address = arlan_mac_addr, + .ndo_tx_timeout = arlan_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init arlan_setup_device(struct net_device *dev, int num) +{ + struct arlan_private *ap = netdev_priv(dev); + int err; + + ARLAN_DEBUG_ENTRY("arlan_setup_device"); + + ap->conf = (struct arlan_shmem *)(ap+1); + + dev->tx_queue_len = tx_queue_len; + dev->netdev_ops = &arlan_netdev_ops; + dev->watchdog_timeo = 3*HZ; + + ap->irq_test_done = 0; + ap->Conf = &arlan_conf[num]; + + ap->Conf->pre_Command_Wait = 40; + ap->Conf->rx_tweak1 = 30; + ap->Conf->rx_tweak2 = 0; + + + err = register_netdev(dev); + if (err) { + release_mem_region(virt_to_phys((void *) dev->mem_start), + ARLAN_SHMEM_SIZE); + free_netdev(dev); + return err; + } + arlan_device[num] = dev; + ARLAN_DEBUG_EXIT("arlan_setup_device"); + return 0; +} + +static int __init arlan_probe_here(struct net_device *dev, + unsigned long memaddr) +{ + struct arlan_private *ap = netdev_priv(dev); + + ARLAN_DEBUG_ENTRY("arlan_probe_here"); + + if (arlan_check_fingerprint(memaddr)) + return -ENODEV; + + printk(KERN_NOTICE "%s: Arlan found at %llx, \n ", dev->name, + (u64) virt_to_phys((void*)memaddr)); + + ap->card = (void *) memaddr; + dev->mem_start = memaddr; + dev->mem_end = memaddr + ARLAN_SHMEM_SIZE-1; + + if (dev->irq < 2) + { + READSHM(dev->irq, ap->card->irqLevel, u_char); + } else if (dev->irq == 2) + dev->irq = 9; + + arlan_read_card_configuration(dev); + + ARLAN_DEBUG_EXIT("arlan_probe_here"); + return 0; +} + + +static int arlan_open(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + int ret = 0; + + ARLAN_DEBUG_ENTRY("arlan_open"); + + ret = request_irq(dev->irq, &arlan_interrupt, 0, dev->name, dev); + if (ret) + { + printk(KERN_ERR "%s: unable to get IRQ %d .\n", + dev->name, dev->irq); + return ret; + } + + + priv->bad = 0; + priv->lastReset = 0; + priv->reset = 0; + memcpy_fromio(dev->dev_addr, arlan->lanCardNodeId, 6); + memset(dev->broadcast, 0xff, 6); + dev->tx_queue_len = tx_queue_len; + priv->interrupt_processing_active = 0; + spin_lock_init(&priv->lock); + + netif_start_queue (dev); + + priv->registrationLostCount = 0; + priv->registrationLastSeen = jiffies; + priv->txLast = 0; + priv->tx_command_given = 0; + priv->rx_command_given = 0; + + priv->reRegisterExp = 1; + priv->tx_last_sent = jiffies - 1; + priv->tx_last_cleared = jiffies; + priv->Conf->writeEEPROM = 0; + priv->Conf->registrationInterrupts = 1; + + init_timer(&priv->timer); + priv->timer.expires = jiffies + HZ / 10; + priv->timer.data = (unsigned long) dev; + priv->timer.function = &arlan_registration_timer; /* timer handler */ + + arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW); + mdelay(200); + add_timer(&priv->timer); + + ARLAN_DEBUG_EXIT("arlan_open"); + return 0; +} + + +static void arlan_tx_timeout (struct net_device *dev) +{ + printk(KERN_ERR "%s: arlan transmit timed out, kernel decided\n", dev->name); + /* Try to restart the adaptor. */ + arlan_command(dev, ARLAN_COMMAND_CLEAN_AND_RESET); + // dev->trans_start = jiffies; + // netif_start_queue (dev); +} + + +static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev) +{ + short length; + unsigned char *buf; + + ARLAN_DEBUG_ENTRY("arlan_tx"); + + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; + + if (length + 0x12 > 0x800) { + printk(KERN_ERR "TX RING overflow \n"); + netif_stop_queue (dev); + } + + if (arlan_hw_tx(dev, buf, length) == -1) + goto bad_end; + + dev->trans_start = jiffies; + + dev_kfree_skb(skb); + + arlan_process_interrupt(dev); + ARLAN_DEBUG_EXIT("arlan_tx"); + return NETDEV_TX_OK; + +bad_end: + arlan_process_interrupt(dev); + netif_stop_queue (dev); + ARLAN_DEBUG_EXIT("arlan_tx"); + return NETDEV_TX_BUSY; +} + + +static inline int DoNotReTransmitCrap(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + if (TXLAST(dev).length < priv->Conf->ReTransmitPacketMaxSize) + return 1; + return 0; + +} + +static inline int DoNotWaitReTransmitCrap(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + if (TXLAST(dev).length < priv->Conf->waitReTransmitPacketMaxSize) + return 1; + return 0; +} + +static inline void arlan_queue_retransmit(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + ARLAN_DEBUG_ENTRY("arlan_queue_retransmit"); + + if (DoNotWaitReTransmitCrap(dev)) + { + arlan_drop_tx(dev); + } else + priv->ReTransmitRequested++; + + ARLAN_DEBUG_EXIT("arlan_queue_retransmit"); +} + +static inline void RetryOrFail(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + ARLAN_DEBUG_ENTRY("RetryOrFail"); + + if (priv->retransmissions > priv->Conf->retries || + DoNotReTransmitCrap(dev)) + { + arlan_drop_tx(dev); + } + else if (priv->bad <= priv->Conf->fastReTransCount) + { + arlan_retransmit_now(dev); + } + else arlan_queue_retransmit(dev); + + ARLAN_DEBUG_EXIT("RetryOrFail"); +} + + +static void arlan_tx_done_interrupt(struct net_device *dev, int status) +{ + struct arlan_private *priv = netdev_priv(dev); + + ARLAN_DEBUG_ENTRY("arlan_tx_done_interrupt"); + + priv->tx_last_cleared = jiffies; + priv->tx_command_given = 0; + switch (status) + { + case 1: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit OK\n"); + dev->stats.tx_packets++; + priv->bad = 0; + priv->reset = 0; + priv->retransmissions = 0; + if (priv->Conf->tx_delay_ms) + { + priv->tx_done_delayed = jiffies + (priv->Conf->tx_delay_ms * HZ) / 1000 + 1; + } + else + { + TXLAST(dev).offset = 0; + if (priv->txLast) + priv->txLast = 0; + else if (TXTAIL(dev).offset) + priv->txLast = 1; + if (TXLAST(dev).offset) + { + arlan_retransmit_now(dev); + dev->trans_start = jiffies; + } + if (!TXHEAD(dev).offset || !TXTAIL(dev).offset) + { + netif_wake_queue (dev); + } + } + } + break; + + case 2: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit timed out\n"); + priv->bad += 1; + //arlan_queue_retransmit(dev); + RetryOrFail(dev); + } + break; + + case 3: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit max retries\n"); + priv->bad += 1; + priv->reset = 0; + //arlan_queue_retransmit(dev); + RetryOrFail(dev); + } + break; + + case 4: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit aborted\n"); + priv->bad += 1; + arlan_queue_retransmit(dev); + //RetryOrFail(dev); + } + break; + + case 5: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit not registered\n"); + priv->bad += 1; + //debug=101; + arlan_queue_retransmit(dev); + } + break; + + case 6: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit destination full\n"); + priv->bad += 1; + priv->reset = 0; + //arlan_drop_tx(dev); + arlan_queue_retransmit(dev); + } + break; + + case 7: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit unknown ack\n"); + priv->bad += 1; + priv->reset = 0; + arlan_queue_retransmit(dev); + } + break; + + case 8: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit dest mail box full\n"); + priv->bad += 1; + priv->reset = 0; + //arlan_drop_tx(dev); + arlan_queue_retransmit(dev); + } + break; + + case 9: + { + IFDEBUG(ARLAN_DEBUG_TX_CHAIN) + printk("arlan intr: transmit root dest not reg.\n"); + priv->bad += 1; + priv->reset = 1; + //arlan_drop_tx(dev); + arlan_queue_retransmit(dev); + } + break; + + default: + { + printk(KERN_ERR "arlan intr: transmit status unknown\n"); + priv->bad += 1; + priv->reset = 1; + arlan_drop_tx(dev); + } + } + + ARLAN_DEBUG_EXIT("arlan_tx_done_interrupt"); +} + + +static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short rxOffset, u_short pkt_len) +{ + char *skbtmp; + int i = 0; + + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + + + ARLAN_DEBUG_ENTRY("arlan_rx_interrupt"); + // by spec, not WRITESHMB(arlan->rxStatus,0x00); + // prohibited here arlan_command(dev, ARLAN_COMMAND_RX); + + if (pkt_len < 10 || pkt_len > 2048) + { + printk(KERN_WARNING "%s: got too short or long packet, len %d \n", dev->name, pkt_len); + return; + } + if (rxOffset + pkt_len > 0x2000) + { + printk("%s: got too long packet, len %d offset %x\n", dev->name, pkt_len, rxOffset); + return; + } + priv->in_bytes += pkt_len; + priv->in_bytes10 += pkt_len; + if (conf->measure_rate < 1) + conf->measure_rate = 1; + if (time_after(jiffies, priv->in_time + conf->measure_rate * HZ)) + { + conf->in_speed = priv->in_bytes / conf->measure_rate; + priv->in_bytes = 0; + priv->in_time = jiffies; + } + if (time_after(jiffies, priv->in_time10 + conf->measure_rate * 10*HZ)) + { + conf->in_speed10 = priv->in_bytes10 / (10 * conf->measure_rate); + priv->in_bytes10 = 0; + priv->in_time10 = jiffies; + } + DEBUGSHM(1, "arlan rcv pkt rxStatus= %d ", arlan->rxStatus, u_char); + switch (rxStatus) + { + case 1: + case 2: + case 3: + { + /* Malloc up new buffer. */ + struct sk_buff *skb; + + DEBUGSHM(50, "arlan recv pkt offs=%d\n", arlan->rxOffset, u_short); + DEBUGSHM(1, "arlan rxFrmType = %d \n", arlan->rxFrmType, u_char); + DEBUGSHM(1, KERN_INFO "arlan rx scrambled = %d \n", arlan->scrambled, u_char); + + /* here we do multicast filtering to avoid slow 8-bit memcopy */ +#ifdef ARLAN_MULTICAST + if (!(dev->flags & IFF_ALLMULTI) && + !(dev->flags & IFF_PROMISC) && + dev->mc_list) + { + char hw_dst_addr[6]; + struct dev_mc_list *dmi = dev->mc_list; + int i; + + memcpy_fromio(hw_dst_addr, arlan->ultimateDestAddress, 6); + if (hw_dst_addr[0] == 0x01) + { + if (mdebug) + if (hw_dst_addr[1] == 0x00) + printk(KERN_ERR "%s mcast 0x0100 \n", dev->name); + else if (hw_dst_addr[1] == 0x40) + printk(KERN_ERR "%s m/bcast 0x0140 \n", dev->name); + while (dmi) + { + if (dmi->dmi_addrlen == 6) { + if (arlan_debug & ARLAN_DEBUG_HEADER_DUMP) + printk(KERN_ERR "%s mcl %pM\n", + dev->name, dmi->dmi_addr); + for (i = 0; i < 6; i++) + if (dmi->dmi_addr[i] != hw_dst_addr[i]) + break; + if (i == 6) + break; + } else + printk(KERN_ERR "%s: invalid multicast address length given.\n", dev->name); + dmi = dmi->next; + } + /* we reach here if multicast filtering is on and packet + * is multicast and not for receive */ + goto end_of_interrupt; + } + } +#endif // ARLAN_MULTICAST + /* multicast filtering ends here */ + pkt_len += ARLAN_FAKE_HDR_LEN; + + skb = dev_alloc_skb(pkt_len + 4); + if (skb == NULL) + { + printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + break; + } + skb_reserve(skb, 2); + skbtmp = skb_put(skb, pkt_len); + + memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN); + memcpy_fromio(skbtmp, arlan->ultimateDestAddress, 6); + memcpy_fromio(skbtmp + 6, arlan->rxSrc, 6); + WRITESHMB(arlan->rxStatus, 0x00); + arlan_command(dev, ARLAN_COMMAND_RX); + + IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) + { + char immedDestAddress[6]; + char immedSrcAddress[6]; + memcpy_fromio(immedDestAddress, arlan->immedDestAddress, 6); + memcpy_fromio(immedSrcAddress, arlan->immedSrcAddress, 6); + + printk(KERN_WARNING "%s t %pM f %pM imd %pM ims %pM\n", + dev->name, skbtmp, + &skbtmp[6], + immedDestAddress, + immedSrcAddress); + } + skb->protocol = eth_type_trans(skb, dev); + IFDEBUG(ARLAN_DEBUG_HEADER_DUMP) + if (skb->protocol != 0x608 && skb->protocol != 0x8) + { + for (i = 0; i <= 22; i++) + printk("%02x:", (u_char) skbtmp[i + 12]); + printk(KERN_ERR "\n"); + printk(KERN_WARNING "arlan kernel pkt type trans %x \n", skb->protocol); + } + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + break; + + default: + printk(KERN_ERR "arlan intr: received unknown status\n"); + dev->stats.rx_crc_errors++; + break; + } + ARLAN_DEBUG_EXIT("arlan_rx_interrupt"); +} + +static void arlan_process_interrupt(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + u_char rxStatus = READSHMB(arlan->rxStatus); + u_char txStatus = READSHMB(arlan->txStatus); + u_short rxOffset = READSHMS(arlan->rxOffset); + u_short pkt_len = READSHMS(arlan->rxLength); + int interrupt_count = 0; + + ARLAN_DEBUG_ENTRY("arlan_process_interrupt"); + + if (test_and_set_bit(0, (void *) &priv->interrupt_processing_active)) + { + if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) + printk(KERN_ERR "interrupt chain reentering \n"); + goto end_int_process; + } + while ((rxStatus || txStatus || priv->interrupt_ack_requested) + && (interrupt_count < 5)) + { + if (rxStatus) + priv->last_rx_int_ack_time = jiffies; + + arlan_command(dev, ARLAN_COMMAND_INT_ACK); + arlan_command(dev, ARLAN_COMMAND_INT_ENABLE); + + IFDEBUG(ARLAN_DEBUG_INTERRUPT) + printk(KERN_ERR "%s: got IRQ rx %x tx %x comm %x rxOff %x rxLen %x \n", + dev->name, rxStatus, txStatus, READSHMB(arlan->commandByte), + rxOffset, pkt_len); + + if (rxStatus == 0 && txStatus == 0) + { + if (priv->irq_test_done) + { + if (!registrationBad(dev)) + IFDEBUG(ARLAN_DEBUG_INTERRUPT) printk(KERN_ERR "%s unknown interrupt(nop? regLost ?) reason tx %d rx %d ", + dev->name, txStatus, rxStatus); + } else { + IFDEBUG(ARLAN_DEBUG_INTERRUPT) + printk(KERN_INFO "%s irq $%d test OK \n", dev->name, dev->irq); + + } + priv->interrupt_ack_requested = 0; + goto ends; + } + if (txStatus != 0) + { + WRITESHMB(arlan->txStatus, 0x00); + arlan_tx_done_interrupt(dev, txStatus); + goto ends; + } + if (rxStatus == 1 || rxStatus == 2) + { /* a packet waiting */ + arlan_rx_interrupt(dev, rxStatus, rxOffset, pkt_len); + goto ends; + } + if (rxStatus > 2 && rxStatus < 0xff) + { + WRITESHMB(arlan->rxStatus, 0x00); + printk(KERN_ERR "%s unknown rxStatus reason tx %d rx %d ", + dev->name, txStatus, rxStatus); + goto ends; + } + if (rxStatus == 0xff) + { + WRITESHMB(arlan->rxStatus, 0x00); + arlan_command(dev, ARLAN_COMMAND_RX); + if (registrationBad(dev)) + netif_device_detach(dev); + if (!registrationBad(dev)) + { + priv->registrationLastSeen = jiffies; + if (!netif_queue_stopped(dev) && !priv->under_reset && !priv->under_config) + netif_wake_queue (dev); + } + goto ends; + } +ends: + + arlan_command_process(dev); + + rxStatus = READSHMB(arlan->rxStatus); + txStatus = READSHMB(arlan->txStatus); + rxOffset = READSHMS(arlan->rxOffset); + pkt_len = READSHMS(arlan->rxLength); + + + priv->irq_test_done = 1; + + interrupt_count++; + } + priv->interrupt_processing_active = 0; + +end_int_process: + arlan_command_process(dev); + + ARLAN_DEBUG_EXIT("arlan_process_interrupt"); + return; +} + +static irqreturn_t arlan_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + u_char rxStatus = READSHMB(arlan->rxStatus); + u_char txStatus = READSHMB(arlan->txStatus); + + ARLAN_DEBUG_ENTRY("arlan_interrupt"); + + + if (!rxStatus && !txStatus) + priv->interrupt_ack_requested++; + + arlan_process_interrupt(dev); + + priv->irq_test_done = 1; + + ARLAN_DEBUG_EXIT("arlan_interrupt"); + return IRQ_HANDLED; + +} + + +static int arlan_close(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + + ARLAN_DEBUG_ENTRY("arlan_close"); + + del_timer_sync(&priv->timer); + + arlan_command(dev, ARLAN_COMMAND_POWERDOWN); + + IFDEBUG(ARLAN_DEBUG_STARTUP) + printk(KERN_NOTICE "%s: Closing device\n", dev->name); + + netif_stop_queue(dev); + free_irq(dev->irq, dev); + + ARLAN_DEBUG_EXIT("arlan_close"); + return 0; +} + +#ifdef ARLAN_DEBUGGING +static long alignLong(volatile u_char * ptr) +{ + long ret; + memcpy_fromio(&ret, (void *) ptr, 4); + return ret; +} +#endif + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ + +static struct net_device_stats *arlan_statistics(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + + + ARLAN_DEBUG_ENTRY("arlan_statistics"); + + /* Update the statistics from the device registers. */ + + READSHM(dev->stats.collisions, arlan->numReTransmissions, u_int); + READSHM(dev->stats.rx_crc_errors, arlan->numCRCErrors, u_int); + READSHM(dev->stats.rx_dropped, arlan->numFramesDiscarded, u_int); + READSHM(dev->stats.rx_fifo_errors, arlan->numRXBufferOverflows, u_int); + READSHM(dev->stats.rx_frame_errors, arlan->numReceiveFramesLost, u_int); + READSHM(dev->stats.rx_over_errors, arlan->numRXOverruns, u_int); + READSHM(dev->stats.rx_packets, arlan->numDatagramsReceived, u_int); + READSHM(dev->stats.tx_aborted_errors, arlan->numAbortErrors, u_int); + READSHM(dev->stats.tx_carrier_errors, arlan->numStatusTimeouts, u_int); + READSHM(dev->stats.tx_dropped, arlan->numDatagramsDiscarded, u_int); + READSHM(dev->stats.tx_fifo_errors, arlan->numTXUnderruns, u_int); + READSHM(dev->stats.tx_packets, arlan->numDatagramsTransmitted, u_int); + READSHM(dev->stats.tx_window_errors, arlan->numHoldOffs, u_int); + + ARLAN_DEBUG_EXIT("arlan_statistics"); + + return &dev->stats; +} + + +static void arlan_set_multicast(struct net_device *dev) +{ + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + struct arlan_conf_stru *conf = priv->Conf; + int board_conf_needed = 0; + + + ARLAN_DEBUG_ENTRY("arlan_set_multicast"); + + if (dev->flags & IFF_PROMISC) + { + unsigned char recMode; + READSHM(recMode, arlan->receiveMode, u_char); + conf->receiveMode = (ARLAN_RCV_PROMISC | ARLAN_RCV_CONTROL); + if (conf->receiveMode != recMode) + board_conf_needed = 1; + } + else + { + /* turn off promiscuous mode */ + unsigned char recMode; + READSHM(recMode, arlan->receiveMode, u_char); + conf->receiveMode = ARLAN_RCV_CLEAN | ARLAN_RCV_CONTROL; + if (conf->receiveMode != recMode) + board_conf_needed = 1; + } + if (board_conf_needed) + arlan_command(dev, ARLAN_COMMAND_CONF); + + ARLAN_DEBUG_EXIT("arlan_set_multicast"); +} + + +struct net_device * __init arlan_probe(int unit) +{ + struct net_device *dev; + int err; + int m; + + ARLAN_DEBUG_ENTRY("arlan_probe"); + + if (arlans_found == MAX_ARLANS) + return ERR_PTR(-ENODEV); + + /* + * Reserve space for local data and a copy of the shared memory + * that is used by the /proc interface. + */ + dev = alloc_etherdev(sizeof(struct arlan_private) + + sizeof(struct arlan_shmem)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + if (dev->mem_start) { + if (arlan_probe_here(dev, dev->mem_start) == 0) + goto found; + goto not_found; + } + + } + + + for (m = (int)phys_to_virt(lastFoundAt) + ARLAN_SHMEM_SIZE; + m <= (int)phys_to_virt(0xDE000); + m += ARLAN_SHMEM_SIZE) + { + if (arlan_probe_here(dev, m) == 0) + { + lastFoundAt = (int)virt_to_phys((void*)m); + goto found; + } + } + + if (lastFoundAt == 0xbe000) + printk(KERN_ERR "arlan: No Arlan devices found \n"); + + not_found: + free_netdev(dev); + return ERR_PTR(-ENODEV); + + found: + err = arlan_setup_device(dev, arlans_found); + if (err) + dev = ERR_PTR(err); + else if (!arlans_found++) + printk(KERN_INFO "Arlan driver %s\n", arlan_version); + + return dev; +} + +#ifdef MODULE +int __init init_module(void) +{ + int i = 0; + + ARLAN_DEBUG_ENTRY("init_module"); + + if (channelSet != channelSetUNKNOWN || channelNumber != channelNumberUNKNOWN || systemId != systemIdUNKNOWN) + return -EINVAL; + + for (i = 0; i < MAX_ARLANS; i++) { + struct net_device *dev = arlan_probe(i); + + if (IS_ERR(dev)) + return PTR_ERR(dev); + } + init_arlan_proc(); + printk(KERN_INFO "Arlan driver %s\n", arlan_version); + ARLAN_DEBUG_EXIT("init_module"); + return 0; +} + + +void __exit cleanup_module(void) +{ + int i = 0; + struct net_device *dev; + + ARLAN_DEBUG_ENTRY("cleanup_module"); + + IFDEBUG(ARLAN_DEBUG_SHUTDOWN) + printk(KERN_INFO "arlan: unloading module\n"); + + cleanup_arlan_proc(); + + for (i = 0; i < MAX_ARLANS; i++) + { + dev = arlan_device[i]; + if (dev) { + arlan_command(dev, ARLAN_COMMAND_POWERDOWN ); + + unregister_netdev(dev); + release_mem_region(virt_to_phys((void *) dev->mem_start), + ARLAN_SHMEM_SIZE); + free_netdev(dev); + arlan_device[i] = NULL; + } + } + + ARLAN_DEBUG_EXIT("cleanup_module"); +} + + +#endif +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/arlan/arlan-proc.c b/drivers/staging/arlan/arlan-proc.c new file mode 100644 index 000000000000..a8b689635a3b --- /dev/null +++ b/drivers/staging/arlan/arlan-proc.c @@ -0,0 +1,1253 @@ +#include "arlan.h" + +#include + +#ifdef CONFIG_PROC_FS + +/* void enableReceive(struct net_device* dev); +*/ + + + +#define ARLAN_STR_SIZE 0x2ff0 +#define DEV_ARLAN_INFO 1 +#define DEV_ARLAN 1 +#define SARLG(type,var) {\ + pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n", #var, READSHMB(priva->card->var)); \ + } + +#define SARLBN(type,var,nn) {\ + pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x",#var);\ + for (i=0; i < nn; i++ ) pos += sprintf(arlan_drive_info+pos, "%02x",READSHMB(priva->card->var[i]));\ + pos += sprintf(arlan_drive_info+pos, "\n"); \ + } + +#define SARLBNpln(type,var,nn) {\ + for (i=0; i < nn; i++ ) pos += sprintf(arlan_drive_info+pos, "%02x",READSHMB(priva->card->var[i]));\ + } + +#define SARLSTR(var,nn) {\ + char tmpStr[400];\ + int tmpLn = nn;\ + if (nn > 399 ) tmpLn = 399; \ + memcpy(tmpStr,(char *) priva->conf->var,tmpLn);\ + tmpStr[tmpLn] = 0; \ + pos += sprintf(arlan_drive_info+pos, "%s\t=\t%s \n",#var,priva->conf->var);\ + } + +#define SARLUC(var) SARLG(u_char, var) +#define SARLUCN(var,nn) SARLBN(u_char,var, nn) +#define SARLUS(var) SARLG(u_short, var) +#define SARLUSN(var,nn) SARLBN(u_short,var, nn) +#define SARLUI(var) SARLG(u_int, var) + +#define SARLUSA(var) {\ + u_short tmpVar;\ + memcpy(&tmpVar, (short *) priva->conf->var,2); \ + pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n",#var, tmpVar);\ +} + +#define SARLUIA(var) {\ + u_int tmpVar;\ + memcpy(&tmpVar, (int* )priva->conf->var,4); \ + pos += sprintf(arlan_drive_info+pos, "%s\t=\t0x%x\n",#var, tmpVar);\ +} + + +static const char *arlan_diagnostic_info_string(struct net_device *dev) +{ + + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + u_char diagnosticInfo; + + READSHM(diagnosticInfo, arlan->diagnosticInfo, u_char); + + switch (diagnosticInfo) + { + case 0xFF: + return "Diagnostic info is OK"; + case 0xFE: + return "ERROR EPROM Checksum error "; + case 0xFD: + return "ERROR Local Ram Test Failed "; + case 0xFC: + return "ERROR SCC failure "; + case 0xFB: + return "ERROR BackBone failure "; + case 0xFA: + return "ERROR transceiver not found "; + case 0xF9: + return "ERROR no more address space "; + case 0xF8: + return "ERROR Checksum error "; + case 0xF7: + return "ERROR Missing SS Code"; + case 0xF6: + return "ERROR Invalid config format"; + case 0xF5: + return "ERROR Reserved errorcode F5"; + case 0xF4: + return "ERROR Invalid spreading code/channel number"; + case 0xF3: + return "ERROR Load Code Error"; + case 0xF2: + return "ERROR Reserver errorcode F2 "; + case 0xF1: + return "ERROR Invalid command receivec by LAN card "; + case 0xF0: + return "ERROR Invalid parameter found in command "; + case 0xEF: + return "ERROR On-chip timer failure "; + case 0xEE: + return "ERROR T410 timer failure "; + case 0xED: + return "ERROR Too Many TxEnable commands "; + case 0xEC: + return "ERROR EEPROM error on radio module "; + default: + return "ERROR unknown Diagnostic info reply code "; + } +} + +static const char *arlan_hardware_type_string(struct net_device *dev) +{ + u_char hardwareType; + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + + READSHM(hardwareType, arlan->hardwareType, u_char); + switch (hardwareType) + { + case 0x00: + return "type A450"; + case 0x01: + return "type A650 "; + case 0x04: + return "type TMA coproc"; + case 0x0D: + return "type A650E "; + case 0x18: + return "type TMA coproc Australian"; + case 0x19: + return "type A650A "; + case 0x26: + return "type TMA coproc European"; + case 0x2E: + return "type A655 "; + case 0x2F: + return "type A655A "; + case 0x30: + return "type A655E "; + case 0x0B: + return "type A670 "; + case 0x0C: + return "type A670E "; + case 0x2D: + return "type A670A "; + case 0x0F: + return "type A411T"; + case 0x16: + return "type A411TA"; + case 0x1B: + return "type A440T"; + case 0x1C: + return "type A412T"; + case 0x1E: + return "type A412TA"; + case 0x22: + return "type A411TE"; + case 0x24: + return "type A412TE"; + case 0x27: + return "type A671T "; + case 0x29: + return "type A671TA "; + case 0x2B: + return "type A671TE "; + case 0x31: + return "type A415T "; + case 0x33: + return "type A415TA "; + case 0x35: + return "type A415TE "; + case 0x37: + return "type A672"; + case 0x39: + return "type A672A "; + case 0x3B: + return "type A672T"; + case 0x6B: + return "type IC2200"; + default: + return "type A672T"; + } +} +#ifdef ARLAN_DEBUGGING +static void arlan_print_diagnostic_info(struct net_device *dev) +{ + int i; + u_char diagnosticInfo; + u_short diagnosticOffset; + u_char hardwareType; + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + + // ARLAN_DEBUG_ENTRY("arlan_print_diagnostic_info"); + + if (READSHMB(arlan->configuredStatusFlag) == 0) + printk("Arlan: Card NOT configured\n"); + else + printk("Arlan: Card is configured\n"); + + READSHM(diagnosticInfo, arlan->diagnosticInfo, u_char); + READSHM(diagnosticOffset, arlan->diagnosticOffset, u_short); + + printk(KERN_INFO "%s\n", arlan_diagnostic_info_string(dev)); + + if (diagnosticInfo != 0xff) + printk("%s arlan: Diagnostic Offset %d \n", dev->name, diagnosticOffset); + + printk("arlan: LAN CODE ID = "); + for (i = 0; i < 6; i++) + DEBUGSHM(1, "%03d:", arlan->lanCardNodeId[i], u_char); + printk("\n"); + + printk("arlan: Arlan BroadCast address = "); + for (i = 0; i < 6; i++) + DEBUGSHM(1, "%03d:", arlan->broadcastAddress[i], u_char); + printk("\n"); + + READSHM(hardwareType, arlan->hardwareType, u_char); + printk(KERN_INFO "%s\n", arlan_hardware_type_string(dev)); + + + DEBUGSHM(1, "arlan: channelNumber=%d\n", arlan->channelNumber, u_char); + DEBUGSHM(1, "arlan: channelSet=%d\n", arlan->channelSet, u_char); + DEBUGSHM(1, "arlan: spreadingCode=%d\n", arlan->spreadingCode, u_char); + DEBUGSHM(1, "arlan: radioNodeId=%d\n", arlan->radioNodeId, u_short); + DEBUGSHM(1, "arlan: SID =%d\n", arlan->SID, u_short); + DEBUGSHM(1, "arlan: rxOffset=%d\n", arlan->rxOffset, u_short); + + DEBUGSHM(1, "arlan: registration mode is %d\n", arlan->registrationMode, u_char); + + printk("arlan: name= "); + IFDEBUG(1) + + for (i = 0; i < 16; i++) + { + char c; + READSHM(c, arlan->name[i], char); + if (c) + printk("%c", c); + } + printk("\n"); + +// ARLAN_DEBUG_EXIT("arlan_print_diagnostic_info"); + +} + + +/****************************** TEST MEMORY **************/ + +static int arlan_hw_test_memory(struct net_device *dev) +{ + u_char *ptr; + int i; + int memlen = sizeof(struct arlan_shmem) - 0xF; /* avoid control register */ + volatile char *arlan_mem = (char *) (dev->mem_start); + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + char pattern; + + ptr = NULL; + + /* hold card in reset state */ + setHardwareReset(dev); + + /* test memory */ + pattern = 0; + for (i = 0; i < memlen; i++) + WRITESHM(arlan_mem[i], ((u_char) pattern++), u_char); + + pattern = 0; + for (i = 0; i < memlen; i++) + { + char res; + READSHM(res, arlan_mem[i], char); + if (res != pattern++) + { + printk(KERN_ERR "Arlan driver memory test 1 failed \n"); + return -1; + } + } + + pattern = 0; + for (i = 0; i < memlen; i++) + WRITESHM(arlan_mem[i], ~(pattern++), char); + + pattern = 0; + for (i = 0; i < memlen; i++) + { + char res; + READSHM(res, arlan_mem[i], char); + if (res != ~(pattern++)) + { + printk(KERN_ERR "Arlan driver memory test 2 failed \n"); + return -1; + } + } + + /* zero memory */ + for (i = 0; i < memlen; i++) + WRITESHM(arlan_mem[i], 0x00, char); + + IFDEBUG(1) printk(KERN_INFO "Arlan: memory tests ok\n"); + + /* set reset flag and then release reset */ + WRITESHM(arlan->resetFlag, 0xff, u_char); + + clearChannelAttention(dev); + clearHardwareReset(dev); + + /* wait for reset flag to become zero, we'll wait for two seconds */ + if (arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW)) + { + printk(KERN_ERR "%s arlan: failed to come back from memory test\n", dev->name); + return -1; + } + return 0; +} + +static int arlan_setup_card_by_book(struct net_device *dev) +{ + u_char irqLevel, configuredStatusFlag; + struct arlan_private *priv = netdev_priv(dev); + volatile struct arlan_shmem __iomem *arlan = priv->card; + +// ARLAN_DEBUG_ENTRY("arlan_setup_card"); + + READSHM(configuredStatusFlag, arlan->configuredStatusFlag, u_char); + + IFDEBUG(10) + if (configuredStatusFlag != 0) + IFDEBUG(10) printk("arlan: CARD IS CONFIGURED\n"); + else + IFDEBUG(10) printk("arlan: card is NOT configured\n"); + + if (testMemory || (READSHMB(arlan->diagnosticInfo) != 0xff)) + if (arlan_hw_test_memory(dev)) + return -1; + + DEBUGSHM(4, "arlan configuredStatus = %d \n", arlan->configuredStatusFlag, u_char); + DEBUGSHM(4, "arlan driver diagnostic: 0x%2x\n", arlan->diagnosticInfo, u_char); + + /* issue nop command - no interrupt */ + arlan_command(dev, ARLAN_COMMAND_NOOP); + if (arlan_command(dev, ARLAN_COMMAND_WAIT_NOW) != 0) + return -1; + + IFDEBUG(50) printk("1st Noop successfully executed !!\n"); + + /* try to turn on the arlan interrupts */ + clearClearInterrupt(dev); + setClearInterrupt(dev); + setInterruptEnable(dev); + + /* issue nop command - with interrupt */ + + arlan_command(dev, ARLAN_COMMAND_NOOPINT); + if (arlan_command(dev, ARLAN_COMMAND_WAIT_NOW) != 0) + return -1; + + + IFDEBUG(50) printk("2nd Noop successfully executed !!\n"); + + READSHM(irqLevel, arlan->irqLevel, u_char) + + if (irqLevel != dev->irq) + { + IFDEBUG(1) printk(KERN_WARNING "arlan dip switches set irq to %d\n", irqLevel); + printk(KERN_WARNING "device driver irq set to %d - does not match\n", dev->irq); + dev->irq = irqLevel; + } + else + IFDEBUG(2) printk("irq level is OK\n"); + + + IFDEBUG(3) arlan_print_diagnostic_info(dev); + + arlan_command(dev, ARLAN_COMMAND_CONF); + + READSHM(configuredStatusFlag, arlan->configuredStatusFlag, u_char); + if (configuredStatusFlag == 0) + { + printk(KERN_WARNING "arlan configure failed\n"); + return -1; + } + arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW); + arlan_command(dev, ARLAN_COMMAND_RX); + arlan_command(dev, ARLAN_COMMAND_LONG_WAIT_NOW); + printk(KERN_NOTICE "%s: arlan driver version %s loaded\n", + dev->name, arlan_version); + +// ARLAN_DEBUG_EXIT("arlan_setup_card"); + + return 0; /* no errors */ +} +#endif + +#ifdef ARLAN_PROC_INTERFACE +#ifdef ARLAN_PROC_SHM_DUMP + +static char arlan_drive_info[ARLAN_STR_SIZE] = "A655\n\0"; + +static int arlan_sysctl_info(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int i; + int retv, pos, devnum; + struct arlan_private *priva = NULL; + struct net_device *dev; + pos = 0; + if (write) + { + printk("wrirte: "); + for (i = 0; i < 100; i++) + printk("adi %x \n", arlan_drive_info[i]); + } + if (ctl->procname == NULL || arlan_drive_info == NULL) + { + printk(KERN_WARNING " procname is NULL in sysctl_table or arlan_drive_info is NULL \n at arlan module\n "); + return -1; + } + devnum = ctl->procname[5] - '0'; + if (devnum < 0 || devnum > MAX_ARLANS - 1) + { + printk(KERN_WARNING "too strange devnum in procfs parse\n "); + return -1; + } + else if (arlan_device[devnum] == NULL) + { + if (ctl->procname) + pos += sprintf(arlan_drive_info + pos, "\t%s\n\n", ctl->procname); + pos += sprintf(arlan_drive_info + pos, "No device found here \n"); + goto final; + } + else + priva = netdev_priv(arlan_device[devnum]); + + if (priva == NULL) + { + printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); + return -1; + } + dev = arlan_device[devnum]; + + memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); + + pos = sprintf(arlan_drive_info, "Arlan info \n"); + /* Header Signature */ + SARLSTR(textRegion, 48); + SARLUC(resetFlag); + pos += sprintf(arlan_drive_info + pos, "diagnosticInfo\t=\t%s \n", arlan_diagnostic_info_string(dev)); + SARLUC(diagnosticInfo); + SARLUS(diagnosticOffset); + SARLUCN(_1, 12); + SARLUCN(lanCardNodeId, 6); + SARLUCN(broadcastAddress, 6); + pos += sprintf(arlan_drive_info + pos, "hardwareType =\t %s \n", arlan_hardware_type_string(dev)); + SARLUC(hardwareType); + SARLUC(majorHardwareVersion); + SARLUC(minorHardwareVersion); + SARLUC(radioModule); + SARLUC(defaultChannelSet); + SARLUCN(_2, 47); + + /* Control/Status Block - 0x0080 */ + SARLUC(interruptInProgress); + SARLUC(cntrlRegImage); + + SARLUCN(_3, 14); + SARLUC(commandByte); + SARLUCN(commandParameter, 15); + + /* Receive Status - 0x00a0 */ + SARLUC(rxStatus); + SARLUC(rxFrmType); + SARLUS(rxOffset); + SARLUS(rxLength); + SARLUCN(rxSrc, 6); + SARLUC(rxBroadcastFlag); + SARLUC(rxQuality); + SARLUC(scrambled); + SARLUCN(_4, 1); + + /* Transmit Status - 0x00b0 */ + SARLUC(txStatus); + SARLUC(txAckQuality); + SARLUC(numRetries); + SARLUCN(_5, 14); + SARLUCN(registeredRouter, 6); + SARLUCN(backboneRouter, 6); + SARLUC(registrationStatus); + SARLUC(configuredStatusFlag); + SARLUCN(_6, 1); + SARLUCN(ultimateDestAddress, 6); + SARLUCN(immedDestAddress, 6); + SARLUCN(immedSrcAddress, 6); + SARLUS(rxSequenceNumber); + SARLUC(assignedLocaltalkAddress); + SARLUCN(_7, 27); + + /* System Parameter Block */ + + /* - Driver Parameters (Novell Specific) */ + + SARLUS(txTimeout); + SARLUS(transportTime); + SARLUCN(_8, 4); + + /* - Configuration Parameters */ + SARLUC(irqLevel); + SARLUC(spreadingCode); + SARLUC(channelSet); + SARLUC(channelNumber); + SARLUS(radioNodeId); + SARLUCN(_9, 2); + SARLUC(scramblingDisable); + SARLUC(radioType); + SARLUS(routerId); + SARLUCN(_10, 9); + SARLUC(txAttenuation); + SARLUIA(systemId); + SARLUS(globalChecksum); + SARLUCN(_11, 4); + SARLUS(maxDatagramSize); + SARLUS(maxFrameSize); + SARLUC(maxRetries); + SARLUC(receiveMode); + SARLUC(priority); + SARLUC(rootOrRepeater); + SARLUCN(specifiedRouter, 6); + SARLUS(fastPollPeriod); + SARLUC(pollDecay); + SARLUSA(fastPollDelay); + SARLUC(arlThreshold); + SARLUC(arlDecay); + SARLUCN(_12, 1); + SARLUS(specRouterTimeout); + SARLUCN(_13, 5); + + /* Scrambled Area */ + SARLUIA(SID); + SARLUCN(encryptionKey, 12); + SARLUIA(_14); + SARLUSA(waitTime); + SARLUSA(lParameter); + SARLUCN(_15, 3); + SARLUS(headerSize); + SARLUS(sectionChecksum); + + SARLUC(registrationMode); + SARLUC(registrationFill); + SARLUS(pollPeriod); + SARLUS(refreshPeriod); + SARLSTR(name, 16); + SARLUCN(NID, 6); + SARLUC(localTalkAddress); + SARLUC(codeFormat); + SARLUC(numChannels); + SARLUC(channel1); + SARLUC(channel2); + SARLUC(channel3); + SARLUC(channel4); + SARLUCN(SSCode, 59); + +/* SARLUCN( _16, 0x140); + */ + /* Statistics Block - 0x0300 */ + SARLUC(hostcpuLock); + SARLUC(lancpuLock); + SARLUCN(resetTime, 18); + SARLUIA(numDatagramsTransmitted); + SARLUIA(numReTransmissions); + SARLUIA(numFramesDiscarded); + SARLUIA(numDatagramsReceived); + SARLUIA(numDuplicateReceivedFrames); + SARLUIA(numDatagramsDiscarded); + SARLUS(maxNumReTransmitDatagram); + SARLUS(maxNumReTransmitFrames); + SARLUS(maxNumConsecutiveDuplicateFrames); + /* misaligned here so we have to go to characters */ + SARLUIA(numBytesTransmitted); + SARLUIA(numBytesReceived); + SARLUIA(numCRCErrors); + SARLUIA(numLengthErrors); + SARLUIA(numAbortErrors); + SARLUIA(numTXUnderruns); + SARLUIA(numRXOverruns); + SARLUIA(numHoldOffs); + SARLUIA(numFramesTransmitted); + SARLUIA(numFramesReceived); + SARLUIA(numReceiveFramesLost); + SARLUIA(numRXBufferOverflows); + SARLUIA(numFramesDiscardedAddrMismatch); + SARLUIA(numFramesDiscardedSIDMismatch); + SARLUIA(numPollsTransmistted); + SARLUIA(numPollAcknowledges); + SARLUIA(numStatusTimeouts); + SARLUIA(numNACKReceived); + SARLUS(auxCmd); + SARLUCN(dumpPtr, 4); + SARLUC(dumpVal); + SARLUC(wireTest); + + /* next 4 seems too long for procfs, over single page ? + SARLUCN( _17, 0x86); + SARLUCN( txBuffer, 0x800); + SARLUCN( rxBuffer, 0x800); + SARLUCN( _18, 0x0bff); + */ + + pos += sprintf(arlan_drive_info + pos, "rxRing\t=\t0x"); + for (i = 0; i < 0x50; i++) + pos += sprintf(arlan_drive_info + pos, "%02x", ((char *) priva->conf)[priva->conf->rxOffset + i]); + pos += sprintf(arlan_drive_info + pos, "\n"); + + SARLUC(configStatus); + SARLUC(_22); + SARLUC(progIOCtrl); + SARLUC(shareMBase); + SARLUC(controlRegister); + + pos += sprintf(arlan_drive_info + pos, " total %d chars\n", pos); + if (ctl) + if (ctl->procname) + pos += sprintf(arlan_drive_info + pos, " driver name : %s\n", ctl->procname); +final: + *lenp = pos; + + if (!write) + retv = proc_dostring(ctl, write, buffer, lenp, ppos); + else + { + *lenp = 0; + return -1; + } + return retv; +} + + +static int arlan_sysctl_info161719(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int i; + int retv, pos, devnum; + struct arlan_private *priva = NULL; + + pos = 0; + devnum = ctl->procname[5] - '0'; + if (arlan_device[devnum] == NULL) + { + pos += sprintf(arlan_drive_info + pos, "No device found here \n"); + goto final; + } + else + priva = netdev_priv(arlan_device[devnum]); + if (priva == NULL) + { + printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); + return -1; + } + memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); + SARLUCN(_16, 0xC0); + SARLUCN(_17, 0x6A); + SARLUCN(_18, 14); + SARLUCN(_19, 0x86); + SARLUCN(_21, 0x3fd); + +final: + *lenp = pos; + retv = proc_dostring(ctl, write, buffer, lenp, ppos); + return retv; +} + +static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int i; + int retv, pos, devnum; + struct arlan_private *priva = NULL; + + pos = 0; + devnum = ctl->procname[5] - '0'; + if (arlan_device[devnum] == NULL) + { + pos += sprintf(arlan_drive_info + pos, "No device found here \n"); + goto final; + } + else + priva = netdev_priv(arlan_device[devnum]); + if (priva == NULL) + { + printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); + return -1; + } + memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); + SARLBNpln(u_char, txBuffer, 0x800); +final: + *lenp = pos; + retv = proc_dostring(ctl, write, buffer, lenp, ppos); + return retv; +} + +static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int i; + int retv, pos, devnum; + struct arlan_private *priva = NULL; + + pos = 0; + devnum = ctl->procname[5] - '0'; + if (arlan_device[devnum] == NULL) + { + pos += sprintf(arlan_drive_info + pos, "No device found here \n"); + goto final; + } else + priva = netdev_priv(arlan_device[devnum]); + if (priva == NULL) + { + printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); + return -1; + } + memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); + SARLBNpln(u_char, rxBuffer, 0x800); +final: + *lenp = pos; + retv = proc_dostring(ctl, write, buffer, lenp, ppos); + return retv; +} + +static int arlan_sysctl_info18(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int i; + int retv, pos, devnum; + struct arlan_private *priva = NULL; + + pos = 0; + devnum = ctl->procname[5] - '0'; + if (arlan_device[devnum] == NULL) + { + pos += sprintf(arlan_drive_info + pos, "No device found here \n"); + goto final; + } + else + priva = netdev_priv(arlan_device[devnum]); + if (priva == NULL) + { + printk(KERN_WARNING " Could not find the device private in arlan procsys, bad\n "); + return -1; + } + memcpy_fromio(priva->conf, priva->card, sizeof(struct arlan_shmem)); + SARLBNpln(u_char, _18, 0x800); + +final: + *lenp = pos; + retv = proc_dostring(ctl, write, buffer, lenp, ppos); + return retv; +} + + +#endif /* #ifdef ARLAN_PROC_SHM_DUMP */ + + +static char conf_reset_result[200]; + +static int arlan_configure(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int pos = 0; + int devnum = ctl->procname[6] - '0'; + struct arlan_private *priv; + + if (devnum < 0 || devnum > MAX_ARLANS - 1) + { + printk(KERN_WARNING "too strange devnum in procfs parse\n "); + return -1; + } + else if (arlan_device[devnum] != NULL) + { + priv = netdev_priv(arlan_device[devnum]); + + arlan_command(arlan_device[devnum], ARLAN_COMMAND_CLEAN_AND_CONF); + } + else + return -1; + + *lenp = pos; + return proc_dostring(ctl, write, buffer, lenp, ppos); +} + +static int arlan_sysctl_reset(ctl_table * ctl, int write, + void __user *buffer, size_t * lenp, loff_t *ppos) +{ + int pos = 0; + int devnum = ctl->procname[5] - '0'; + struct arlan_private *priv; + + if (devnum < 0 || devnum > MAX_ARLANS - 1) + { + printk(KERN_WARNING "too strange devnum in procfs parse\n "); + return -1; + } + else if (arlan_device[devnum] != NULL) + { + priv = netdev_priv(arlan_device[devnum]); + arlan_command(arlan_device[devnum], ARLAN_COMMAND_CLEAN_AND_RESET); + + } else + return -1; + *lenp = pos + 3; + return proc_dostring(ctl, write, buffer, lenp, ppos); +} + + +/* Place files in /proc/sys/dev/arlan */ +#define CTBLN(num,card,nam) \ + { .ctl_name = num,\ + .procname = #nam,\ + .data = &(arlan_conf[card].nam),\ + .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec} +#ifdef ARLAN_DEBUGGING + +#define ARLAN_PROC_DEBUG_ENTRIES \ + { .ctl_name = 48, .procname = "entry_exit_debug",\ + .data = &arlan_entry_and_exit_debug,\ + .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec},\ + { .ctl_name = 49, .procname = "debug", .data = &arlan_debug,\ + .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec}, +#else +#define ARLAN_PROC_DEBUG_ENTRIES +#endif + +#define ARLAN_SYSCTL_TABLE_TOTAL(cardNo)\ + CTBLN(1,cardNo,spreadingCode),\ + CTBLN(2,cardNo, channelNumber),\ + CTBLN(3,cardNo, scramblingDisable),\ + CTBLN(4,cardNo, txAttenuation),\ + CTBLN(5,cardNo, systemId), \ + CTBLN(6,cardNo, maxDatagramSize),\ + CTBLN(7,cardNo, maxFrameSize),\ + CTBLN(8,cardNo, maxRetries),\ + CTBLN(9,cardNo, receiveMode),\ + CTBLN(10,cardNo, priority),\ + CTBLN(11,cardNo, rootOrRepeater),\ + CTBLN(12,cardNo, SID),\ + CTBLN(13,cardNo, registrationMode),\ + CTBLN(14,cardNo, registrationFill),\ + CTBLN(15,cardNo, localTalkAddress),\ + CTBLN(16,cardNo, codeFormat),\ + CTBLN(17,cardNo, numChannels),\ + CTBLN(18,cardNo, channel1),\ + CTBLN(19,cardNo, channel2),\ + CTBLN(20,cardNo, channel3),\ + CTBLN(21,cardNo, channel4),\ + CTBLN(22,cardNo, txClear),\ + CTBLN(23,cardNo, txRetries),\ + CTBLN(24,cardNo, txRouting),\ + CTBLN(25,cardNo, txScrambled),\ + CTBLN(26,cardNo, rxParameter),\ + CTBLN(27,cardNo, txTimeoutMs),\ + CTBLN(28,cardNo, waitCardTimeout),\ + CTBLN(29,cardNo, channelSet), \ + {.ctl_name = 30, .procname = "name",\ + .data = arlan_conf[cardNo].siteName,\ + .maxlen = 16, .mode = 0600, .proc_handler = &proc_dostring},\ + CTBLN(31,cardNo,waitTime),\ + CTBLN(32,cardNo,lParameter),\ + CTBLN(33,cardNo,_15),\ + CTBLN(34,cardNo,headerSize),\ + CTBLN(36,cardNo,tx_delay_ms),\ + CTBLN(37,cardNo,retries),\ + CTBLN(38,cardNo,ReTransmitPacketMaxSize),\ + CTBLN(39,cardNo,waitReTransmitPacketMaxSize),\ + CTBLN(40,cardNo,fastReTransCount),\ + CTBLN(41,cardNo,driverRetransmissions),\ + CTBLN(42,cardNo,txAckTimeoutMs),\ + CTBLN(43,cardNo,registrationInterrupts),\ + CTBLN(44,cardNo,hardwareType),\ + CTBLN(45,cardNo,radioType),\ + CTBLN(46,cardNo,writeEEPROM),\ + CTBLN(47,cardNo,writeRadioType),\ + ARLAN_PROC_DEBUG_ENTRIES\ + CTBLN(50,cardNo,in_speed),\ + CTBLN(51,cardNo,out_speed),\ + CTBLN(52,cardNo,in_speed10),\ + CTBLN(53,cardNo,out_speed10),\ + CTBLN(54,cardNo,in_speed_max),\ + CTBLN(55,cardNo,out_speed_max),\ + CTBLN(56,cardNo,measure_rate),\ + CTBLN(57,cardNo,pre_Command_Wait),\ + CTBLN(58,cardNo,rx_tweak1),\ + CTBLN(59,cardNo,rx_tweak2),\ + CTBLN(60,cardNo,tx_queue_len),\ + + + +static ctl_table arlan_conf_table0[] = +{ + ARLAN_SYSCTL_TABLE_TOTAL(0) + +#ifdef ARLAN_PROC_SHM_DUMP + { + .ctl_name = 150, + .procname = "arlan0-txRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_infotxRing, + }, + { + .ctl_name = 151, + .procname = "arlan0-rxRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_inforxRing, + }, + { + .ctl_name = 152, + .procname = "arlan0-18", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info18, + }, + { + .ctl_name = 153, + .procname = "arlan0-ring", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info161719, + }, + { + .ctl_name = 154, + .procname = "arlan0-shm-cpy", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info, + }, +#endif + { + .ctl_name = 155, + .procname = "config0", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_configure + }, + { + .ctl_name = 156, + .procname = "reset0", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_sysctl_reset, + }, + { .ctl_name = 0 } +}; + +static ctl_table arlan_conf_table1[] = +{ + + ARLAN_SYSCTL_TABLE_TOTAL(1) + +#ifdef ARLAN_PROC_SHM_DUMP + { + .ctl_name = 150, + .procname = "arlan1-txRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_infotxRing, + }, + { + .ctl_name = 151, + .procname = "arlan1-rxRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_inforxRing, + }, + { + .ctl_name = 152, + .procname = "arlan1-18", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info18, + }, + { + .ctl_name = 153, + .procname = "arlan1-ring", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info161719, + }, + { + .ctl_name = 154, + .procname = "arlan1-shm-cpy", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info, + }, +#endif + { + .ctl_name = 155, + .procname = "config1", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_configure, + }, + { + .ctl_name = 156, + .procname = "reset1", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_sysctl_reset, + }, + { .ctl_name = 0 } +}; + +static ctl_table arlan_conf_table2[] = +{ + + ARLAN_SYSCTL_TABLE_TOTAL(2) + +#ifdef ARLAN_PROC_SHM_DUMP + { + .ctl_name = 150, + .procname = "arlan2-txRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_infotxRing, + }, + { + .ctl_name = 151, + .procname = "arlan2-rxRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_inforxRing, + }, + { + .ctl_name = 152, + .procname = "arlan2-18", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info18, + }, + { + .ctl_name = 153, + .procname = "arlan2-ring", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info161719, + }, + { + .ctl_name = 154, + .procname = "arlan2-shm-cpy", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info, + }, +#endif + { + .ctl_name = 155, + .procname = "config2", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_configure, + }, + { + .ctl_name = 156, + .procname = "reset2", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_sysctl_reset, + }, + { .ctl_name = 0 } +}; + +static ctl_table arlan_conf_table3[] = +{ + + ARLAN_SYSCTL_TABLE_TOTAL(3) + +#ifdef ARLAN_PROC_SHM_DUMP + { + .ctl_name = 150, + .procname = "arlan3-txRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_infotxRing, + }, + { + .ctl_name = 151, + .procname = "arlan3-rxRing", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_inforxRing, + }, + { + .ctl_name = 152, + .procname = "arlan3-18", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info18, + }, + { + .ctl_name = 153, + .procname = "arlan3-ring", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info161719, + }, + { + .ctl_name = 154, + .procname = "arlan3-shm-cpy", + .data = &arlan_drive_info, + .maxlen = ARLAN_STR_SIZE, + .mode = 0400, + .proc_handler = &arlan_sysctl_info, + }, +#endif + { + .ctl_name = 155, + .procname = "config3", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_configure, + }, + { + .ctl_name = 156, + .procname = "reset3", + .data = &conf_reset_result, + .maxlen = 100, + .mode = 0400, + .proc_handler = &arlan_sysctl_reset, + }, + { .ctl_name = 0 } +}; + + + +static ctl_table arlan_table[] = +{ + { + .ctl_name = 0, + .procname = "arlan0", + .maxlen = 0, + .mode = 0600, + .child = arlan_conf_table0, + }, + { + .ctl_name = 0, + .procname = "arlan1", + .maxlen = 0, + .mode = 0600, + .child = arlan_conf_table1, + }, + { + .ctl_name = 0, + .procname = "arlan2", + .maxlen = 0, + .mode = 0600, + .child = arlan_conf_table2, + }, + { + .ctl_name = 0, + .procname = "arlan3", + .maxlen = 0, + .mode = 0600, + .child = arlan_conf_table3, + }, + { .ctl_name = 0 } +}; + +#else + +static ctl_table arlan_table[MAX_ARLANS + 1] = +{ + { .ctl_name = 0 } +}; +#endif + + +// static int mmtu = 1234; + +static ctl_table arlan_root_table[] = +{ + { + .ctl_name = CTL_ARLAN, + .procname = "arlan", + .maxlen = 0, + .mode = 0555, + .child = arlan_table, + }, + { .ctl_name = 0 } +}; + +/* Make sure that /proc/sys/dev is there */ +//static ctl_table arlan_device_root_table[] = +//{ +// {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table}, +// {0} +//}; + + +static struct ctl_table_header *arlan_device_sysctl_header; + +int __init init_arlan_proc(void) +{ + + int i = 0; + if (arlan_device_sysctl_header) + return 0; + for (i = 0; i < MAX_ARLANS && arlan_device[i]; i++) + arlan_table[i].ctl_name = i + 1; + arlan_device_sysctl_header = register_sysctl_table(arlan_root_table); + if (!arlan_device_sysctl_header) + return -1; + + return 0; + +} + +void __exit cleanup_arlan_proc(void) +{ + unregister_sysctl_table(arlan_device_sysctl_header); + arlan_device_sysctl_header = NULL; + +} +#endif diff --git a/drivers/staging/arlan/arlan.h b/drivers/staging/arlan/arlan.h new file mode 100644 index 000000000000..fb3ad51a1caf --- /dev/null +++ b/drivers/staging/arlan/arlan.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 1997 Cullen Jennings + * Copyright (C) 1998 Elmer.Joandi@ut.ee, +37-255-13500 + * GNU General Public License applies + */ + +#include +#include +#include +#include +#include /* For the statistics structure. */ +#include /* For ARPHRD_ETHER */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +//#define ARLAN_DEBUGGING 1 + +#define ARLAN_PROC_INTERFACE +#define MAX_ARLANS 4 /* not more than 4 ! */ +#define ARLAN_PROC_SHM_DUMP /* shows all card registers, makes driver way larger */ + +#define ARLAN_MAX_MULTICAST_ADDRS 16 +#define ARLAN_RCV_CLEAN 0 +#define ARLAN_RCV_PROMISC 1 +#define ARLAN_RCV_CONTROL 2 + +#ifdef CONFIG_PROC_FS +extern int init_arlan_proc(void); +extern void cleanup_arlan_proc(void); +#else +#define init_arlan_proc() ({ 0; }) +#define cleanup_arlan_proc() do { } while (0) +#endif + +extern struct net_device *arlan_device[MAX_ARLANS]; +extern int arlan_debug; +extern int arlan_entry_debug; +extern int arlan_exit_debug; +extern int testMemory; +extern int arlan_command(struct net_device * dev, int command); + +#define SIDUNKNOWN -1 +#define radioNodeIdUNKNOWN -1 +#define irqUNKNOWN 0 +#define debugUNKNOWN 0 +#define testMemoryUNKNOWN 1 +#define spreadingCodeUNKNOWN 0 +#define channelNumberUNKNOWN 0 +#define channelSetUNKNOWN 0 +#define systemIdUNKNOWN -1 +#define registrationModeUNKNOWN -1 + + +#define IFDEBUG( L ) if ( (L) & arlan_debug ) +#define ARLAN_FAKE_HDR_LEN 12 + +#ifdef ARLAN_DEBUGGING + #define DEBUG 1 + #define ARLAN_ENTRY_EXIT_DEBUGGING 1 + #define ARLAN_DEBUG(a,b) printk(KERN_DEBUG a, b) +#else + #define ARLAN_DEBUG(a,b) +#endif + +#define ARLAN_SHMEM_SIZE 0x2000 + +struct arlan_shmem +{ + /* Header Signature */ + volatile char textRegion[48]; + volatile u_char resetFlag; + volatile u_char diagnosticInfo; + volatile u_short diagnosticOffset; + volatile u_char _1[12]; + volatile u_char lanCardNodeId[6]; + volatile u_char broadcastAddress[6]; + volatile u_char hardwareType; + volatile u_char majorHardwareVersion; + volatile u_char minorHardwareVersion; + volatile u_char radioModule;// shows EEPROM, can be overridden at 0x111 + volatile u_char defaultChannelSet; // shows EEProm, can be overriiden at 0x10A + volatile u_char _2[47]; + + /* Control/Status Block - 0x0080 */ + volatile u_char interruptInProgress; /* not used by lancpu */ + volatile u_char cntrlRegImage; /* not used by lancpu */ + volatile u_char _3[13]; + volatile u_char dumpByte; + volatile u_char commandByte; /* non-zero = active */ + volatile u_char commandParameter[15]; + + /* Receive Status - 0x00a0 */ + volatile u_char rxStatus; /* 1- data, 2-control, 0xff - registr change */ + volatile u_char rxFrmType; + volatile u_short rxOffset; + volatile u_short rxLength; + volatile u_char rxSrc[6]; + volatile u_char rxBroadcastFlag; + volatile u_char rxQuality; + volatile u_char scrambled; + volatile u_char _4[1]; + + /* Transmit Status - 0x00b0 */ + volatile u_char txStatus; + volatile u_char txAckQuality; + volatile u_char numRetries; + volatile u_char _5[14]; + volatile u_char registeredRouter[6]; + volatile u_char backboneRouter[6]; + volatile u_char registrationStatus; + volatile u_char configuredStatusFlag; + volatile u_char _6[1]; + volatile u_char ultimateDestAddress[6]; + volatile u_char immedDestAddress[6]; + volatile u_char immedSrcAddress[6]; + volatile u_short rxSequenceNumber; + volatile u_char assignedLocaltalkAddress; + volatile u_char _7[27]; + + /* System Parameter Block */ + + /* - Driver Parameters (Novell Specific) */ + + volatile u_short txTimeout; + volatile u_short transportTime; + volatile u_char _8[4]; + + /* - Configuration Parameters */ + volatile u_char irqLevel; + volatile u_char spreadingCode; + volatile u_char channelSet; + volatile u_char channelNumber; + volatile u_short radioNodeId; + volatile u_char _9[2]; + volatile u_char scramblingDisable; + volatile u_char radioType; + volatile u_short routerId; + volatile u_char _10[9]; + volatile u_char txAttenuation; + volatile u_char systemId[4]; + volatile u_short globalChecksum; + volatile u_char _11[4]; + volatile u_short maxDatagramSize; + volatile u_short maxFrameSize; + volatile u_char maxRetries; + volatile u_char receiveMode; + volatile u_char priority; + volatile u_char rootOrRepeater; + volatile u_char specifiedRouter[6]; + volatile u_short fastPollPeriod; + volatile u_char pollDecay; + volatile u_char fastPollDelay[2]; + volatile u_char arlThreshold; + volatile u_char arlDecay; + volatile u_char _12[1]; + volatile u_short specRouterTimeout; + volatile u_char _13[5]; + + /* Scrambled Area */ + volatile u_char SID[4]; + volatile u_char encryptionKey[12]; + volatile u_char _14[2]; + volatile u_char waitTime[2]; + volatile u_char lParameter[2]; + volatile u_char _15[3]; + volatile u_short headerSize; + volatile u_short sectionChecksum; + + volatile u_char registrationMode; + volatile u_char registrationFill; + volatile u_short pollPeriod; + volatile u_short refreshPeriod; + volatile u_char name[16]; + volatile u_char NID[6]; + volatile u_char localTalkAddress; + volatile u_char codeFormat; + volatile u_char numChannels; + volatile u_char channel1; + volatile u_char channel2; + volatile u_char channel3; + volatile u_char channel4; + volatile u_char SSCode[59]; + + volatile u_char _16[0xC0]; + volatile u_short auxCmd; + volatile u_char dumpPtr[4]; + volatile u_char dumpVal; + volatile u_char _17[0x6A]; + volatile u_char wireTest; + volatile u_char _18[14]; + + /* Statistics Block - 0x0300 */ + volatile u_char hostcpuLock; + volatile u_char lancpuLock; + volatile u_char resetTime[18]; + + volatile u_char numDatagramsTransmitted[4]; + volatile u_char numReTransmissions[4]; + volatile u_char numFramesDiscarded[4]; + volatile u_char numDatagramsReceived[4]; + volatile u_char numDuplicateReceivedFrames[4]; + volatile u_char numDatagramsDiscarded[4]; + + volatile u_short maxNumReTransmitDatagram; + volatile u_short maxNumReTransmitFrames; + volatile u_short maxNumConsecutiveDuplicateFrames; + /* misaligned here so we have to go to characters */ + + volatile u_char numBytesTransmitted[4]; + volatile u_char numBytesReceived[4]; + volatile u_char numCRCErrors[4]; + volatile u_char numLengthErrors[4]; + volatile u_char numAbortErrors[4]; + volatile u_char numTXUnderruns[4]; + volatile u_char numRXOverruns[4]; + volatile u_char numHoldOffs[4]; + volatile u_char numFramesTransmitted[4]; + volatile u_char numFramesReceived[4]; + volatile u_char numReceiveFramesLost[4]; + volatile u_char numRXBufferOverflows[4]; + volatile u_char numFramesDiscardedAddrMismatch[4]; + volatile u_char numFramesDiscardedSIDMismatch[4]; + volatile u_char numPollsTransmistted[4]; + volatile u_char numPollAcknowledges[4]; + volatile u_char numStatusTimeouts[4]; + volatile u_char numNACKReceived[4]; + + volatile u_char _19[0x86]; + + volatile u_char txBuffer[0x800]; + volatile u_char rxBuffer[0x800]; + + volatile u_char _20[0x800]; + volatile u_char _21[0x3fb]; + volatile u_char configStatus; + volatile u_char _22; + volatile u_char progIOCtrl; + volatile u_char shareMBase; + volatile u_char controlRegister; +}; + +struct arlan_conf_stru { + int spreadingCode; + int channelSet; + int channelNumber; + int scramblingDisable; + int txAttenuation; + int systemId; + int maxDatagramSize; + int maxFrameSize; + int maxRetries; + int receiveMode; + int priority; + int rootOrRepeater; + int SID; + int radioNodeId; + int registrationMode; + int registrationFill; + int localTalkAddress; + int codeFormat; + int numChannels; + int channel1; + int channel2; + int channel3; + int channel4; + int txClear; + int txRetries; + int txRouting; + int txScrambled; + int rxParameter; + int txTimeoutMs; + int txAckTimeoutMs; + int waitCardTimeout; + int waitTime; + int lParameter; + int _15; + int headerSize; + int retries; + int tx_delay_ms; + int waitReTransmitPacketMaxSize; + int ReTransmitPacketMaxSize; + int fastReTransCount; + int driverRetransmissions; + int registrationInterrupts; + int hardwareType; + int radioType; + int writeRadioType; + int writeEEPROM; + char siteName[17]; + int measure_rate; + int in_speed; + int out_speed; + int in_speed10; + int out_speed10; + int in_speed_max; + int out_speed_max; + int pre_Command_Wait; + int rx_tweak1; + int rx_tweak2; + int tx_queue_len; +}; + +extern struct arlan_conf_stru arlan_conf[MAX_ARLANS]; + +struct TxParam +{ + volatile short offset; + volatile short length; + volatile u_char dest[6]; + volatile unsigned char clear; + volatile unsigned char retries; + volatile unsigned char routing; + volatile unsigned char scrambled; +}; + +#define TX_RING_SIZE 2 +/* Information that need to be kept for each board. */ +struct arlan_private { + struct arlan_shmem __iomem * card; + struct arlan_shmem * conf; + + struct arlan_conf_stru * Conf; + int bad; + int reset; + unsigned long lastReset; + struct timer_list timer; + struct timer_list tx_delay_timer; + struct timer_list tx_retry_timer; + struct timer_list rx_check_timer; + + int registrationLostCount; + int reRegisterExp; + int irq_test_done; + + struct TxParam txRing[TX_RING_SIZE]; + char reTransmitBuff[0x800]; + int txLast; + unsigned ReTransmitRequested; + unsigned long tx_done_delayed; + unsigned long registrationLastSeen; + + unsigned long tx_last_sent; + unsigned long tx_last_cleared; + unsigned long retransmissions; + unsigned long interrupt_ack_requested; + spinlock_t lock; + unsigned long waiting_command_mask; + unsigned long card_polling_interval; + unsigned long last_command_buff_free_time; + + int under_reset; + int under_config; + int rx_command_given; + int tx_command_given; + unsigned long interrupt_processing_active; + unsigned long last_rx_int_ack_time; + unsigned long in_bytes; + unsigned long out_bytes; + unsigned long in_time; + unsigned long out_time; + unsigned long in_time10; + unsigned long out_time10; + unsigned long in_bytes10; + unsigned long out_bytes10; + int init_etherdev_alloc; +}; + + + +#define ARLAN_CLEAR 0x00 +#define ARLAN_RESET 0x01 +#define ARLAN_CHANNEL_ATTENTION 0x02 +#define ARLAN_INTERRUPT_ENABLE 0x04 +#define ARLAN_CLEAR_INTERRUPT 0x08 +#define ARLAN_POWER 0x40 +#define ARLAN_ACCESS 0x80 + +#define ARLAN_COM_CONF 0x01 +#define ARLAN_COM_RX_ENABLE 0x03 +#define ARLAN_COM_RX_ABORT 0x04 +#define ARLAN_COM_TX_ENABLE 0x05 +#define ARLAN_COM_TX_ABORT 0x06 +#define ARLAN_COM_NOP 0x07 +#define ARLAN_COM_STANDBY 0x08 +#define ARLAN_COM_ACTIVATE 0x09 +#define ARLAN_COM_GOTO_SLOW_POLL 0x0a +#define ARLAN_COM_INT 0x80 + + +#define TXLAST(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[((struct arlan_private *)netdev_priv(dev))->txLast]) +#define TXHEAD(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[0]) +#define TXTAIL(dev) (((struct arlan_private *)netdev_priv(dev))->txRing[1]) + +#define TXBuffStart(dev) offsetof(struct arlan_shmem, txBuffer) +#define TXBuffEnd(dev) offsetof(struct arlan_shmem, xxBuffer) + +#define READSHM(to,from,atype) {\ + atype tmp;\ + memcpy_fromio(&(tmp),&(from),sizeof(atype));\ + to = tmp;\ + } + +#define READSHMEM(from,atype)\ + atype from; \ + READSHM(from, arlan->from, atype); + +#define WRITESHM(to,from,atype) \ + { atype tmpSHM = from;\ + memcpy_toio(&(to),&tmpSHM,sizeof(atype));\ + } + +#define DEBUGSHM(levelSHM,stringSHM,stuff,atype) \ + { atype tmpSHM; \ + memcpy_fromio(&tmpSHM,&(stuff),sizeof(atype));\ + IFDEBUG(levelSHM) printk(stringSHM,tmpSHM);\ + } + +#define WRITESHMB(to, val) \ + writeb(val,&(to)) +#define READSHMB(to) \ + readb(&(to)) +#define WRITESHMS(to, val) \ + writew(val,&(to)) +#define READSHMS(to) \ + readw(&(to)) +#define WRITESHMI(to, val) \ + writel(val,&(to)) +#define READSHMI(to) \ + readl(&(to)) + + + + + +#define registrationBad(dev)\ + ( ( READSHMB(((struct arlan_private *)netdev_priv(dev))->card->registrationMode) > 0) && \ + ( READSHMB(((struct arlan_private *)netdev_priv(dev))->card->registrationStatus) == 0) ) + + +#define readControlRegister(dev)\ + READSHMB(((struct arlan_private *)netdev_priv(dev))->card->cntrlRegImage) + +#define writeControlRegister(dev, v){\ + WRITESHMB(((struct arlan_private *)netdev_priv(dev))->card->cntrlRegImage ,((v) &0xF) );\ + WRITESHMB(((struct arlan_private *)netdev_priv(dev))->card->controlRegister ,(v) );} + + +#define arlan_interrupt_lancpu(dev) {\ + int cr; \ + \ + cr = readControlRegister(dev);\ + if (cr & ARLAN_CHANNEL_ATTENTION){ \ + writeControlRegister(dev, (cr & ~ARLAN_CHANNEL_ATTENTION));\ + }else \ + writeControlRegister(dev, (cr | ARLAN_CHANNEL_ATTENTION));\ +} + +#define clearChannelAttention(dev){ \ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_CHANNEL_ATTENTION);} +#define setHardwareReset(dev) {\ + writeControlRegister(dev,readControlRegister(dev) | ARLAN_RESET);} +#define clearHardwareReset(dev) {\ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_RESET);} +#define setInterruptEnable(dev){\ + writeControlRegister(dev,readControlRegister(dev) | ARLAN_INTERRUPT_ENABLE) ;} +#define clearInterruptEnable(dev){\ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_INTERRUPT_ENABLE) ;} +#define setClearInterrupt(dev){\ + writeControlRegister(dev,readControlRegister(dev) | ARLAN_CLEAR_INTERRUPT) ;} +#define clearClearInterrupt(dev){\ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_CLEAR_INTERRUPT);} +#define setPowerOff(dev){\ + writeControlRegister(dev,readControlRegister(dev) | (ARLAN_POWER && ARLAN_ACCESS));\ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_ACCESS);} +#define setPowerOn(dev){\ + writeControlRegister(dev,readControlRegister(dev) & ~(ARLAN_POWER)); } +#define arlan_lock_card_access(dev){\ + writeControlRegister(dev,readControlRegister(dev) & ~ARLAN_ACCESS);} +#define arlan_unlock_card_access(dev){\ + writeControlRegister(dev,readControlRegister(dev) | ARLAN_ACCESS ); } + + + + +#define ARLAN_COMMAND_RX 0x000001 +#define ARLAN_COMMAND_NOOP 0x000002 +#define ARLAN_COMMAND_NOOPINT 0x000004 +#define ARLAN_COMMAND_TX 0x000008 +#define ARLAN_COMMAND_CONF 0x000010 +#define ARLAN_COMMAND_RESET 0x000020 +#define ARLAN_COMMAND_TX_ABORT 0x000040 +#define ARLAN_COMMAND_RX_ABORT 0x000080 +#define ARLAN_COMMAND_POWERDOWN 0x000100 +#define ARLAN_COMMAND_POWERUP 0x000200 +#define ARLAN_COMMAND_SLOW_POLL 0x000400 +#define ARLAN_COMMAND_ACTIVATE 0x000800 +#define ARLAN_COMMAND_INT_ACK 0x001000 +#define ARLAN_COMMAND_INT_ENABLE 0x002000 +#define ARLAN_COMMAND_WAIT_NOW 0x004000 +#define ARLAN_COMMAND_LONG_WAIT_NOW 0x008000 +#define ARLAN_COMMAND_STANDBY 0x010000 +#define ARLAN_COMMAND_INT_RACK 0x020000 +#define ARLAN_COMMAND_INT_RENABLE 0x040000 +#define ARLAN_COMMAND_CONF_WAIT 0x080000 +#define ARLAN_COMMAND_TBUSY_CLEAR 0x100000 +#define ARLAN_COMMAND_CLEAN_AND_CONF (ARLAN_COMMAND_TX_ABORT\ + | ARLAN_COMMAND_RX_ABORT\ + | ARLAN_COMMAND_CONF) +#define ARLAN_COMMAND_CLEAN_AND_RESET (ARLAN_COMMAND_TX_ABORT\ + | ARLAN_COMMAND_RX_ABORT\ + | ARLAN_COMMAND_RESET) + + + +#define ARLAN_DEBUG_CHAIN_LOCKS 0x00001 +#define ARLAN_DEBUG_RESET 0x00002 +#define ARLAN_DEBUG_TIMING 0x00004 +#define ARLAN_DEBUG_CARD_STATE 0x00008 +#define ARLAN_DEBUG_TX_CHAIN 0x00010 +#define ARLAN_DEBUG_MULTICAST 0x00020 +#define ARLAN_DEBUG_HEADER_DUMP 0x00040 +#define ARLAN_DEBUG_INTERRUPT 0x00080 +#define ARLAN_DEBUG_STARTUP 0x00100 +#define ARLAN_DEBUG_SHUTDOWN 0x00200 + -- cgit v1.2.3-59-g8ed1b From 0234f84ebb00d36c48062befa5436eef36b71ccd Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 28 Oct 2009 16:06:56 -0400 Subject: wavelan: move driver to staging Move the wavelan driver to drivers/staging. This is another pre-802.11 driver that has seen virtually no non-API-fixup activity in years, and for which no active hardware is likely to still exist. This driver represents unnecessary ongoing maintenance for no clear benefit. This patch brought to you by the "hacking" session at the 2009 Kernel Summit in Tokyo, Japan... Acked-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 39 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/i82586.h | 413 --- drivers/net/wireless/i82593.h | 229 -- drivers/net/wireless/wavelan.c | 4383 ------------------------------ drivers/net/wireless/wavelan.h | 370 --- drivers/net/wireless/wavelan.p.h | 696 ----- drivers/net/wireless/wavelan_cs.c | 4635 -------------------------------- drivers/net/wireless/wavelan_cs.h | 386 --- drivers/net/wireless/wavelan_cs.p.h | 766 ------ drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 2 + drivers/staging/wavelan/Kconfig | 38 + drivers/staging/wavelan/Makefile | 2 + drivers/staging/wavelan/i82586.h | 413 +++ drivers/staging/wavelan/i82593.h | 229 ++ drivers/staging/wavelan/wavelan.c | 4383 ++++++++++++++++++++++++++++++ drivers/staging/wavelan/wavelan.h | 370 +++ drivers/staging/wavelan/wavelan.p.h | 696 +++++ drivers/staging/wavelan/wavelan_cs.c | 4635 ++++++++++++++++++++++++++++++++ drivers/staging/wavelan/wavelan_cs.h | 386 +++ drivers/staging/wavelan/wavelan_cs.p.h | 766 ++++++ 22 files changed, 11922 insertions(+), 11919 deletions(-) delete mode 100644 drivers/net/wireless/i82586.h delete mode 100644 drivers/net/wireless/i82593.h delete mode 100644 drivers/net/wireless/wavelan.c delete mode 100644 drivers/net/wireless/wavelan.h delete mode 100644 drivers/net/wireless/wavelan.p.h delete mode 100644 drivers/net/wireless/wavelan_cs.c delete mode 100644 drivers/net/wireless/wavelan_cs.h delete mode 100644 drivers/net/wireless/wavelan_cs.p.h create mode 100644 drivers/staging/wavelan/Kconfig create mode 100644 drivers/staging/wavelan/Makefile create mode 100644 drivers/staging/wavelan/i82586.h create mode 100644 drivers/staging/wavelan/i82593.h create mode 100644 drivers/staging/wavelan/wavelan.c create mode 100644 drivers/staging/wavelan/wavelan.h create mode 100644 drivers/staging/wavelan/wavelan.p.h create mode 100644 drivers/staging/wavelan/wavelan_cs.c create mode 100644 drivers/staging/wavelan/wavelan_cs.h create mode 100644 drivers/staging/wavelan/wavelan_cs.p.h (limited to 'drivers/staging') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d50b3bee9a9b..f94188f05a02 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -25,45 +25,6 @@ menuconfig WLAN_PRE80211 This option does not affect the kernel build, it only lets you choose drivers. -config WAVELAN - tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on ISA && WLAN_PRE80211 - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - ---help--- - The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is - a Radio LAN (wireless Ethernet-like Local Area Network) using the - radio frequencies 900 MHz and 2.4 GHz. - - If you want to use an ISA WaveLAN card under Linux, say Y and read - the Ethernet-HOWTO, available from - . Some more specific - information is contained in - and in the source code - . - - You will also need the wireless tools package available from - . - Please read the man pages contained therein. - - To compile this driver as a module, choose M here: the module will be - called wavelan. - -config PCMCIA_WAVELAN - tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on PCMCIA && WLAN_PRE80211 - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - help - Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA - (PC-card) wireless Ethernet networking card to your computer. This - driver is for the non-IEEE-802.11 Wavelan cards. - - To compile this driver as a module, choose M here: the module will be - called wavelan_cs. If unsure, say N. - config PCMCIA_NETWAVE tristate "Xircom Netwave AirSurfer Pcmcia wireless support" depends on PCMCIA && WLAN_PRE80211 diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index b56c70f4ca75..f4a7c8ae27ea 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -6,9 +6,7 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ # Obsolete cards -obj-$(CONFIG_WAVELAN) += wavelan.o obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o -obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o obj-$(CONFIG_HERMES) += orinoco/ diff --git a/drivers/net/wireless/i82586.h b/drivers/net/wireless/i82586.h deleted file mode 100644 index 5f65b250646f..000000000000 --- a/drivers/net/wireless/i82586.h +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor. - * - * See: - * Intel Microcommunications 1991 - * p1-1 to p1-37 - * Intel order No. 231658 - * ISBN 1-55512-119-5 - * - * Unfortunately, the above chapter mentions neither - * the System Configuration Pointer (SCP) nor the - * Intermediate System Configuration Pointer (ISCP), - * so we probably need to look elsewhere for the - * whole story -- some recommend the "Intel LAN - * Components manual" but I have neither a copy - * nor a full reference. But "elsewhere" may be - * in the same publication... - * The description of a later device, the - * "82596CA High-Performance 32-Bit Local Area Network - * Coprocessor", (ibid. p1-38 to p1-109) does mention - * the SCP and ISCP and also has an i82586 compatibility - * mode. Even more useful is "AP-235 An 82586 Data Link - * Driver" (ibid. p1-337 to p1-417). - */ - -#define I82586_MEMZ (64 * 1024) - -#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t)) - -#define ADDR_LEN 6 -#define I82586NULL 0xFFFF - -#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0) - -/* - * System Configuration Pointer (SCP). - */ -typedef struct scp_t scp_t; -struct scp_t -{ - unsigned short scp_sysbus; /* 82586 bus width: */ -#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */ -#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */ - unsigned short scp_junk[2]; /* Unused */ - unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */ - unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */ -}; - -/* - * Intermediate System Configuration Pointer (ISCP). - */ -typedef struct iscp_t iscp_t; -struct iscp_t -{ - unsigned short iscp_busy; /* set by CPU before first CA, */ - /* cleared by 82586 after read. */ - unsigned short iscp_offset; /* offset of SCB */ - unsigned short iscp_basel; /* base of SCB */ - unsigned short iscp_baseh; /* " */ -}; - -/* - * System Control Block (SCB). - * The 82586 writes its status to scb_status and then - * raises an interrupt to alert the CPU. - * The CPU writes a command to scb_command and - * then issues a Channel Attention (CA) to alert the 82586. - */ -typedef struct scb_t scb_t; -struct scb_t -{ - unsigned short scb_status; /* Status of 82586 */ -#define SCB_ST_INT (0xF << 12) /* Some of: */ -#define SCB_ST_CX (0x1 << 15) /* Cmd completed */ -#define SCB_ST_FR (0x1 << 14) /* Frame received */ -#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */ -#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */ -#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */ -#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */ -#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */ -#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */ -#define SCB_ST_CUS_ACTV (2 << 8) /* Active */ -#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */ -#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */ -#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */ -#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */ -#define SCB_ST_RUS_NRES (2 << 4) /* No resources */ -#define SCB_ST_RUS_RDY (4 << 4) /* Ready */ - unsigned short scb_command; /* Next command */ -#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */ -#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */ -#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */ -#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */ -#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */ -#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */ -#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */ -#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */ -#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */ -#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */ -#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */ -#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */ -#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */ -#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */ -#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */ -#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */ -#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */ -#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */ - unsigned short scb_cbl_offset; /* Offset of first command unit */ - /* Action Command */ - unsigned short scb_rfa_offset; /* Offset of first Receive */ - /* Frame Descriptor in the */ - /* Receive Frame Area */ - unsigned short scb_crcerrs; /* Properly aligned frames */ - /* received with a CRC error */ - unsigned short scb_alnerrs; /* Misaligned frames received */ - /* with a CRC error */ - unsigned short scb_rscerrs; /* Frames lost due to no space */ - unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */ -}; - -#define scboff(p,f) toff(scb_t, p, f) - -/* - * The eight Action Commands. - */ -typedef enum acmd_e acmd_e; -enum acmd_e -{ - acmd_nop = 0, /* Do nothing */ - acmd_ia_setup = 1, /* Load an (ethernet) address into the */ - /* 82586 */ - acmd_configure = 2, /* Update the 82586 operating parameters */ - acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */ - /* addresses into the 82586 */ - acmd_transmit = 4, /* Transmit a frame */ - acmd_tdr = 5, /* Perform a Time Domain Reflectometer */ - /* test on the serial link */ - acmd_dump = 6, /* Copy 82586 registers to memory */ - acmd_diagnose = 7, /* Run an internal self test */ -}; - -/* - * Generic Action Command header. - */ -typedef struct ach_t ach_t; -struct ach_t -{ - unsigned short ac_status; /* Command status: */ -#define AC_SFLD_C (0x1 << 15) /* Command completed */ -#define AC_SFLD_B (0x1 << 14) /* Busy executing */ -#define AC_SFLD_OK (0x1 << 13) /* Completed error free */ -#define AC_SFLD_A (0x1 << 12) /* Command aborted */ -#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */ -#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */ - /* during transmission */ -#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */ - /* (stopped) lost CTS */ -#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */ - /* (stopped) slow DMA */ -#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */ - /* other link traffic */ -#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */ - /* detect after last tx */ -#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */ - /* excessive collisions */ -#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */ - unsigned short ac_command; /* Command specifier: */ -#define AC_CFLD_EL (0x1 << 15) /* End of command list */ -#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */ -#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */ -#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */ - unsigned short ac_link; /* Next Action Command */ -}; - -#define acoff(p,f) toff(ach_t, p, f) - -/* - * The Nop Action Command. - */ -typedef struct ac_nop_t ac_nop_t; -struct ac_nop_t -{ - ach_t nop_h; -}; - -/* - * The IA-Setup Action Command. - */ -typedef struct ac_ias_t ac_ias_t; -struct ac_ias_t -{ - ach_t ias_h; - unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */ -}; - -/* - * The Configure Action Command. - */ -typedef struct ac_cfg_t ac_cfg_t; -struct ac_cfg_t -{ - ach_t cfg_h; - unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */ -#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0) - unsigned char cfg_fifolim; /* FIFO threshold */ -#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0) - unsigned char cfg_byte8; -#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */ -#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */ - /* external sync. */ - unsigned char cfg_byte9; -#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */ -#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */ -#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */ -#define AC_CFG_PLEN_2 0 /* 2 bytes */ -#define AC_CFG_PLEN_4 1 /* 4 bytes */ -#define AC_CFG_PLEN_8 2 /* 8 bytes */ -#define AC_CFG_PLEN_16 3 /* 16 bytes */ -#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */ - /* explicit in buffers */ -#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */ - unsigned char cfg_byte10; -#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */ - /* backoff method */ -#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */ -#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */ - unsigned char cfg_ifs; /* Interframe spacing */ - unsigned char cfg_slotl; /* Slot time (low byte) */ - unsigned char cfg_byte13; -#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */ -#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */ - unsigned char cfg_byte14; -#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */ -#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */ -#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */ -#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */ -#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */ -#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */ -#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */ -#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */ - unsigned char cfg_byte15; -#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */ - /* detect source */ -#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */ - /* filter in bit times */ -#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */ - /* sense source */ -#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */ - /* filter in bit times */ - unsigned short cfg_min_frm_len; -#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */ -}; - -/* - * The MC-Setup Action Command. - */ -typedef struct ac_mcs_t ac_mcs_t; -struct ac_mcs_t -{ - ach_t mcs_h; - unsigned short mcs_cnt; /* No. of bytes of MC addresses */ -#if 0 - unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */ - ... -#endif -}; - -#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ - -/* - * The Transmit Action Command. - */ -typedef struct ac_tx_t ac_tx_t; -struct ac_tx_t -{ - ach_t tx_h; - unsigned short tx_tbd_offset; /* Address of list of buffers. */ -#if 0 -Linux packets are passed down with the destination MAC address -and length/type field already prepended to the data, -so we do not need to insert it. Consistent with this -we must also set the AC_CFG_ALOC(..) flag during the -ac_cfg_t action command. - unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */ - unsigned short tx_length; /* The frame length */ -#endif /* 0 */ -}; - -/* - * The Time Domain Reflectometer Action Command. - */ -typedef struct ac_tdr_t ac_tdr_t; -struct ac_tdr_t -{ - ach_t tdr_h; - unsigned short tdr_result; /* Result. */ -#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */ -#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */ -#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */ -#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */ -#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */ - /* site in transmit */ - /* clock cycles */ -}; - -/* - * The Dump Action Command. - */ -typedef struct ac_dmp_t ac_dmp_t; -struct ac_dmp_t -{ - ach_t dmp_h; - unsigned short dmp_offset; /* Result. */ -}; - -/* - * Size of the result of the dump command. - */ -#define DUMPBYTES 170 - -/* - * The Diagnose Action Command. - */ -typedef struct ac_dgn_t ac_dgn_t; -struct ac_dgn_t -{ - ach_t dgn_h; -}; - -/* - * Transmit Buffer Descriptor (TBD). - */ -typedef struct tbd_t tbd_t; -struct tbd_t -{ - unsigned short tbd_status; /* Written by the CPU */ -#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */ - /* last for this frame */ -#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */ - /* bytes in this buffer */ - unsigned short tbd_next_bd_offset; /* Next in list */ - unsigned short tbd_bufl; /* Buffer address (low) */ - unsigned short tbd_bufh; /* " " (high) */ -}; - -/* - * Receive Buffer Descriptor (RBD). - */ -typedef struct rbd_t rbd_t; -struct rbd_t -{ - unsigned short rbd_status; /* Written by the 82586 */ -#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */ - /* last for this frame */ -#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */ -#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */ - /* bytes in this buffer */ - unsigned short rbd_next_rbd_offset; /* Next rbd in list */ - unsigned short rbd_bufl; /* Data pointer (low) */ - unsigned short rbd_bufh; /* " " (high) */ - unsigned short rbd_el_size; /* EL+Data buf. size */ -#define RBD_EL (0x1 << 15) /* This BD is the */ - /* last in the list */ -#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */ - /* buffer can hold */ -}; - -#define rbdoff(p,f) toff(rbd_t, p, f) - -/* - * Frame Descriptor (FD). - */ -typedef struct fd_t fd_t; -struct fd_t -{ - unsigned short fd_status; /* Written by the 82586 */ -#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */ -#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */ -#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */ -#define FD_STATUS_S11 (0x1 << 11) /* CRC error */ -#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */ -#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */ -#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */ -#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */ -#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */ - unsigned short fd_command; /* Command */ -#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */ -#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */ - unsigned short fd_link_offset; /* Next FD */ - unsigned short fd_rbd_offset; /* First RBD (data) */ - /* Prepared by CPU, */ - /* updated by 82586 */ -#if 0 -I think the rest is unused since we -have set AC_CFG_ALOC(..). However, just -in case, we leave the space. -#endif /* 0 */ - unsigned char fd_dest[ADDR_LEN]; /* Destination address */ - /* Written by 82586 */ - unsigned char fd_src[ADDR_LEN]; /* Source address */ - /* Written by 82586 */ - unsigned short fd_length; /* Frame length or type */ - /* Written by 82586 */ -}; - -#define fdoff(p,f) toff(fd_t, p, f) - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * For more details, see wavelan.c. - */ diff --git a/drivers/net/wireless/i82593.h b/drivers/net/wireless/i82593.h deleted file mode 100644 index afac5c7a323d..000000000000 --- a/drivers/net/wireless/i82593.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Definitions for Intel 82593 CSMA/CD Core LAN Controller - * The definitions are taken from the 1992 users manual with Intel - * order number 297125-001. - * - * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp - * - * Copyright 1994, Anders Klemets - * - * HISTORY - * i82593.h,v - * Revision 1.4 2005/11/4 09:15:00 baroniunas - * Modified copyright with permission of author as follows: - * - * "If I82539.H is the only file with my copyright statement - * that is included in the Source Forge project, then you have - * my approval to change the copyright statement to be a GPL - * license, in the way you proposed on October 10." - * - * Revision 1.1 1996/07/17 15:23:12 root - * Initial revision - * - * Revision 1.3 1995/04/05 15:13:58 adj - * Initial alpha release - * - * Revision 1.2 1994/06/16 23:57:31 klemets - * Mirrored all the fields in the configuration block. - * - * Revision 1.1 1994/06/02 20:25:34 klemets - * Initial revision - * - * - */ -#ifndef _I82593_H -#define _I82593_H - -/* Intel 82593 CSMA/CD Core LAN Controller */ - -/* Port 0 Command Register definitions */ - -/* Execution operations */ -#define OP0_NOP 0 /* CHNL = 0 */ -#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ -#define OP0_IA_SETUP 1 -#define OP0_CONFIGURE 2 -#define OP0_MC_SETUP 3 -#define OP0_TRANSMIT 4 -#define OP0_TDR 5 -#define OP0_DUMP 6 -#define OP0_DIAGNOSE 7 -#define OP0_TRANSMIT_NO_CRC 9 -#define OP0_RETRANSMIT 12 -#define OP0_ABORT 13 -/* Reception operations */ -#define OP0_RCV_ENABLE 8 -#define OP0_RCV_DISABLE 10 -#define OP0_STOP_RCV 11 -/* Status pointer control operations */ -#define OP0_FIX_PTR 15 /* CHNL = 1 */ -#define OP0_RLS_PTR 15 /* CHNL = 0 */ -#define OP0_RESET 14 - -#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ -#define CR0_STATUS_0 0x00 -#define CR0_STATUS_1 0x20 -#define CR0_STATUS_2 0x40 -#define CR0_STATUS_3 0x60 -#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ - -/* Port 0 Status Register definitions */ - -#define SR0_NO_RESULT 0 /* dummy */ -#define SR0_EVENT_MASK 0x0f -#define SR0_IA_SETUP_DONE 1 -#define SR0_CONFIGURE_DONE 2 -#define SR0_MC_SETUP_DONE 3 -#define SR0_TRANSMIT_DONE 4 -#define SR0_TDR_DONE 5 -#define SR0_DUMP_DONE 6 -#define SR0_DIAGNOSE_PASSED 7 -#define SR0_TRANSMIT_NO_CRC_DONE 9 -#define SR0_RETRANSMIT_DONE 12 -#define SR0_EXECUTION_ABORTED 13 -#define SR0_END_OF_FRAME 8 -#define SR0_RECEPTION_ABORTED 10 -#define SR0_DIAGNOSE_FAILED 15 -#define SR0_STOP_REG_HIT 11 - -#define SR0_CHNL (1 << 4) -#define SR0_EXECUTION (1 << 5) -#define SR0_RECEPTION (1 << 6) -#define SR0_INTERRUPT (1 << 7) -#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) - -#define SR3_EXEC_STATE_MASK 0x03 -#define SR3_EXEC_IDLE 0 -#define SR3_TX_ABORT_IN_PROGRESS 1 -#define SR3_EXEC_ACTIVE 2 -#define SR3_ABORT_IN_PROGRESS 3 -#define SR3_EXEC_CHNL (1 << 2) -#define SR3_STP_ON_NO_RSRC (1 << 3) -#define SR3_RCVING_NO_RSRC (1 << 4) -#define SR3_RCV_STATE_MASK 0x60 -#define SR3_RCV_IDLE 0x00 -#define SR3_RCV_READY 0x20 -#define SR3_RCV_ACTIVE 0x40 -#define SR3_RCV_STOP_IN_PROG 0x60 -#define SR3_RCV_CHNL (1 << 7) - -/* Port 1 Command Register definitions */ - -#define OP1_NOP 0 -#define OP1_SWIT_TO_PORT_0 1 -#define OP1_INT_DISABLE 2 -#define OP1_INT_ENABLE 3 -#define OP1_SET_TS 5 -#define OP1_RST_TS 7 -#define OP1_POWER_DOWN 8 -#define OP1_RESET_RING_MNGMT 11 -#define OP1_RESET 14 -#define OP1_SEL_RST 15 - -#define CR1_STATUS_4 0x00 -#define CR1_STATUS_5 0x20 -#define CR1_STATUS_6 0x40 -#define CR1_STOP_REG_UPDATE (1 << 7) - -/* Receive frame status bits */ - -#define RX_RCLD (1 << 0) -#define RX_IA_MATCH (1 << 1) -#define RX_NO_AD_MATCH (1 << 2) -#define RX_NO_SFD (1 << 3) -#define RX_SRT_FRM (1 << 7) -#define RX_OVRRUN (1 << 8) -#define RX_ALG_ERR (1 << 10) -#define RX_CRC_ERR (1 << 11) -#define RX_LEN_ERR (1 << 12) -#define RX_RCV_OK (1 << 13) -#define RX_TYP_LEN (1 << 15) - -/* Transmit status bits */ - -#define TX_NCOL_MASK 0x0f -#define TX_FRTL (1 << 4) -#define TX_MAX_COL (1 << 5) -#define TX_HRT_BEAT (1 << 6) -#define TX_DEFER (1 << 7) -#define TX_UND_RUN (1 << 8) -#define TX_LOST_CTS (1 << 9) -#define TX_LOST_CRS (1 << 10) -#define TX_LTCOL (1 << 11) -#define TX_OK (1 << 13) -#define TX_COLL (1 << 15) - -struct i82593_conf_block { - u_char fifo_limit : 4, - forgnesi : 1, - fifo_32 : 1, - d6mod : 1, - throttle_enb : 1; - u_char throttle : 6, - cntrxint : 1, - contin : 1; - u_char addr_len : 3, - acloc : 1, - preamb_len : 2, - loopback : 2; - u_char lin_prio : 3, - tbofstop : 1, - exp_prio : 3, - bof_met : 1; - u_char : 4, - ifrm_spc : 4; - u_char : 5, - slottim_low : 3; - u_char slottim_hi : 3, - : 1, - max_retr : 4; - u_char prmisc : 1, - bc_dis : 1, - : 1, - crs_1 : 1, - nocrc_ins : 1, - crc_1632 : 1, - : 1, - crs_cdt : 1; - u_char cs_filter : 3, - crs_src : 1, - cd_filter : 3, - : 1; - u_char : 2, - min_fr_len : 6; - u_char lng_typ : 1, - lng_fld : 1, - rxcrc_xf : 1, - artx : 1, - sarec : 1, - tx_jabber : 1, /* why is this called max_len in the manual? */ - hash_1 : 1, - lbpkpol : 1; - u_char : 6, - fdx : 1, - : 1; - u_char dummy_6 : 6, /* supposed to be ones */ - mult_ia : 1, - dis_bof : 1; - u_char dummy_1 : 1, /* supposed to be one */ - tx_ifs_retrig : 2, - mc_all : 1, - rcv_mon : 2, - frag_acpt : 1, - tstrttrs : 1; - u_char fretx : 1, - runt_eop : 1, - hw_sw_pin : 1, - big_endn : 1, - syncrqs : 1, - sttlen : 1, - tx_eop : 1, - rx_eop : 1; - u_char rbuf_size : 5, - rcvstop : 1, - : 2; -}; - -#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ - -#endif /* _I82593_H */ diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c deleted file mode 100644 index d634b2da3b84..000000000000 --- a/drivers/net/wireless/wavelan.c +++ /dev/null @@ -1,4383 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * Original copyright follows (also see the end of this file). - * See wavelan.p.h for details. - * - * - * - * AT&T GIS (nee NCR) WaveLAN card: - * An Ethernet-like radio transceiver - * controlled by an Intel 82586 coprocessor. - */ - -#include "wavelan.p.h" /* Private header */ - -/************************* MISC SUBROUTINES **************************/ -/* - * Subroutines which won't fit in one of the following category - * (WaveLAN modem or i82586) - */ - -/*------------------------------------------------------------------*/ -/* - * Translate irq number to PSA irq parameter - */ -static u8 wv_irq_to_psa(int irq) -{ - if (irq < 0 || irq >= ARRAY_SIZE(irqvals)) - return 0; - - return irqvals[irq]; -} - -/*------------------------------------------------------------------*/ -/* - * Translate PSA irq parameter to irq number - */ -static int __init wv_psa_to_irq(u8 irqval) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(irqvals); i++) - if (irqvals[i] == irqval) - return i; - - return -1; -} - -/********************* HOST ADAPTER SUBROUTINES *********************/ -/* - * Useful subroutines to manage the WaveLAN ISA interface - * - * One major difference with the PCMCIA hardware (except the port mapping) - * is that we have to keep the state of the Host Control Register - * because of the interrupt enable & bus size flags. - */ - -/*------------------------------------------------------------------*/ -/* - * Read from card's Host Adaptor Status Register. - */ -static inline u16 hasr_read(unsigned long ioaddr) -{ - return (inw(HASR(ioaddr))); -} /* hasr_read */ - -/*------------------------------------------------------------------*/ -/* - * Write to card's Host Adapter Command Register. - */ -static inline void hacr_write(unsigned long ioaddr, u16 hacr) -{ - outw(hacr, HACR(ioaddr)); -} /* hacr_write */ - -/*------------------------------------------------------------------*/ -/* - * Write to card's Host Adapter Command Register. Include a delay for - * those times when it is needed. - */ -static void hacr_write_slow(unsigned long ioaddr, u16 hacr) -{ - hacr_write(ioaddr, hacr); - /* delay might only be needed sometimes */ - mdelay(1); -} /* hacr_write_slow */ - -/*------------------------------------------------------------------*/ -/* - * Set the channel attention bit. - */ -static inline void set_chan_attn(unsigned long ioaddr, u16 hacr) -{ - hacr_write(ioaddr, hacr | HACR_CA); -} /* set_chan_attn */ - -/*------------------------------------------------------------------*/ -/* - * Reset, and then set host adaptor into default mode. - */ -static inline void wv_hacr_reset(unsigned long ioaddr) -{ - hacr_write_slow(ioaddr, HACR_RESET); - hacr_write(ioaddr, HACR_DEFAULT); -} /* wv_hacr_reset */ - -/*------------------------------------------------------------------*/ -/* - * Set the I/O transfer over the ISA bus to 8-bit mode - */ -static inline void wv_16_off(unsigned long ioaddr, u16 hacr) -{ - hacr &= ~HACR_16BITS; - hacr_write(ioaddr, hacr); -} /* wv_16_off */ - -/*------------------------------------------------------------------*/ -/* - * Set the I/O transfer over the ISA bus to 8-bit mode - */ -static inline void wv_16_on(unsigned long ioaddr, u16 hacr) -{ - hacr |= HACR_16BITS; - hacr_write(ioaddr, hacr); -} /* wv_16_on */ - -/*------------------------------------------------------------------*/ -/* - * Disable interrupts on the WaveLAN hardware. - * (called by wv_82586_stop()) - */ -static inline void wv_ints_off(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - - lp->hacr &= ~HACR_INTRON; - hacr_write(ioaddr, lp->hacr); -} /* wv_ints_off */ - -/*------------------------------------------------------------------*/ -/* - * Enable interrupts on the WaveLAN hardware. - * (called by wv_hw_reset()) - */ -static inline void wv_ints_on(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - - lp->hacr |= HACR_INTRON; - hacr_write(ioaddr, lp->hacr); -} /* wv_ints_on */ - -/******************* MODEM MANAGEMENT SUBROUTINES *******************/ -/* - * Useful subroutines to manage the modem of the WaveLAN - */ - -/*------------------------------------------------------------------*/ -/* - * Read the Parameter Storage Area from the WaveLAN card's memory - */ -/* - * Read bytes from the PSA. - */ -static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */ - u8 * b, /* buffer to fill */ - int n) -{ /* size to read */ - wv_16_off(ioaddr, hacr); - - while (n-- > 0) { - outw(o, PIOR2(ioaddr)); - o++; - *b++ = inb(PIOP2(ioaddr)); - } - - wv_16_on(ioaddr, hacr); -} /* psa_read */ - -/*------------------------------------------------------------------*/ -/* - * Write the Parameter Storage Area to the WaveLAN card's memory. - */ -static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */ - u8 * b, /* Buffer in memory */ - int n) -{ /* Length of buffer */ - int count = 0; - - wv_16_off(ioaddr, hacr); - - while (n-- > 0) { - outw(o, PIOR2(ioaddr)); - o++; - - outb(*b, PIOP2(ioaddr)); - b++; - - /* Wait for the memory to finish its write cycle */ - count = 0; - while ((count++ < 100) && - (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1); - } - - wv_16_on(ioaddr, hacr); -} /* psa_write */ - -#ifdef SET_PSA_CRC -/*------------------------------------------------------------------*/ -/* - * Calculate the PSA CRC - * Thanks to Valster, Nico for the code - * NOTE: By specifying a length including the CRC position the - * returned value should be zero. (i.e. a correct checksum in the PSA) - * - * The Windows drivers don't use the CRC, but the AP and the PtP tool - * depend on it. - */ -static u16 psa_crc(u8 * psa, /* The PSA */ - int size) -{ /* Number of short for CRC */ - int byte_cnt; /* Loop on the PSA */ - u16 crc_bytes = 0; /* Data in the PSA */ - int bit_cnt; /* Loop on the bits of the short */ - - for (byte_cnt = 0; byte_cnt < size; byte_cnt++) { - crc_bytes ^= psa[byte_cnt]; /* Its an xor */ - - for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) { - if (crc_bytes & 0x0001) - crc_bytes = (crc_bytes >> 1) ^ 0xA001; - else - crc_bytes >>= 1; - } - } - - return crc_bytes; -} /* psa_crc */ -#endif /* SET_PSA_CRC */ - -/*------------------------------------------------------------------*/ -/* - * update the checksum field in the Wavelan's PSA - */ -static void update_psa_checksum(struct net_device * dev, unsigned long ioaddr, u16 hacr) -{ -#ifdef SET_PSA_CRC - psa_t psa; - u16 crc; - - /* read the parameter storage area */ - psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa)); - - /* update the checksum */ - crc = psa_crc((unsigned char *) &psa, - sizeof(psa) - sizeof(psa.psa_crc[0]) - - sizeof(psa.psa_crc[1]) - - sizeof(psa.psa_crc_status)); - - psa.psa_crc[0] = crc & 0xFF; - psa.psa_crc[1] = (crc & 0xFF00) >> 8; - - /* Write it ! */ - psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa, - (unsigned char *) &psa.psa_crc, 2); - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", - dev->name, psa.psa_crc[0], psa.psa_crc[1]); - - /* Check again (luxury !) */ - crc = psa_crc((unsigned char *) &psa, - sizeof(psa) - sizeof(psa.psa_crc_status)); - - if (crc != 0) - printk(KERN_WARNING - "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", - dev->name); -#endif /* DEBUG_IOCTL_INFO */ -#endif /* SET_PSA_CRC */ -} /* update_psa_checksum */ - -/*------------------------------------------------------------------*/ -/* - * Write 1 byte to the MMC. - */ -static void mmc_out(unsigned long ioaddr, u16 o, u8 d) -{ - int count = 0; - - /* Wait for MMC to go idle */ - while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) - udelay(10); - - outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr)); -} - -/*------------------------------------------------------------------*/ -/* - * Routine to write bytes to the Modem Management Controller. - * We start at the end because it is the way it should be! - */ -static void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n) -{ - o += n; - b += n; - - while (n-- > 0) - mmc_out(ioaddr, --o, *(--b)); -} /* mmc_write */ - -/*------------------------------------------------------------------*/ -/* - * Read a byte from the MMC. - * Optimised version for 1 byte, avoid using memory. - */ -static u8 mmc_in(unsigned long ioaddr, u16 o) -{ - int count = 0; - - while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) - udelay(10); - outw(o << 1, MMCR(ioaddr)); - - while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) - udelay(10); - return (u8) (inw(MMCR(ioaddr)) >> 8); -} - -/*------------------------------------------------------------------*/ -/* - * Routine to read bytes from the Modem Management Controller. - * The implementation is complicated by a lack of address lines, - * which prevents decoding of the low-order bit. - * (code has just been moved in the above function) - * We start at the end because it is the way it should be! - */ -static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n) -{ - o += n; - b += n; - - while (n-- > 0) - *(--b) = mmc_in(ioaddr, --o); -} /* mmc_read */ - -/*------------------------------------------------------------------*/ -/* - * Get the type of encryption available. - */ -static inline int mmc_encr(unsigned long ioaddr) -{ /* I/O port of the card */ - int temp; - - temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail)); - if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) - return 0; - else - return temp; -} - -/*------------------------------------------------------------------*/ -/* - * Wait for the frequency EEPROM to complete a command. - * I hope this one will be optimally inlined. - */ -static inline void fee_wait(unsigned long ioaddr, /* I/O port of the card */ - int delay, /* Base delay to wait for */ - int number) -{ /* Number of time to wait */ - int count = 0; /* Wait only a limited time */ - - while ((count++ < number) && - (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - MMR_FEE_STATUS_BUSY)) udelay(delay); -} - -/*------------------------------------------------------------------*/ -/* - * Read bytes from the Frequency EEPROM (frequency select cards). - */ -static void fee_read(unsigned long ioaddr, /* I/O port of the card */ - u16 o, /* destination offset */ - u16 * b, /* data buffer */ - int n) -{ /* number of registers */ - b += n; /* Position at the end of the area */ - - /* Write the address */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while (n-- > 0) { - /* Write the read command */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ); - - /* Wait until EEPROM is ready (should be quick). */ - fee_wait(ioaddr, 10, 100); - - /* Read the value. */ - *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) | - mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); - } -} - - -/*------------------------------------------------------------------*/ -/* - * Write bytes from the Frequency EEPROM (frequency select cards). - * This is a bit complicated, because the frequency EEPROM has to - * be unprotected and the write enabled. - * Jean II - */ -static void fee_write(unsigned long ioaddr, /* I/O port of the card */ - u16 o, /* destination offset */ - u16 * b, /* data buffer */ - int n) -{ /* number of registers */ - b += n; /* Position at the end of the area. */ - -#ifdef EEPROM_IS_PROTECTED /* disabled */ -#ifdef DOESNT_SEEM_TO_WORK /* disabled */ - /* Ask to read the protected register */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); - - fee_wait(ioaddr, 10, 100); - - /* Read the protected register. */ - printk("Protected 2: %02X-%02X\n", - mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)), - mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); -#endif /* DOESNT_SEEM_TO_WORK */ - - /* Enable protected register. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); - - fee_wait(ioaddr, 10, 100); - - /* Unprotect area. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); -#ifdef DOESNT_SEEM_TO_WORK /* disabled */ - /* or use: */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); -#endif /* DOESNT_SEEM_TO_WORK */ - - fee_wait(ioaddr, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ - - /* Write enable. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); - - fee_wait(ioaddr, 10, 100); - - /* Write the EEPROM address. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while (n-- > 0) { - /* Write the value. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); - mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF); - - /* Write the write command. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_WRITE); - - /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */ - mdelay(10); - fee_wait(ioaddr, 10, 100); - } - - /* Write disable. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); - - fee_wait(ioaddr, 10, 100); - -#ifdef EEPROM_IS_PROTECTED /* disabled */ - /* Reprotect EEPROM. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); - - fee_wait(ioaddr, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ -} - -/************************ I82586 SUBROUTINES *************************/ -/* - * Useful subroutines to manage the Ethernet controller - */ - -/*------------------------------------------------------------------*/ -/* - * Read bytes from the on-board RAM. - * Why does inlining this function make it fail? - */ -static /*inline */ void obram_read(unsigned long ioaddr, - u16 o, u8 * b, int n) -{ - outw(o, PIOR1(ioaddr)); - insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); -} - -/*------------------------------------------------------------------*/ -/* - * Write bytes to the on-board RAM. - */ -static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n) -{ - outw(o, PIOR1(ioaddr)); - outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); -} - -/*------------------------------------------------------------------*/ -/* - * Acknowledge the reading of the status issued by the i82586. - */ -static void wv_ack(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - u16 scb_cs; - int i; - - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - scb_cs &= SCB_ST_INT; - - if (scb_cs == 0) - return; - - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - udelay(100); - -#ifdef DEBUG_CONFIG_ERROR - if (i <= 0) - printk(KERN_INFO - "%s: wv_ack(): board not accepting command.\n", - dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Set channel attention bit and busy wait until command has - * completed, then acknowledge completion of the command. - */ -static int wv_synchronous_cmd(struct net_device * dev, const char *str) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - u16 scb_cmd; - ach_t cb; - int i; - - scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cmd, sizeof(scb_cmd)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, - sizeof(cb)); - if (cb.ac_status & AC_SFLD_C) - break; - - udelay(10); - } - udelay(100); - - if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO "%s: %s failed; status = 0x%x\n", - dev->name, str, cb.ac_status); -#endif -#ifdef DEBUG_I82586_SHOW - wv_scb_show(ioaddr); -#endif - return -1; - } - - /* Ack the status */ - wv_ack(dev); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Configuration commands completion interrupt. - * Check if done, and if OK. - */ -static int -wv_config_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp) -{ - unsigned short mcs_addr; - unsigned short status; - int ret; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name); -#endif - - mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t) - + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t); - - /* Read the status of the last command (set mc list). */ - obram_read(ioaddr, acoff(mcs_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - - /* If not completed -> exit */ - if ((status & AC_SFLD_C) == 0) - ret = 0; /* Not ready to be scrapped */ - else { -#ifdef DEBUG_CONFIG_ERROR - unsigned short cfg_addr; - unsigned short ias_addr; - - /* Check mc_config command */ - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n", - dev->name, status); - - /* check ia-config command */ - ias_addr = mcs_addr - sizeof(ac_ias_t); - obram_read(ioaddr, acoff(ias_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n", - dev->name, status); - - /* Check config command. */ - cfg_addr = ias_addr - sizeof(ac_cfg_t); - obram_read(ioaddr, acoff(cfg_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): configure failed; status = 0x%x\n", - dev->name, status); -#endif /* DEBUG_CONFIG_ERROR */ - - ret = 1; /* Ready to be scrapped */ - } - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, - ret); -#endif - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Command completion interrupt. - * Reclaim as many freed tx buffers as we can. - * (called in wavelan_interrupt()). - * Note : the spinlock is already grabbed for us. - */ -static int wv_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp) -{ - int nreaped = 0; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name); -#endif - - /* Loop on all the transmit buffers */ - while (lp->tx_first_in_use != I82586NULL) { - unsigned short tx_status; - - /* Read the first transmit buffer */ - obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), - (unsigned char *) &tx_status, - sizeof(tx_status)); - - /* If not completed -> exit */ - if ((tx_status & AC_SFLD_C) == 0) - break; - - /* Hack for reconfiguration */ - if (tx_status == 0xFFFF) - if (!wv_config_complete(dev, ioaddr, lp)) - break; /* Not completed */ - - /* We now remove this buffer */ - nreaped++; - --lp->tx_n_in_use; - -/* -if (lp->tx_n_in_use > 0) - printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); -*/ - - /* Was it the last one? */ - if (lp->tx_n_in_use <= 0) - lp->tx_first_in_use = I82586NULL; - else { - /* Next one in the chain */ - lp->tx_first_in_use += TXBLOCKZ; - if (lp->tx_first_in_use >= - OFFSET_CU + - NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -= - NTXBLOCKS * TXBLOCKZ; - } - - /* Hack for reconfiguration */ - if (tx_status == 0xFFFF) - continue; - - /* Now, check status of the finished command */ - if (tx_status & AC_SFLD_OK) { - int ncollisions; - - dev->stats.tx_packets++; - ncollisions = tx_status & AC_SFLD_MAXCOL; - dev->stats.collisions += ncollisions; -#ifdef DEBUG_TX_INFO - if (ncollisions > 0) - printk(KERN_DEBUG - "%s: wv_complete(): tx completed after %d collisions.\n", - dev->name, ncollisions); -#endif - } else { - dev->stats.tx_errors++; - if (tx_status & AC_SFLD_S10) { - dev->stats.tx_carrier_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: no CS.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S9) { - dev->stats.tx_carrier_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: lost CTS.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S8) { - dev->stats.tx_fifo_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: slow DMA.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S6) { - dev->stats.tx_heartbeat_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: heart beat.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S5) { - dev->stats.tx_aborted_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: too many collisions.\n", - dev->name); -#endif - } - } - -#ifdef DEBUG_TX_INFO - printk(KERN_DEBUG - "%s: wv_complete(): tx completed, tx_status 0x%04x\n", - dev->name, tx_status); -#endif - } - -#ifdef DEBUG_INTERRUPT_INFO - if (nreaped > 1) - printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", - dev->name, nreaped); -#endif - - /* - * Inform upper layers. - */ - if (lp->tx_n_in_use < NTXBLOCKS - 1) { - netif_wake_queue(dev); - } -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name); -#endif - return nreaped; -} - -/*------------------------------------------------------------------*/ -/* - * Reconfigure the i82586, or at least ask for it. - * Because wv_82586_config uses a transmission buffer, we must do it - * when we are sure that there is one left, so we do it now - * or in wavelan_packet_xmit() (I can't find any better place, - * wavelan_interrupt is not an option), so you may experience - * delays sometimes. - */ -static void wv_82586_reconfig(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - - /* Arm the flag, will be cleard in wv_82586_config() */ - lp->reconfig_82586 = 1; - - /* Check if we can do it now ! */ - if((netif_running(dev)) && !(netif_queue_stopped(dev))) { - spin_lock_irqsave(&lp->spinlock, flags); - /* May fail */ - wv_82586_config(dev); - spin_unlock_irqrestore(&lp->spinlock, flags); - } - else { -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wv_82586_reconfig(): delayed (state = %lX)\n", - dev->name, dev->state); -#endif - } -} - -/********************* DEBUG & INFO SUBROUTINES *********************/ -/* - * This routine is used in the code to show information for debugging. - * Most of the time, it dumps the contents of hardware structures. - */ - -#ifdef DEBUG_PSA_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted contents of the Parameter Storage Area. - */ -static void wv_psa_show(psa_t * p) -{ - printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n"); - printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", - p->psa_io_base_addr_1, - p->psa_io_base_addr_2, - p->psa_io_base_addr_3, p->psa_io_base_addr_4); - printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", - p->psa_rem_boot_addr_1, - p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3); - printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); - printk("psa_int_req_no: %d\n", p->psa_int_req_no); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr); - printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr); - printk(KERN_DEBUG "psa_univ_local_sel: %d, ", - p->psa_univ_local_sel); - printk("psa_comp_number: %d, ", p->psa_comp_number); - printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); - printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", - p->psa_feature_select); - printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); - printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); - printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); - printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], - p->psa_nwid[1]); - printk("psa_nwid_select: %d\n", p->psa_nwid_select); - printk(KERN_DEBUG "psa_encryption_select: %d, ", - p->psa_encryption_select); - printk - ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - p->psa_encryption_key[0], p->psa_encryption_key[1], - p->psa_encryption_key[2], p->psa_encryption_key[3], - p->psa_encryption_key[4], p->psa_encryption_key[5], - p->psa_encryption_key[6], p->psa_encryption_key[7]); - printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); - printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", - p->psa_call_code[0]); - printk - ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2], - p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5], - p->psa_call_code[6], p->psa_call_code[7]); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n", - p->psa_reserved[0], - p->psa_reserved[1]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); - printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); - printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); -} /* wv_psa_show */ -#endif /* DEBUG_PSA_SHOW */ - -#ifdef DEBUG_MMC_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the Modem Management Controller. - * This function needs to be completed. - */ -static void wv_mmc_show(struct net_device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); - mmr_t m; - - /* Basic check */ - if (hasr_read(ioaddr) & HASR_NO_CLK) { - printk(KERN_WARNING - "%s: wv_mmc_show: modem not connected\n", - dev->name); - return; - } - - /* Read the mmc */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m)); - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); - - /* Don't forget to update statistics */ - lp->wstats.discard.nwid += - (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; - - printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n"); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG - "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2], - m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5], - m.mmr_unused0[6], m.mmr_unused0[7]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", - m.mmr_des_avail, m.mmr_des_status); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", - m.mmr_unused1[0], - m.mmr_unused1[1], - m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", - m.mmr_dce_status, - (m. - mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? - "energy detected," : "", - (m. - mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? - "loop test indicated," : "", - (m. - mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? - "transmitter on," : "", - (m. - mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? - "jabber timer expired," : ""); - printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", - m.mmr_unused2[0], m.mmr_unused2[1]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", - (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, - (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); - printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", - m.mmr_thr_pre_set & MMR_THR_PRE_SET, - (m. - mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : - "below"); - printk(KERN_DEBUG "signal_lvl: %d [%s], ", - m.mmr_signal_lvl & MMR_SIGNAL_LVL, - (m. - mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : - "no new msg"); - printk("silence_lvl: %d [%s], ", - m.mmr_silence_lvl & MMR_SILENCE_LVL, - (m. - mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : - "no new update"); - printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL, - (m. - mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : - "Antenna 0"); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); -#endif /* DEBUG_SHOW_UNUSED */ -} /* wv_mmc_show */ -#endif /* DEBUG_MMC_SHOW */ - -#ifdef DEBUG_I82586_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the last block of the i82586 memory. - */ -static void wv_scb_show(unsigned long ioaddr) -{ - scb_t scb; - - obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - printk(KERN_DEBUG "##### WaveLAN system control block: #####\n"); - - printk(KERN_DEBUG "status: "); - printk("stat 0x%x[%s%s%s%s] ", - (scb. - scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | - SCB_ST_RNR)) >> 12, - (scb. - scb_status & SCB_ST_CX) ? "command completion interrupt," : - "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "", - (scb. - scb_status & SCB_ST_CNA) ? "command unit not active," : "", - (scb. - scb_status & SCB_ST_RNR) ? "receiving unit not ready," : - ""); - printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8, - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_IDLE) ? "idle" : "", - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_SUSP) ? "suspended" : "", - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_ACTV) ? "active" : ""); - printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4, - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_IDLE) ? "idle" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_SUSP) ? "suspended" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_NRES) ? "no resources" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_RDY) ? "ready" : ""); - - printk(KERN_DEBUG "command: "); - printk("ack 0x%x[%s%s%s%s] ", - (scb. - scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | - SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12, - (scb. - scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "", - (scb. - scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "", - (scb. - scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "", - (scb. - scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""); - printk("cuc 0x%x[%s%s%s%s%s] ", - (scb.scb_command & SCB_CMD_CUC) >> 8, - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_NOP) ? "nop" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_GO) ? "start cbl_offset" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_RES) ? "resume execution" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_SUS) ? "suspend execution" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_ABT) ? "abort execution" : ""); - printk("ruc 0x%x[%s%s%s%s%s]\n", - (scb.scb_command & SCB_CMD_RUC) >> 4, - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_NOP) ? "nop" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_GO) ? "start rfa_offset" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_RES) ? "resume reception" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_SUS) ? "suspend reception" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_ABT) ? "abort reception" : ""); - - printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset); - printk("rfa_offset 0x%x\n", scb.scb_rfa_offset); - - printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs); - printk("alnerrs %d ", scb.scb_alnerrs); - printk("rscerrs %d ", scb.scb_rscerrs); - printk("ovrnerrs %d\n", scb.scb_ovrnerrs); -} - -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the i82586's receive unit. - */ -static void wv_ru_show(struct net_device * dev) -{ - printk(KERN_DEBUG - "##### WaveLAN i82586 receiver unit status: #####\n"); - printk(KERN_DEBUG "ru:"); - /* - * Not implemented yet - */ - printk("\n"); -} /* wv_ru_show */ - -/*------------------------------------------------------------------*/ -/* - * Display info about one control block of the i82586 memory. - */ -static void wv_cu_show_one(struct net_device * dev, net_local * lp, int i, u16 p) -{ - unsigned long ioaddr; - ac_tx_t actx; - - ioaddr = dev->base_addr; - - printk("%d: 0x%x:", i, p); - - obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx)); - printk(" status=0x%x,", actx.tx_h.ac_status); - printk(" command=0x%x,", actx.tx_h.ac_command); - - /* - { - tbd_t tbd; - - obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd)); - printk(" tbd_status=0x%x,", tbd.tbd_status); - } - */ - - printk("|"); -} - -/*------------------------------------------------------------------*/ -/* - * Print status of the command unit of the i82586. - */ -static void wv_cu_show(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned int i; - u16 p; - - printk(KERN_DEBUG - "##### WaveLAN i82586 command unit status: #####\n"); - - printk(KERN_DEBUG); - for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) { - wv_cu_show_one(dev, lp, i, p); - - p += TXBLOCKZ; - if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - p -= NTXBLOCKS * TXBLOCKZ; - } - printk("\n"); -} -#endif /* DEBUG_I82586_SHOW */ - -#ifdef DEBUG_DEVICE_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the WaveLAN PCMCIA device driver. - */ -static void wv_dev_show(struct net_device * dev) -{ - printk(KERN_DEBUG "dev:"); - printk(" state=%lX,", dev->state); - printk(" trans_start=%ld,", dev->trans_start); - printk(" flags=0x%x,", dev->flags); - printk("\n"); -} /* wv_dev_show */ - -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the WaveLAN PCMCIA device driver's - * private information. - */ -static void wv_local_show(struct net_device * dev) -{ - net_local *lp; - - lp = netdev_priv(dev); - - printk(KERN_DEBUG "local:"); - printk(" tx_n_in_use=%d,", lp->tx_n_in_use); - printk(" hacr=0x%x,", lp->hacr); - printk(" rx_head=0x%x,", lp->rx_head); - printk(" rx_last=0x%x,", lp->rx_last); - printk(" tx_first_free=0x%x,", lp->tx_first_free); - printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use); - printk("\n"); -} /* wv_local_show */ -#endif /* DEBUG_DEVICE_SHOW */ - -#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) -/*------------------------------------------------------------------*/ -/* - * Dump packet header (and content if necessary) on the screen - */ -static inline void wv_packet_info(u8 * p, /* Packet to dump */ - int length, /* Length of the packet */ - char *msg1, /* Name of the device */ - char *msg2) -{ /* Name of the function */ - int i; - int maxi; - - printk(KERN_DEBUG - "%s: %s(): dest %pM, length %d\n", - msg1, msg2, p, length); - printk(KERN_DEBUG - "%s: %s(): src %pM, type 0x%02X%02X\n", - msg1, msg2, &p[6], p[12], p[13]); - -#ifdef DEBUG_PACKET_DUMP - - printk(KERN_DEBUG "data=\""); - - if ((maxi = length) > DEBUG_PACKET_DUMP) - maxi = DEBUG_PACKET_DUMP; - for (i = 14; i < maxi; i++) - if (p[i] >= ' ' && p[i] <= '~') - printk(" %c", p[i]); - else - printk("%02X", p[i]); - if (maxi < length) - printk(".."); - printk("\"\n"); - printk(KERN_DEBUG "\n"); -#endif /* DEBUG_PACKET_DUMP */ -} -#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ - -/*------------------------------------------------------------------*/ -/* - * This is the information which is displayed by the driver at startup. - * There are lots of flags for configuring it to your liking. - */ -static void wv_init_info(struct net_device * dev) -{ - short ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - - /* Read the parameter storage area */ - psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); - -#ifdef DEBUG_PSA_SHOW - wv_psa_show(&psa); -#endif -#ifdef DEBUG_MMC_SHOW - wv_mmc_show(dev); -#endif -#ifdef DEBUG_I82586_SHOW - wv_cu_show(dev); -#endif - -#ifdef DEBUG_BASIC_SHOW - /* Now, let's go for the basic stuff. */ - printk(KERN_NOTICE "%s: WaveLAN at %#x, %pM, IRQ %d", - dev->name, ioaddr, dev->dev_addr, dev->irq); - - /* Print current network ID. */ - if (psa.psa_nwid_select) - printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], - psa.psa_nwid[1]); - else - printk(", nwid off"); - - /* If 2.00 card */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - unsigned short freq; - - /* Ask the EEPROM to read the frequency from the first area. */ - fee_read(ioaddr, 0x00, &freq, 1); - - /* Print frequency */ - printk(", 2.00, %ld", (freq >> 6) + 2400L); - - /* Hack! */ - if (freq & 0x20) - printk(".5"); - } else { - printk(", PC"); - switch (psa.psa_comp_number) { - case PSA_COMP_PC_AT_915: - case PSA_COMP_PC_AT_2400: - printk("-AT"); - break; - case PSA_COMP_PC_MC_915: - case PSA_COMP_PC_MC_2400: - printk("-MC"); - break; - case PSA_COMP_PCMCIA_915: - printk("MCIA"); - break; - default: - printk("?"); - } - printk(", "); - switch (psa.psa_subband) { - case PSA_SUBBAND_915: - printk("915"); - break; - case PSA_SUBBAND_2425: - printk("2425"); - break; - case PSA_SUBBAND_2460: - printk("2460"); - break; - case PSA_SUBBAND_2484: - printk("2484"); - break; - case PSA_SUBBAND_2430_5: - printk("2430.5"); - break; - default: - printk("?"); - } - } - - printk(" MHz\n"); -#endif /* DEBUG_BASIC_SHOW */ - -#ifdef DEBUG_VERSION_SHOW - /* Print version information */ - printk(KERN_NOTICE "%s", version); -#endif -} /* wv_init_info */ - -/********************* IOCTL, STATS & RECONFIG *********************/ -/* - * We found here routines that are called by Linux on different - * occasions after the configuration and not for transmitting data - * These may be called when the user use ifconfig, /proc/net/dev - * or wireless extensions - */ - - -/*------------------------------------------------------------------*/ -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets - * num_addrs == 0 Normal mode, clear multicast list - * num_addrs > 0 Multicast mode, receive normal and MC packets, - * and do best-effort filtering. - */ -static void wavelan_set_multicast_list(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", - dev->name); -#endif - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG - "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", - dev->name, dev->flags, dev->mc_count); -#endif - - /* Are we asking for promiscuous mode, - * or all multicast addresses (we don't have that!) - * or too many multicast addresses for the hardware filter? */ - if ((dev->flags & IFF_PROMISC) || - (dev->flags & IFF_ALLMULTI) || - (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) { - /* - * Enable promiscuous mode: receive all packets. - */ - if (!lp->promiscuous) { - lp->promiscuous = 1; - lp->mc_count = 0; - - wv_82586_reconfig(dev); - } - } else - /* Are there multicast addresses to send? */ - if (dev->mc_list != (struct dev_mc_list *) NULL) { - /* - * Disable promiscuous mode, but receive all packets - * in multicast list - */ -#ifdef MULTICAST_AVOID - if (lp->promiscuous || (dev->mc_count != lp->mc_count)) -#endif - { - lp->promiscuous = 0; - lp->mc_count = dev->mc_count; - - wv_82586_reconfig(dev); - } - } else { - /* - * Switch to normal mode: disable promiscuous mode and - * clear the multicast list. - */ - if (lp->promiscuous || lp->mc_count == 0) { - lp->promiscuous = 0; - lp->mc_count = 0; - - wv_82586_reconfig(dev); - } - } -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", - dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * This function doesn't exist. - * (Note : it was a nice way to test the reconfigure stuff...) - */ -#ifdef SET_MAC_ADDRESS -static int wavelan_set_mac_address(struct net_device * dev, void *addr) -{ - struct sockaddr *mac = addr; - - /* Copy the address. */ - memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); - - /* Reconfigure the beast. */ - wv_82586_reconfig(dev); - - return 0; -} -#endif /* SET_MAC_ADDRESS */ - - -/*------------------------------------------------------------------*/ -/* - * Frequency setting (for hardware capable of it) - * It's a bit complicated and you don't really want to look into it. - * (called in wavelan_ioctl) - */ -static int wv_set_frequency(unsigned long ioaddr, /* I/O port of the card */ - iw_freq * frequency) -{ - const int BAND_NUM = 10; /* Number of bands */ - long freq = 0L; /* offset to 2.4 GHz in .5 MHz */ -#ifdef DEBUG_IOCTL_INFO - int i; -#endif - - /* Setting by frequency */ - /* Theoretically, you may set any frequency between - * the two limits with a 0.5 MHz precision. In practice, - * I don't want you to have trouble with local regulations. - */ - if ((frequency->e == 1) && - (frequency->m >= (int) 2.412e8) - && (frequency->m <= (int) 2.487e8)) { - freq = ((frequency->m / 10000) - 24000L) / 5; - } - - /* Setting by channel (same as wfreqsel) */ - /* Warning: each channel is 22 MHz wide, so some of the channels - * will interfere. */ - if ((frequency->e == 0) && (frequency->m < BAND_NUM)) { - /* Get frequency offset. */ - freq = channel_bands[frequency->m] >> 1; - } - - /* Verify that the frequency is allowed. */ - if (freq != 0L) { - u16 table[10]; /* Authorized frequency table */ - - /* Read the frequency table. */ - fee_read(ioaddr, 0x71, table, 10); - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "Frequency table: "); - for (i = 0; i < 10; i++) { - printk(" %04X", table[i]); - } - printk("\n"); -#endif - - /* Look in the table to see whether the frequency is allowed. */ - if (!(table[9 - ((freq - 24) / 16)] & - (1 << ((freq - 24) % 16)))) return -EINVAL; /* not allowed */ - } else - return -EINVAL; - - /* if we get a usable frequency */ - if (freq != 0L) { - unsigned short area[16]; - unsigned short dac[2]; - unsigned short area_verify[16]; - unsigned short dac_verify[2]; - /* Corresponding gain (in the power adjust value table) - * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8 - * and WCIN062D.DOC, page 6.2.9. */ - unsigned short power_limit[] = { 40, 80, 120, 160, 0 }; - int power_band = 0; /* Selected band */ - unsigned short power_adjust; /* Correct value */ - - /* Search for the gain. */ - power_band = 0; - while ((freq > power_limit[power_band]) && - (power_limit[++power_band] != 0)); - - /* Read the first area. */ - fee_read(ioaddr, 0x00, area, 16); - - /* Read the DAC. */ - fee_read(ioaddr, 0x60, dac, 2); - - /* Read the new power adjust value. */ - fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust, - 1); - if (power_band & 0x1) - power_adjust >>= 8; - else - power_adjust &= 0xFF; - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "WaveLAN EEPROM Area 1: "); - for (i = 0; i < 16; i++) { - printk(" %04X", area[i]); - } - printk("\n"); - - printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", - dac[0], dac[1]); -#endif - - /* Frequency offset (for info only) */ - area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); - - /* Receiver Principle main divider coefficient */ - area[3] = (freq >> 1) + 2400L - 352L; - area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); - - /* Transmitter Main divider coefficient */ - area[13] = (freq >> 1) + 2400L; - area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); - - /* Other parts of the area are flags, bit streams or unused. */ - - /* Set the value in the DAC. */ - dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); - dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); - - /* Write the first area. */ - fee_write(ioaddr, 0x00, area, 16); - - /* Write the DAC. */ - fee_write(ioaddr, 0x60, dac, 2); - - /* We now should verify here that the writing of the EEPROM went OK. */ - - /* Reread the first area. */ - fee_read(ioaddr, 0x00, area_verify, 16); - - /* Reread the DAC. */ - fee_read(ioaddr, 0x60, dac_verify, 2); - - /* Compare. */ - if (memcmp(area, area_verify, 16 * 2) || - memcmp(dac, dac_verify, 2 * 2)) { -#ifdef DEBUG_IOCTL_ERROR - printk(KERN_INFO - "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n"); -#endif - return -EOPNOTSUPP; - } - - /* We must download the frequency parameters to the - * synthesizers (from the EEPROM - area 1) - * Note: as the EEPROM is automatically decremented, we set the end - * if the area... */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait until the download is finished. */ - fee_wait(ioaddr, 100, 100); - - /* We must now download the power adjust value (gain) to - * the synthesizers (from the EEPROM - area 7 - DAC). */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait for the download to finish. */ - fee_wait(ioaddr, 100, 100); - -#ifdef DEBUG_IOCTL_INFO - /* Verification of what we have done */ - - printk(KERN_DEBUG "WaveLAN EEPROM Area 1: "); - for (i = 0; i < 16; i++) { - printk(" %04X", area_verify[i]); - } - printk("\n"); - - printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", - dac_verify[0], dac_verify[1]); -#endif - - return 0; - } else - return -EINVAL; /* Bah, never get there... */ -} - -/*------------------------------------------------------------------*/ -/* - * Give the list of available frequencies. - */ -static int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */ - iw_freq * list, /* List of frequencies to fill */ - int max) -{ /* Maximum number of frequencies */ - u16 table[10]; /* Authorized frequency table */ - long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ - int i; /* index in the table */ - int c = 0; /* Channel number */ - - /* Read the frequency table. */ - fee_read(ioaddr, 0x71 /* frequency table */ , table, 10); - - /* Check all frequencies. */ - i = 0; - for (freq = 0; freq < 150; freq++) - /* Look in the table if the frequency is allowed */ - if (table[9 - (freq / 16)] & (1 << (freq % 16))) { - /* Compute approximate channel number */ - while ((c < ARRAY_SIZE(channel_bands)) && - (((channel_bands[c] >> 1) - 24) < freq)) - c++; - list[i].i = c; /* Set the list index */ - - /* put in the list */ - list[i].m = (((freq + 24) * 5) + 24000L) * 10000; - list[i++].e = 1; - - /* Check number. */ - if (i >= max) - return (i); - } - - return (i); -} - -#ifdef IW_WIRELESS_SPY -/*------------------------------------------------------------------*/ -/* - * Gather wireless spy statistics: for each packet, compare the source - * address with our list, and if they match, get the statistics. - * Sorry, but this function really needs the wireless extensions. - */ -static inline void wl_spy_gather(struct net_device * dev, - u8 * mac, /* MAC address */ - u8 * stats) /* Statistics to gather */ -{ - struct iw_quality wstats; - - wstats.qual = stats[2] & MMR_SGNL_QUAL; - wstats.level = stats[0] & MMR_SIGNAL_LVL; - wstats.noise = stats[1] & MMR_SILENCE_LVL; - wstats.updated = 0x7; - - /* Update spy records */ - wireless_spy_update(dev, mac, &wstats); -} -#endif /* IW_WIRELESS_SPY */ - -#ifdef HISTOGRAM -/*------------------------------------------------------------------*/ -/* - * This function calculates a histogram of the signal level. - * As the noise is quite constant, it's like doing it on the SNR. - * We have defined a set of interval (lp->his_range), and each time - * the level goes in that interval, we increment the count (lp->his_sum). - * With this histogram you may detect if one WaveLAN is really weak, - * or you may also calculate the mean and standard deviation of the level. - */ -static inline void wl_his_gather(struct net_device * dev, u8 * stats) -{ /* Statistics to gather */ - net_local *lp = netdev_priv(dev); - u8 level = stats[0] & MMR_SIGNAL_LVL; - int i; - - /* Find the correct interval. */ - i = 0; - while ((i < (lp->his_number - 1)) - && (level >= lp->his_range[i++])); - - /* Increment interval counter. */ - (lp->his_sum[i])++; -} -#endif /* HISTOGRAM */ - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get protocol name - */ -static int wavelan_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - strcpy(wrqu->name, "WaveLAN"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set NWID - */ -static int wavelan_set_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - mm_t m; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Set NWID in WaveLAN. */ - if (!wrqu->nwid.disabled) { - /* Set NWID in psa */ - psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; - psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; - psa.psa_nwid_select = 0x01; - psa_write(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - - /* Set NWID in mmc. */ - m.w.mmw_netw_id_l = psa.psa_nwid[1]; - m.w.mmw_netw_id_h = psa.psa_nwid[0]; - mmc_write(ioaddr, - (char *) &m.w.mmw_netw_id_l - - (char *) &m, - (unsigned char *) &m.w.mmw_netw_id_l, 2); - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); - } else { - /* Disable NWID in the psa. */ - psa.psa_nwid_select = 0x00; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_nwid_select - - (char *) &psa, - (unsigned char *) &psa.psa_nwid_select, - 1); - - /* Disable NWID in the mmc (no filtering). */ - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), - MMW_LOOPT_SEL_DIS_NWID); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get NWID - */ -static int wavelan_get_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Read the NWID. */ - psa_read(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrqu->nwid.disabled = !(psa.psa_nwid_select); - wrqu->nwid.fixed = 1; /* Superfluous */ - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set frequency - */ -static int wavelan_set_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - unsigned long flags; - int ret; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - ret = wv_set_frequency(ioaddr, &(wrqu->freq)); - else - ret = -EOPNOTSUPP; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get frequency - */ -static int wavelan_get_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). - * Does it work for everybody, especially old cards? */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - unsigned short freq; - - /* Ask the EEPROM to read the frequency from the first area. */ - fee_read(ioaddr, 0x00, &freq, 1); - wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; - wrqu->freq.e = 1; - } else { - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_subband - (char *) &psa, - (unsigned char *) &psa.psa_subband, 1); - - if (psa.psa_subband <= 4) { - wrqu->freq.m = fixed_bands[psa.psa_subband]; - wrqu->freq.e = (psa.psa_subband != 0); - } else - ret = -EOPNOTSUPP; - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set level threshold - */ -static int wavelan_set_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Set the level threshold. */ - /* We should complain loudly if wrqu->sens.fixed = 0, because we - * can't set auto mode... */ - psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), - psa.psa_thr_pre_set); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get level threshold - */ -static int wavelan_get_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Read the level threshold. */ - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; - wrqu->sens.fixed = 1; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set encryption key - */ -static int wavelan_set_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - unsigned long flags; - psa_t psa; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check if capable of encryption */ - if (!mmc_encr(ioaddr)) { - ret = -EOPNOTSUPP; - } - - /* Check the size of the key */ - if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { - ret = -EINVAL; - } - - if(!ret) { - /* Basic checking... */ - if (wrqu->encoding.length == 8) { - /* Copy the key in the driver */ - memcpy(psa.psa_encryption_key, extra, - wrqu->encoding.length); - psa.psa_encryption_select = 1; - - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 8 + 1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); - mmc_write(ioaddr, mmwoff(0, mmw_encr_key), - (unsigned char *) &psa. - psa_encryption_key, 8); - } - - /* disable encryption */ - if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { - psa.psa_encryption_select = 0; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get encryption key - */ -static int wavelan_get_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check if encryption is available */ - if (!mmc_encr(ioaddr)) { - ret = -EOPNOTSUPP; - } else { - /* Read the encryption key */ - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1 + 8); - - /* encryption is enabled ? */ - if (psa.psa_encryption_select) - wrqu->encoding.flags = IW_ENCODE_ENABLED; - else - wrqu->encoding.flags = IW_ENCODE_DISABLED; - wrqu->encoding.flags |= mmc_encr(ioaddr); - - /* Copy the key to the user buffer */ - wrqu->encoding.length = 8; - memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get range info - */ -static int wavelan_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - struct iw_range *range = (struct iw_range *) extra; - unsigned long flags; - int ret = 0; - - /* Set the length (very important for backward compatibility) */ - wrqu->data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(range, 0, sizeof(struct iw_range)); - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 9; - - /* Set information in the range struct. */ - range->throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */ - range->min_nwid = 0x0000; - range->max_nwid = 0xFFFF; - - range->sensitivity = 0x3F; - range->max_qual.qual = MMR_SGNL_QUAL; - range->max_qual.level = MMR_SIGNAL_LVL; - range->max_qual.noise = MMR_SILENCE_LVL; - range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range->avg_qual.level = 30; - range->avg_qual.noise = 8; - - range->num_bitrates = 1; - range->bitrate[0] = 2000000; /* 2 Mb/s */ - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | - IW_EVENT_CAPA_MASK(0x8B04)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - range->num_channels = 10; - range->num_frequency = wv_frequency_list(ioaddr, range->freq, - IW_MAX_FREQUENCIES); - } else - range->num_channels = range->num_frequency = 0; - - /* Encryption supported ? */ - if (mmc_encr(ioaddr)) { - range->encoding_size[0] = 8; /* DES = 64 bits key */ - range->num_encoding_sizes = 1; - range->max_encoding_tokens = 1; /* Only one key possible */ - } else { - range->num_encoding_sizes = 0; - range->max_encoding_tokens = 0; - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set quality threshold - */ -static int wavelan_set_qthr(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - psa.psa_quality_thr = *(extra) & 0x0F; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), - psa.psa_quality_thr); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get quality threshold - */ -static int wavelan_get_qthr(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); /* lp is not unused */ - psa_t psa; - unsigned long flags; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - *(extra) = psa.psa_quality_thr & 0x0F; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return 0; -} - -#ifdef HISTOGRAM -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set histogram - */ -static int wavelan_set_histo(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); /* lp is not unused */ - - /* Check the number of intervals. */ - if (wrqu->data.length > 16) { - return(-E2BIG); - } - - /* Disable histo while we copy the addresses. - * As we don't disable interrupts, we need to do this */ - lp->his_number = 0; - - /* Are there ranges to copy? */ - if (wrqu->data.length > 0) { - /* Copy interval ranges to the driver */ - memcpy(lp->his_range, extra, wrqu->data.length); - - { - int i; - printk(KERN_DEBUG "Histo :"); - for(i = 0; i < wrqu->data.length; i++) - printk(" %d", lp->his_range[i]); - printk("\n"); - } - - /* Reset result structure. */ - memset(lp->his_sum, 0x00, sizeof(long) * 16); - } - - /* Now we can set the number of ranges */ - lp->his_number = wrqu->data.length; - - return(0); -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get histogram - */ -static int wavelan_get_histo(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); /* lp is not unused */ - - /* Set the number of intervals. */ - wrqu->data.length = lp->his_number; - - /* Give back the distribution statistics */ - if(lp->his_number > 0) - memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); - - return(0); -} -#endif /* HISTOGRAM */ - -/*------------------------------------------------------------------*/ -/* - * Structures to export the Wireless Handlers - */ - -static const iw_handler wavelan_handler[] = -{ - NULL, /* SIOCSIWNAME */ - wavelan_get_name, /* SIOCGIWNAME */ - wavelan_set_nwid, /* SIOCSIWNWID */ - wavelan_get_nwid, /* SIOCGIWNWID */ - wavelan_set_freq, /* SIOCSIWFREQ */ - wavelan_get_freq, /* SIOCGIWFREQ */ - NULL, /* SIOCSIWMODE */ - NULL, /* SIOCGIWMODE */ - wavelan_set_sens, /* SIOCSIWSENS */ - wavelan_get_sens, /* SIOCGIWSENS */ - NULL, /* SIOCSIWRANGE */ - wavelan_get_range, /* SIOCGIWRANGE */ - NULL, /* SIOCSIWPRIV */ - NULL, /* SIOCGIWPRIV */ - NULL, /* SIOCSIWSTATS */ - NULL, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - NULL, /* SIOCSIWAP */ - NULL, /* SIOCGIWAP */ - NULL, /* -- hole -- */ - NULL, /* SIOCGIWAPLIST */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWESSID */ - NULL, /* SIOCGIWESSID */ - NULL, /* SIOCSIWNICKN */ - NULL, /* SIOCGIWNICKN */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWRATE */ - NULL, /* SIOCGIWRATE */ - NULL, /* SIOCSIWRTS */ - NULL, /* SIOCGIWRTS */ - NULL, /* SIOCSIWFRAG */ - NULL, /* SIOCGIWFRAG */ - NULL, /* SIOCSIWTXPOW */ - NULL, /* SIOCGIWTXPOW */ - NULL, /* SIOCSIWRETRY */ - NULL, /* SIOCGIWRETRY */ - /* Bummer ! Why those are only at the end ??? */ - wavelan_set_encode, /* SIOCSIWENCODE */ - wavelan_get_encode, /* SIOCGIWENCODE */ -}; - -static const iw_handler wavelan_private_handler[] = -{ - wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ - wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ -#ifdef HISTOGRAM - wavelan_set_histo, /* SIOCIWFIRSTPRIV + 2 */ - wavelan_get_histo, /* SIOCIWFIRSTPRIV + 3 */ -#endif /* HISTOGRAM */ -}; - -static const struct iw_priv_args wavelan_private_args[] = { -/*{ cmd, set_args, get_args, name } */ - { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, - { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, - { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, - { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, -}; - -static const struct iw_handler_def wavelan_handler_def = -{ - .num_standard = ARRAY_SIZE(wavelan_handler), - .num_private = ARRAY_SIZE(wavelan_private_handler), - .num_private_args = ARRAY_SIZE(wavelan_private_args), - .standard = wavelan_handler, - .private = wavelan_private_handler, - .private_args = wavelan_private_args, - .get_wireless_stats = wavelan_get_wireless_stats, -}; - -/*------------------------------------------------------------------*/ -/* - * Get wireless statistics. - * Called by /proc/net/wireless - */ -static iw_stats *wavelan_get_wireless_stats(struct net_device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); - mmr_t m; - iw_stats *wstats; - unsigned long flags; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", - dev->name); -#endif - - /* Check */ - if (lp == (net_local *) NULL) - return (iw_stats *) NULL; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - wstats = &lp->wstats; - - /* Get data from the mmc. */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - - mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); - mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, - 2); - mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, - 4); - - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); - - /* Copy data to wireless stuff. */ - wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; - wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; - wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; - wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; - wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) - | ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) - | ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); - wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; - wstats->discard.code = 0L; - wstats->discard.misc = 0L; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", - dev->name); -#endif - return &lp->wstats; -} - -/************************* PACKET RECEPTION *************************/ -/* - * This part deals with receiving the packets. - * The interrupt handler gets an interrupt when a packet has been - * successfully received and calls this part. - */ - -/*------------------------------------------------------------------*/ -/* - * This routine does the actual copying of data (including the Ethernet - * header structure) from the WaveLAN card to an sk_buff chain that - * will be passed up to the network interface layer. NOTE: we - * currently don't handle trailer protocols (neither does the rest of - * the network interface), so if that is needed, it will (at least in - * part) be added here. The contents of the receive ring buffer are - * copied to a message chain that is then passed to the kernel. - * - * Note: if any errors occur, the packet is "dropped on the floor". - * (called by wv_packet_rcv()) - */ -static void -wv_packet_read(struct net_device * dev, u16 buf_off, int sksize) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - struct sk_buff *skb; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", - dev->name, buf_off, sksize); -#endif - - /* Allocate buffer for the data */ - if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO - "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n", - dev->name, sksize); -#endif - dev->stats.rx_dropped++; - return; - } - - /* Copy the packet to the buffer. */ - obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); - skb->protocol = eth_type_trans(skb, dev); - -#ifdef DEBUG_RX_INFO - wv_packet_info(skb_mac_header(skb), sksize, dev->name, - "wv_packet_read"); -#endif /* DEBUG_RX_INFO */ - - /* Statistics-gathering and associated stuff. - * It seem a bit messy with all the define, but it's really - * simple... */ - if ( -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ - (lp->spy_data.spy_number > 0) || -#endif /* IW_WIRELESS_SPY */ -#ifdef HISTOGRAM - (lp->his_number > 0) || -#endif /* HISTOGRAM */ - 0) { - u8 stats[3]; /* signal level, noise level, signal quality */ - - /* Read signal level, silence level and signal quality bytes */ - /* Note: in the PCMCIA hardware, these are part of the frame. - * It seems that for the ISA hardware, it's nowhere to be - * found in the frame, so I'm obliged to do this (it has a - * side effect on /proc/net/wireless). - * Any ideas? - */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3); - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); - -#ifdef DEBUG_RX_INFO - printk(KERN_DEBUG - "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", - dev->name, stats[0] & 0x3F, stats[1] & 0x3F, - stats[2] & 0x0F); -#endif - - /* Spying stuff */ -#ifdef IW_WIRELESS_SPY - wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, - stats); -#endif /* IW_WIRELESS_SPY */ -#ifdef HISTOGRAM - wl_his_gather(dev, stats); -#endif /* HISTOGRAM */ - } - - /* - * Hand the packet to the network module. - */ - netif_rx(skb); - - /* Keep statistics up to date */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += sksize; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Transfer as many packets as we can - * from the device RAM. - * (called in wavelan_interrupt()). - * Note : the spinlock is already grabbed for us. - */ -static void wv_receive(struct net_device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); - fd_t fd; - rbd_t rbd; - int nreaped = 0; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name); -#endif - - /* Loop on each received packet. */ - for (;;) { - obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, - sizeof(fd)); - - /* Note about the status : - * It start up to be 0 (the value we set). Then, when the RU - * grab the buffer to prepare for reception, it sets the - * FD_STATUS_B flag. When the RU has finished receiving the - * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate - * completion and set the other flags to indicate the eventual - * errors. FD_STATUS_OK indicates that the reception was OK. - */ - - /* If the current frame is not complete, we have reached the end. */ - if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) - break; /* This is how we exit the loop. */ - - nreaped++; - - /* Check whether frame was correctly received. */ - if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) { - /* Does the frame contain a pointer to the data? Let's check. */ - if (fd.fd_rbd_offset != I82586NULL) { - /* Read the receive buffer descriptor */ - obram_read(ioaddr, fd.fd_rbd_offset, - (unsigned char *) &rbd, - sizeof(rbd)); - -#ifdef DEBUG_RX_ERROR - if ((rbd.rbd_status & RBD_STATUS_EOF) != - RBD_STATUS_EOF) printk(KERN_INFO - "%s: wv_receive(): missing EOF flag.\n", - dev->name); - - if ((rbd.rbd_status & RBD_STATUS_F) != - RBD_STATUS_F) printk(KERN_INFO - "%s: wv_receive(): missing F flag.\n", - dev->name); -#endif /* DEBUG_RX_ERROR */ - - /* Read the packet and transmit to Linux */ - wv_packet_read(dev, rbd.rbd_bufl, - rbd. - rbd_status & - RBD_STATUS_ACNT); - } -#ifdef DEBUG_RX_ERROR - else /* if frame has no data */ - printk(KERN_INFO - "%s: wv_receive(): frame has no data.\n", - dev->name); -#endif - } else { /* If reception was no successful */ - - dev->stats.rx_errors++; - -#ifdef DEBUG_RX_INFO - printk(KERN_DEBUG - "%s: wv_receive(): frame not received successfully (%X).\n", - dev->name, fd.fd_status); -#endif - -#ifdef DEBUG_RX_ERROR - if ((fd.fd_status & FD_STATUS_S6) != 0) - printk(KERN_INFO - "%s: wv_receive(): no EOF flag.\n", - dev->name); -#endif - - if ((fd.fd_status & FD_STATUS_S7) != 0) { - dev->stats.rx_length_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): frame too short.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S8) != 0) { - dev->stats.rx_over_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): rx DMA overrun.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S9) != 0) { - dev->stats.rx_fifo_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): ran out of resources.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S10) != 0) { - dev->stats.rx_frame_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): alignment error.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S11) != 0) { - dev->stats.rx_crc_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): CRC error.\n", - dev->name); -#endif - } - } - - fd.fd_status = 0; - obram_write(ioaddr, fdoff(lp->rx_head, fd_status), - (unsigned char *) &fd.fd_status, - sizeof(fd.fd_status)); - - fd.fd_command = FD_COMMAND_EL; - obram_write(ioaddr, fdoff(lp->rx_head, fd_command), - (unsigned char *) &fd.fd_command, - sizeof(fd.fd_command)); - - fd.fd_command = 0; - obram_write(ioaddr, fdoff(lp->rx_last, fd_command), - (unsigned char *) &fd.fd_command, - sizeof(fd.fd_command)); - - lp->rx_last = lp->rx_head; - lp->rx_head = fd.fd_link_offset; - } /* for(;;) -> loop on all frames */ - -#ifdef DEBUG_RX_INFO - if (nreaped > 1) - printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", - dev->name, nreaped); -#endif -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name); -#endif -} - -/*********************** PACKET TRANSMISSION ***********************/ -/* - * This part deals with sending packets through the WaveLAN. - * - */ - -/*------------------------------------------------------------------*/ -/* - * This routine fills in the appropriate registers and memory - * locations on the WaveLAN card and starts the card off on - * the transmit. - * - * The principle: - * Each block contains a transmit command, a NOP command, - * a transmit block descriptor and a buffer. - * The CU read the transmit block which point to the tbd, - * read the tbd and the content of the buffer. - * When it has finish with it, it goes to the next command - * which in our case is the NOP. The NOP points on itself, - * so the CU stop here. - * When we add the next block, we modify the previous nop - * to make it point on the new tx command. - * Simple, isn't it ? - * - * (called in wavelan_packet_xmit()) - */ -static int wv_packet_write(struct net_device * dev, void *buf, short length) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - unsigned short txblock; - unsigned short txpred; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short buf_addr; - ac_tx_t tx; - ac_nop_t nop; - tbd_t tbd; - int clen = length; - unsigned long flags; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, - length); -#endif - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check nothing bad has happened */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { -#ifdef DEBUG_TX_ERROR - printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n", - dev->name); -#endif - spin_unlock_irqrestore(&lp->spinlock, flags); - return 1; - } - - /* Calculate addresses of next block and previous block. */ - txblock = lp->tx_first_free; - txpred = txblock - TXBLOCKZ; - if (txpred < OFFSET_CU) - txpred += NTXBLOCKS * TXBLOCKZ; - lp->tx_first_free += TXBLOCKZ; - if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; - - lp->tx_n_in_use++; - - /* Calculate addresses of the different parts of the block. */ - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - buf_addr = tbd_addr + sizeof(tbd); - - /* - * Transmit command - */ - tx.tx_h.ac_status = 0; - obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), - (unsigned char *) &tx.tx_h.ac_status, - sizeof(tx.tx_h.ac_status)); - - /* - * NOP command - */ - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* - * Transmit buffer descriptor - */ - tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen); - tbd.tbd_next_bd_offset = I82586NULL; - tbd.tbd_bufl = buf_addr; - tbd.tbd_bufh = 0; - obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd)); - - /* - * Data - */ - obram_write(ioaddr, buf_addr, buf, length); - - /* - * Overwrite the predecessor NOP link - * so that it points to this txblock. - */ - nop_addr = txpred + sizeof(tx); - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = txblock; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Make sure the watchdog will keep quiet for a while */ - dev->trans_start = jiffies; - - /* Keep stats up to date. */ - dev->stats.tx_bytes += length; - - if (lp->tx_first_in_use == I82586NULL) - lp->tx_first_in_use = txblock; - - if (lp->tx_n_in_use < NTXBLOCKS - 1) - netif_wake_queue(dev); - - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_TX_INFO - wv_packet_info((u8 *) buf, length, dev->name, - "wv_packet_write"); -#endif /* DEBUG_TX_INFO */ - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); -#endif - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine is called when we want to send a packet (NET3 callback) - * In this routine, we check if the harware is ready to accept - * the packet. We also prevent reentrance. Then we call the function - * to send the packet. - */ -static netdev_tx_t wavelan_packet_xmit(struct sk_buff *skb, - struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - char data[ETH_ZLEN]; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, - (unsigned) skb); -#endif - - /* - * Block a timer-based transmit from overlapping. - * In other words, prevent reentering this routine. - */ - netif_stop_queue(dev); - - /* If somebody has asked to reconfigure the controller, - * we can do it now. - */ - if (lp->reconfig_82586) { - spin_lock_irqsave(&lp->spinlock, flags); - wv_82586_config(dev); - spin_unlock_irqrestore(&lp->spinlock, flags); - /* Check that we can continue */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) - return NETDEV_TX_BUSY; - } - - /* Do we need some padding? */ - /* Note : on wireless the propagation time is in the order of 1us, - * and we don't have the Ethernet specific requirement of beeing - * able to detect collisions, therefore in theory we don't really - * need to pad. Jean II */ - if (skb->len < ETH_ZLEN) { - memset(data, 0, ETH_ZLEN); - skb_copy_from_linear_data(skb, data, skb->len); - /* Write packet on the card */ - if(wv_packet_write(dev, data, ETH_ZLEN)) - return NETDEV_TX_BUSY; /* We failed */ - } - else if(wv_packet_write(dev, skb->data, skb->len)) - return NETDEV_TX_BUSY; /* We failed */ - - - dev_kfree_skb(skb); - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); -#endif - return NETDEV_TX_OK; -} - -/*********************** HARDWARE CONFIGURATION ***********************/ -/* - * This part does the real job of starting and configuring the hardware. - */ - -/*--------------------------------------------------------------------*/ -/* - * Routine to initialize the Modem Management Controller. - * (called by wv_hw_reset()) - */ -static int wv_mmc_init(struct net_device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - mmw_t m; - int configured; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); -#endif - - /* Read the parameter storage area. */ - psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); - -#ifdef USE_PSA_CONFIG - configured = psa.psa_conf_status & 1; -#else - configured = 0; -#endif - - /* Is the PSA is not configured */ - if (!configured) { - /* User will be able to configure NWID later (with iwconfig). */ - psa.psa_nwid[0] = 0; - psa.psa_nwid[1] = 0; - - /* no NWID checking since NWID is not set */ - psa.psa_nwid_select = 0; - - /* Disable encryption */ - psa.psa_encryption_select = 0; - - /* Set to standard values: - * 0x04 for AT, - * 0x01 for MCA, - * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) - */ - if (psa.psa_comp_number & 1) - psa.psa_thr_pre_set = 0x01; - else - psa.psa_thr_pre_set = 0x04; - psa.psa_quality_thr = 0x03; - - /* It is configured */ - psa.psa_conf_status |= 1; - -#ifdef USE_PSA_CONFIG - /* Write the psa. */ - psa_write(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 4); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_conf_status - (char *) &psa, - (unsigned char *) &psa.psa_conf_status, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); -#endif - } - - /* Zero the mmc structure. */ - memset(&m, 0x00, sizeof(m)); - - /* Copy PSA info to the mmc. */ - m.mmw_netw_id_l = psa.psa_nwid[1]; - m.mmw_netw_id_h = psa.psa_nwid[0]; - - if (psa.psa_nwid_select & 1) - m.mmw_loopt_sel = 0x00; - else - m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; - - memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, - sizeof(m.mmw_encr_key)); - - if (psa.psa_encryption_select) - m.mmw_encr_enable = - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; - else - m.mmw_encr_enable = 0; - - m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; - m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; - - /* - * Set default modem control parameters. - * See NCR document 407-0024326 Rev. A. - */ - m.mmw_jabber_enable = 0x01; - m.mmw_freeze = 0; - m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; - m.mmw_ifs = 0x20; - m.mmw_mod_delay = 0x04; - m.mmw_jam_time = 0x38; - - m.mmw_des_io_invert = 0; - m.mmw_decay_prm = 0; - m.mmw_decay_updat_prm = 0; - - /* Write all info to MMC. */ - mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m)); - - /* The following code starts the modem of the 2.00 frequency - * selectable cards at power on. It's not strictly needed for the - * following boots. - * The original patch was by Joe Finney for the PCMCIA driver, but - * I've cleaned it up a bit and added documentation. - * Thanks to Loeke Brederveld from Lucent for the info. - */ - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) - * Does it work for everybody, especially old cards? */ - /* Note: WFREQSEL verifies that it is able to read a sensible - * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID - * is 0xA (Xilinx version) or 0xB (Ariadne version). - * My test is more crude but does work. */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - /* We must download the frequency parameters to the - * synthesizers (from the EEPROM - area 1) - * Note: as the EEPROM is automatically decremented, we set the end - * if the area... */ - m.mmw_fee_addr = 0x0F; - m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; - mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, - (unsigned char *) &m.mmw_fee_ctrl, 2); - - /* Wait until the download is finished. */ - fee_wait(ioaddr, 100, 100); - -#ifdef DEBUG_CONFIG_INFO - /* The frequency was in the last word downloaded. */ - mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m, - (unsigned char *) &m.mmw_fee_data_l, 2); - - /* Print some info for the user. */ - printk(KERN_DEBUG - "%s: WaveLAN 2.00 recognised (frequency select). Current frequency = %ld\n", - dev->name, - ((m. - mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) * - 5 / 2 + 24000L); -#endif - - /* We must now download the power adjust value (gain) to - * the synthesizers (from the EEPROM - area 7 - DAC). */ - m.mmw_fee_addr = 0x61; - m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; - mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, - (unsigned char *) &m.mmw_fee_ctrl, 2); - - /* Wait until the download is finished. */ - } - /* if 2.00 card */ -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Construct the fd and rbd structures. - * Start the receive unit. - * (called by wv_hw_reset()) - */ -static int wv_ru_start(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - u16 scb_cs; - fd_t fd; - rbd_t rbd; - u16 rx; - u16 rx_next; - int i; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); -#endif - - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY) - return 0; - - lp->rx_head = OFFSET_RU; - - for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) { - rx_next = - (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ; - - fd.fd_status = 0; - fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0; - fd.fd_link_offset = rx_next; - fd.fd_rbd_offset = rx + sizeof(fd); - obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd)); - - rbd.rbd_status = 0; - rbd.rbd_next_rbd_offset = I82586NULL; - rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd); - rbd.rbd_bufh = 0; - rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ); - obram_write(ioaddr, rx + sizeof(fd), - (unsigned char *) &rbd, sizeof(rbd)); - - lp->rx_last = rx; - } - - obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), - (unsigned char *) &lp->rx_head, sizeof(lp->rx_head)); - - scb_cs = SCB_CMD_RUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_ru_start(): board not accepting command.\n", - dev->name); -#endif - return -1; - } -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Initialise the transmit blocks. - * Start the command unit executing the NOP - * self-loop of the first transmit block. - * - * Here we create the list of send buffers used to transmit packets - * between the PC and the command unit. For each buffer, we create a - * buffer descriptor (pointing on the buffer), a transmit command - * (pointing to the buffer descriptor) and a NOP command. - * The transmit command is linked to the NOP, and the NOP to itself. - * When we will have finished executing the transmit command, we will - * then loop on the NOP. By releasing the NOP link to a new command, - * we may send another buffer. - * - * (called by wv_hw_reset()) - */ -static int wv_cu_start(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - int i; - u16 txblock; - u16 first_nop; - u16 scb_cs; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name); -#endif - - lp->tx_first_free = OFFSET_CU; - lp->tx_first_in_use = I82586NULL; - - for (i = 0, txblock = OFFSET_CU; - i < NTXBLOCKS; i++, txblock += TXBLOCKZ) { - ac_tx_t tx; - ac_nop_t nop; - tbd_t tbd; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short buf_addr; - - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - buf_addr = tbd_addr + sizeof(tbd); - - tx.tx_h.ac_status = 0; - tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I; - tx.tx_h.ac_link = nop_addr; - tx.tx_tbd_offset = tbd_addr; - obram_write(ioaddr, tx_addr, (unsigned char *) &tx, - sizeof(tx)); - - nop.nop_h.ac_status = 0; - nop.nop_h.ac_command = acmd_nop; - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, nop_addr, (unsigned char *) &nop, - sizeof(nop)); - - tbd.tbd_status = TBD_STATUS_EOF; - tbd.tbd_next_bd_offset = I82586NULL; - tbd.tbd_bufl = buf_addr; - tbd.tbd_bufh = 0; - obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, - sizeof(tbd)); - } - - first_nop = - OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t); - obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), - (unsigned char *) &first_nop, sizeof(first_nop)); - - scb_cs = SCB_CMD_CUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_cu_start(): board not accepting command.\n", - dev->name); -#endif - return -1; - } - - lp->tx_n_in_use = 0; - netif_start_queue(dev); -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine does a standard configuration of the WaveLAN - * controller (i82586). - * - * It initialises the scp, iscp and scb structure - * The first two are just pointers to the next. - * The last one is used for basic configuration and for basic - * communication (interrupt status). - * - * (called by wv_hw_reset()) - */ -static int wv_82586_start(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - scp_t scp; /* system configuration pointer */ - iscp_t iscp; /* intermediate scp */ - scb_t scb; /* system control block */ - ach_t cb; /* Action command header */ - u8 zeroes[512]; - int i; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name); -#endif - - /* - * Clear the onboard RAM. - */ - memset(&zeroes[0], 0x00, sizeof(zeroes)); - for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes)) - obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes)); - - /* - * Construct the command unit structures: - * scp, iscp, scb, cb. - */ - memset(&scp, 0x00, sizeof(scp)); - scp.scp_sysbus = SCP_SY_16BBUS; - scp.scp_iscpl = OFFSET_ISCP; - obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp, - sizeof(scp)); - - memset(&iscp, 0x00, sizeof(iscp)); - iscp.iscp_busy = 1; - iscp.iscp_offset = OFFSET_SCB; - obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, - sizeof(iscp)); - - /* Our first command is to reset the i82586. */ - memset(&scb, 0x00, sizeof(scb)); - scb.scb_command = SCB_CMD_RESET; - scb.scb_cbl_offset = OFFSET_CU; - scb.scb_rfa_offset = OFFSET_RU; - obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - set_chan_attn(ioaddr, lp->hacr); - - /* Wait for command to finish. */ - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, - sizeof(iscp)); - - if (iscp.iscp_busy == (unsigned short) 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): iscp_busy timeout.\n", - dev->name); -#endif - return -1; - } - - /* Check command completion. */ - for (i = 15; i > 0; i--) { - obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA)) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", - dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); -#endif - return -1; - } - - wv_ack(dev); - - /* Set the action command header. */ - memset(&cb, 0x00, sizeof(cb)); - cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose); - cb.ac_link = OFFSET_CU; - obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); - - if (wv_synchronous_cmd(dev, "diag()") == -1) - return -1; - - obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); - if (cb.ac_status & AC_SFLD_FAIL) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): i82586 Self Test failed.\n", - dev->name); -#endif - return -1; - } -#ifdef DEBUG_I82586_SHOW - wv_scb_show(ioaddr); -#endif - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine does a standard configuration of the WaveLAN - * controller (i82586). - * - * This routine is a violent hack. We use the first free transmit block - * to make our configuration. In the buffer area, we create the three - * configuration commands (linked). We make the previous NOP point to - * the beginning of the buffer instead of the tx command. After, we go - * as usual to the NOP command. - * Note that only the last command (mc_set) will generate an interrupt. - * - * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit()) - */ -static void wv_82586_config(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - unsigned short txblock; - unsigned short txpred; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short cfg_addr; - unsigned short ias_addr; - unsigned short mcs_addr; - ac_tx_t tx; - ac_nop_t nop; - ac_cfg_t cfg; /* Configure action */ - ac_ias_t ias; /* IA-setup action */ - ac_mcs_t mcs; /* Multicast setup */ - struct dev_mc_list *dmi; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); -#endif - - /* Check nothing bad has happened */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n", - dev->name); -#endif - return; - } - - /* Calculate addresses of next block and previous block. */ - txblock = lp->tx_first_free; - txpred = txblock - TXBLOCKZ; - if (txpred < OFFSET_CU) - txpred += NTXBLOCKS * TXBLOCKZ; - lp->tx_first_free += TXBLOCKZ; - if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; - - lp->tx_n_in_use++; - - /* Calculate addresses of the different parts of the block. */ - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */ - ias_addr = cfg_addr + sizeof(cfg); - mcs_addr = ias_addr + sizeof(ias); - - /* - * Transmit command - */ - tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */ - obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), - (unsigned char *) &tx.tx_h.ac_status, - sizeof(tx.tx_h.ac_status)); - - /* - * NOP command - */ - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Create a configure action. */ - memset(&cfg, 0x00, sizeof(cfg)); - - /* - * For Linux we invert AC_CFG_ALOC() so as to conform - * to the way that net packets reach us from above. - * (See also ac_tx_t.) - * - * Updated from Wavelan Manual WCIN085B - */ - cfg.cfg_byte_cnt = - AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); - cfg.cfg_fifolim = AC_CFG_FIFOLIM(4); - cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0); - cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | - AC_CFG_ILPBCK(0) | - AC_CFG_PRELEN(AC_CFG_PLEN_2) | - AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); - cfg.cfg_byte10 = AC_CFG_BOFMET(1) | - AC_CFG_ACR(6) | AC_CFG_LINPRIO(0); - cfg.cfg_ifs = 0x20; - cfg.cfg_slotl = 0x0C; - cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0); - cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | - AC_CFG_BTSTF(0) | - AC_CFG_CRC16(0) | - AC_CFG_NCRC(0) | - AC_CFG_TNCRS(1) | - AC_CFG_MANCH(0) | - AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous); - cfg.cfg_byte15 = AC_CFG_ICDS(0) | - AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0); -/* - cfg.cfg_min_frm_len = AC_CFG_MNFRM(64); -*/ - cfg.cfg_min_frm_len = AC_CFG_MNFRM(8); - - cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure); - cfg.cfg_h.ac_link = ias_addr; - obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg)); - - /* Set up the MAC address */ - memset(&ias, 0x00, sizeof(ias)); - ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup); - ias.ias_h.ac_link = mcs_addr; - memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0], - sizeof(ias.ias_addr)); - obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias)); - - /* Initialize adapter's Ethernet multicast addresses */ - memset(&mcs, 0x00, sizeof(mcs)); - mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup); - mcs.mcs_h.ac_link = nop_addr; - mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count; - obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs)); - - /* Any address to set? */ - if (lp->mc_count) { - for (dmi = dev->mc_list; dmi; dmi = dmi->next) - outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr, - WAVELAN_ADDR_SIZE >> 1); - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wv_82586_config(): set %d multicast addresses:\n", - dev->name, lp->mc_count); - for (dmi = dev->mc_list; dmi; dmi = dmi->next) - printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); -#endif - } - - /* - * Overwrite the predecessor NOP link - * so that it points to the configure action. - */ - nop_addr = txpred + sizeof(tx); - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = cfg_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Job done, clear the flag */ - lp->reconfig_82586 = 0; - - if (lp->tx_first_in_use == I82586NULL) - lp->tx_first_in_use = txblock; - - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) - netif_stop_queue(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * This routine, called by wavelan_close(), gracefully stops the - * WaveLAN controller (i82586). - * (called by wavelan_close()) - */ -static void wv_82586_stop(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - u16 scb_cmd; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name); -#endif - - /* Suspend both command unit and receive unit. */ - scb_cmd = - (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & - SCB_CMD_RUC_SUS); - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cmd, sizeof(scb_cmd)); - set_chan_attn(ioaddr, lp->hacr); - - /* No more interrupts */ - wv_ints_off(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Totally reset the WaveLAN and restart it. - * Performs the following actions: - * 1. A power reset (reset DMA) - * 2. Initialize the radio modem (using wv_mmc_init) - * 3. Reset & Configure LAN controller (using wv_82586_start) - * 4. Start the LAN controller's command unit - * 5. Start the LAN controller's receive unit - * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open()) - */ -static int wv_hw_reset(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long ioaddr = dev->base_addr; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* Increase the number of resets done. */ - lp->nresets++; - - wv_hacr_reset(ioaddr); - lp->hacr = HACR_DEFAULT; - - if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0)) - return -1; - - /* Enable the card to send interrupts. */ - wv_ints_on(dev); - - /* Start card functions */ - if (wv_cu_start(dev) < 0) - return -1; - - /* Setup the controller and parameters */ - wv_82586_config(dev); - - /* Finish configuration with the receive unit */ - if (wv_ru_start(dev) < 0) - return -1; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Check if there is a WaveLAN at the specific base address. - * As a side effect, this reads the MAC address. - * (called in wavelan_probe() and init_module()) - */ -static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac) -{ - int i; /* Loop counter */ - - /* Check if the base address if available. */ - if (!request_region(ioaddr, sizeof(ha_t), "wavelan probe")) - return -EBUSY; /* ioaddr already used */ - - /* Reset host interface */ - wv_hacr_reset(ioaddr); - - /* Read the MAC address from the parameter storage area. */ - psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), - mac, 6); - - release_region(ioaddr, sizeof(ha_t)); - - /* - * Check the first three octets of the address for the manufacturer's code. - * Note: if this can't find your WaveLAN card, you've got a - * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on - * how to configure your card. - */ - for (i = 0; i < ARRAY_SIZE(MAC_ADDRESSES); i++) - if ((mac[0] == MAC_ADDRESSES[i][0]) && - (mac[1] == MAC_ADDRESSES[i][1]) && - (mac[2] == MAC_ADDRESSES[i][2])) - return 0; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_WARNING - "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n", - ioaddr, mac[0], mac[1], mac[2]); -#endif - return -ENODEV; -} - -/************************ INTERRUPT HANDLING ************************/ - -/* - * This function is the interrupt handler for the WaveLAN card. This - * routine will be called whenever: - */ -static irqreturn_t wavelan_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - unsigned long ioaddr; - net_local *lp; - u16 hasr; - u16 status; - u16 ack_cmd; - - dev = dev_id; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); -#endif - - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - -#ifdef DEBUG_INTERRUPT_INFO - /* Check state of our spinlock */ - if(spin_is_locked(&lp->spinlock)) - printk(KERN_DEBUG - "%s: wavelan_interrupt(): spinlock is already locked !!!\n", - dev->name); -#endif - - /* Prevent reentrancy. We need to do that because we may have - * multiple interrupt handler running concurrently. - * It is safe because interrupts are disabled before acquiring - * the spinlock. */ - spin_lock(&lp->spinlock); - - /* We always had spurious interrupts at startup, but lately I - * saw them comming *between* the request_irq() and the - * spin_lock_irqsave() in wavelan_open(), so the spinlock - * protection is no enough. - * So, we also check lp->hacr that will tell us is we enabled - * irqs or not (see wv_ints_on()). - * We can't use netif_running(dev) because we depend on the - * proper processing of the irq generated during the config. */ - - /* Which interrupt it is ? */ - hasr = hasr_read(ioaddr); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_INFO - "%s: wavelan_interrupt(): hasr 0x%04x; hacr 0x%04x.\n", - dev->name, hasr, lp->hacr); -#endif - - /* Check modem interrupt */ - if ((hasr & HASR_MMC_INTR) && (lp->hacr & HACR_MMC_INT_ENABLE)) { - u8 dce_status; - - /* - * Interrupt from the modem management controller. - * This will clear it -- ignored for now. - */ - mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, - sizeof(dce_status)); - -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", - dev->name, dce_status); -#endif - } - - /* Check if not controller interrupt */ - if (((hasr & HASR_82586_INTR) == 0) || - ((lp->hacr & HACR_82586_INT_ENABLE) == 0)) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): interrupt not coming from i82586 - hasr 0x%04x.\n", - dev->name, hasr); -#endif - spin_unlock (&lp->spinlock); - return IRQ_NONE; - } - - /* Read interrupt data. */ - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &status, sizeof(status)); - - /* - * Acknowledge the interrupt(s). - */ - ack_cmd = status & SCB_ST_INT; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &ack_cmd, sizeof(ack_cmd)); - set_chan_attn(ioaddr, lp->hacr); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n", - dev->name, status); -#endif - - /* Command completed. */ - if ((status & SCB_ST_CX) == SCB_ST_CX) { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_interrupt(): command completed.\n", - dev->name); -#endif - wv_complete(dev, ioaddr, lp); - } - - /* Frame received. */ - if ((status & SCB_ST_FR) == SCB_ST_FR) { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_interrupt(): received packet.\n", - dev->name); -#endif - wv_receive(dev); - } - - /* Check the state of the command unit. */ - if (((status & SCB_ST_CNA) == SCB_ST_CNA) || - (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && - (netif_running(dev)))) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): CU inactive -- restarting\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* Check the state of the command unit. */ - if (((status & SCB_ST_RNR) == SCB_ST_RNR) || - (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && - (netif_running(dev)))) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): RU not ready -- restarting\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* Release spinlock */ - spin_unlock (&lp->spinlock); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); -#endif - return IRQ_HANDLED; -} - -/*------------------------------------------------------------------*/ -/* - * Watchdog: when we start a transmission, a timer is set for us in the - * kernel. If the transmission completes, this timer is disabled. If - * the timer expires, we are called and we try to unlock the hardware. - */ -static void wavelan_watchdog(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - unsigned long flags; - unsigned int nreaped; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); -#endif - -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", - dev->name); -#endif - - /* Check that we came here for something */ - if (lp->tx_n_in_use <= 0) { - return; - } - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Try to see if some buffers are not free (in case we missed - * an interrupt */ - nreaped = wv_complete(dev, ioaddr, lp); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_watchdog(): %d reaped, %d remain.\n", - dev->name, nreaped, lp->tx_n_in_use); -#endif - -#ifdef DEBUG_PSA_SHOW - { - psa_t psa; - psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); - wv_psa_show(&psa); - } -#endif -#ifdef DEBUG_MMC_SHOW - wv_mmc_show(dev); -#endif -#ifdef DEBUG_I82586_SHOW - wv_cu_show(dev); -#endif - - /* If no buffer has been freed */ - if (nreaped == 0) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_watchdog(): cleanup failed, trying reset\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* At this point, we should have some free Tx buffer ;-) */ - if (lp->tx_n_in_use < NTXBLOCKS - 1) - netif_wake_queue(dev); - - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); -#endif -} - -/********************* CONFIGURATION CALLBACKS *********************/ -/* - * Here are the functions called by the Linux networking code (NET3) - * for initialization, configuration and deinstallations of the - * WaveLAN ISA hardware. - */ - -/*------------------------------------------------------------------*/ -/* - * Configure and start up the WaveLAN PCMCIA adaptor. - * Called by NET3 when it "opens" the device. - */ -static int wavelan_open(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* Check irq */ - if (dev->irq == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", - dev->name); -#endif - return -ENXIO; - } - - if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) - { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", - dev->name); -#endif - return -EAGAIN; - } - - spin_lock_irqsave(&lp->spinlock, flags); - - if (wv_hw_reset(dev) != -1) { - netif_start_queue(dev); - } else { - free_irq(dev->irq, dev); -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_open(): impossible to start the card\n", - dev->name); -#endif - spin_unlock_irqrestore(&lp->spinlock, flags); - return -EAGAIN; - } - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Shut down the WaveLAN ISA card. - * Called by NET3 when it "closes" the device. - */ -static int wavelan_close(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - netif_stop_queue(dev); - - /* - * Flush the Tx and disable Rx. - */ - spin_lock_irqsave(&lp->spinlock, flags); - wv_82586_stop(dev); - spin_unlock_irqrestore(&lp->spinlock, flags); - - free_irq(dev->irq, dev); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); -#endif - return 0; -} - -static const struct net_device_ops wavelan_netdev_ops = { - .ndo_open = wavelan_open, - .ndo_stop = wavelan_close, - .ndo_start_xmit = wavelan_packet_xmit, - .ndo_set_multicast_list = wavelan_set_multicast_list, - .ndo_tx_timeout = wavelan_watchdog, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -#ifdef SET_MAC_ADDRESS - .ndo_set_mac_address = wavelan_set_mac_address -#else - .ndo_set_mac_address = eth_mac_addr, -#endif -}; - - -/*------------------------------------------------------------------*/ -/* - * Probe an I/O address, and if the WaveLAN is there configure the - * device structure - * (called by wavelan_probe() and via init_module()). - */ -static int __init wavelan_config(struct net_device *dev, unsigned short ioaddr) -{ - u8 irq_mask; - int irq; - net_local *lp; - mac_addr mac; - int err; - - if (!request_region(ioaddr, sizeof(ha_t), "wavelan")) - return -EADDRINUSE; - - err = wv_check_ioaddr(ioaddr, mac); - if (err) - goto out; - - memcpy(dev->dev_addr, mac, 6); - - dev->base_addr = ioaddr; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n", - dev->name, (unsigned int) dev, ioaddr); -#endif - - /* Check IRQ argument on command line. */ - if (dev->irq != 0) { - irq_mask = wv_irq_to_psa(dev->irq); - - if (irq_mask == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "%s: wavelan_config(): invalid IRQ %d ignored.\n", - dev->name, dev->irq); -#endif - dev->irq = 0; - } else { -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wavelan_config(): changing IRQ to %d\n", - dev->name, dev->irq); -#endif - psa_write(ioaddr, HACR_DEFAULT, - psaoff(0, psa_int_req_no), &irq_mask, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, HACR_DEFAULT); - wv_hacr_reset(ioaddr); - } - } - - psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), - &irq_mask, 1); - if ((irq = wv_psa_to_irq(irq_mask)) == -1) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_config(): could not wavelan_map_irq(%d).\n", - dev->name, irq_mask); -#endif - err = -EAGAIN; - goto out; - } - - dev->irq = irq; - - dev->mem_start = 0x0000; - dev->mem_end = 0x0000; - dev->if_port = 0; - - /* Initialize device structures */ - memset(netdev_priv(dev), 0, sizeof(net_local)); - lp = netdev_priv(dev); - - /* Back link to the device structure. */ - lp->dev = dev; - /* Add the device at the beginning of the linked list. */ - lp->next = wavelan_list; - wavelan_list = lp; - - lp->hacr = HACR_DEFAULT; - - /* Multicast stuff */ - lp->promiscuous = 0; - lp->mc_count = 0; - - /* Init spinlock */ - spin_lock_init(&lp->spinlock); - - dev->netdev_ops = &wavelan_netdev_ops; - dev->watchdog_timeo = WATCHDOG_JIFFIES; - dev->wireless_handlers = &wavelan_handler_def; - lp->wireless_data.spy_data = &lp->spy_data; - dev->wireless_data = &lp->wireless_data; - - dev->mtu = WAVELAN_MTU; - - /* Display nice information. */ - wv_init_info(dev); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name); -#endif - return 0; -out: - release_region(ioaddr, sizeof(ha_t)); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Check for a network adaptor of this type. Return '0' iff one - * exists. There seem to be different interpretations of - * the initial value of dev->base_addr. - * We follow the example in drivers/net/ne.c. - * (called in "Space.c") - */ -struct net_device * __init wavelan_probe(int unit) -{ - struct net_device *dev; - short base_addr; - int def_irq; - int i; - int r = 0; - - /* compile-time check the sizes of structures */ - BUILD_BUG_ON(sizeof(psa_t) != PSA_SIZE); - BUILD_BUG_ON(sizeof(mmw_t) != MMW_SIZE); - BUILD_BUG_ON(sizeof(mmr_t) != MMR_SIZE); - BUILD_BUG_ON(sizeof(ha_t) != HA_SIZE); - - dev = alloc_etherdev(sizeof(net_local)); - if (!dev) - return ERR_PTR(-ENOMEM); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - base_addr = dev->base_addr; - def_irq = dev->irq; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG - "%s: ->wavelan_probe(dev=%p (base_addr=0x%x))\n", - dev->name, dev, (unsigned int) dev->base_addr); -#endif - - /* Don't probe at all. */ - if (base_addr < 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "%s: wavelan_probe(): invalid base address\n", - dev->name); -#endif - r = -ENXIO; - } else if (base_addr > 0x100) { /* Check a single specified location. */ - r = wavelan_config(dev, base_addr); -#ifdef DEBUG_CONFIG_INFO - if (r != 0) - printk(KERN_DEBUG - "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n", - dev->name, base_addr); -#endif - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); -#endif - } else { /* Scan all possible addresses of the WaveLAN hardware. */ - for (i = 0; i < ARRAY_SIZE(iobase); i++) { - dev->irq = def_irq; - if (wavelan_config(dev, iobase[i]) == 0) { -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG - "%s: <-wavelan_probe()\n", - dev->name); -#endif - break; - } - } - if (i == ARRAY_SIZE(iobase)) - r = -ENODEV; - } - if (r) - goto out; - r = register_netdev(dev); - if (r) - goto out1; - return dev; -out1: - release_region(dev->base_addr, sizeof(ha_t)); - wavelan_list = wavelan_list->next; -out: - free_netdev(dev); - return ERR_PTR(r); -} - -/****************************** MODULE ******************************/ -/* - * Module entry point: insertion and removal - */ - -#ifdef MODULE -/*------------------------------------------------------------------*/ -/* - * Insertion of the module - * I'm now quite proud of the multi-device support. - */ -int __init init_module(void) -{ - int ret = -EIO; /* Return error if no cards found */ - int i; - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "-> init_module()\n"); -#endif - - /* If probing is asked */ - if (io[0] == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "WaveLAN init_module(): doing device probing (bad !)\n"); - printk(KERN_WARNING - "Specify base addresses while loading module to correct the problem\n"); -#endif - - /* Copy the basic set of address to be probed. */ - for (i = 0; i < ARRAY_SIZE(iobase); i++) - io[i] = iobase[i]; - } - - - /* Loop on all possible base addresses. */ - for (i = 0; i < ARRAY_SIZE(io) && io[i] != 0; i++) { - struct net_device *dev = alloc_etherdev(sizeof(net_local)); - if (!dev) - break; - if (name[i]) - strcpy(dev->name, name[i]); /* Copy name */ - dev->base_addr = io[i]; - dev->irq = irq[i]; - - /* Check if there is something at this base address. */ - if (wavelan_config(dev, io[i]) == 0) { - if (register_netdev(dev) != 0) { - release_region(dev->base_addr, sizeof(ha_t)); - wavelan_list = wavelan_list->next; - } else { - ret = 0; - continue; - } - } - free_netdev(dev); - } - -#ifdef DEBUG_CONFIG_ERROR - if (!wavelan_list) - printk(KERN_WARNING - "WaveLAN init_module(): no device found\n"); -#endif - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "<- init_module()\n"); -#endif - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Removal of the module - */ -void cleanup_module(void) -{ -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "-> cleanup_module()\n"); -#endif - - /* Loop on all devices and release them. */ - while (wavelan_list) { - struct net_device *dev = wavelan_list->dev; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: cleanup_module(): removing device at 0x%x\n", - dev->name, (unsigned int) dev); -#endif - unregister_netdev(dev); - - release_region(dev->base_addr, sizeof(ha_t)); - wavelan_list = wavelan_list->next; - - free_netdev(dev); - } - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "<- cleanup_module()\n"); -#endif -} -#endif /* MODULE */ -MODULE_LICENSE("GPL"); - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * This software was developed as a component of the - * Linux operating system. - * It is based on other device drivers and information - * either written or supplied by: - * Ajay Bakre (bakre@paul.rutgers.edu), - * Donald Becker (becker@scyld.com), - * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), - * Anders Klemets (klemets@it.kth.se), - * Vladimir V. Kolpakov (w@stier.koenig.ru), - * Marc Meertens (Marc.Meertens@Utrecht.NCR.com), - * Pauline Middelink (middelin@polyware.iaf.nl), - * Robert Morris (rtm@das.harvard.edu), - * Jean Tourrilhes (jt@hplb.hpl.hp.com), - * Girish Welling (welling@paul.rutgers.edu), - * - * Thanks go also to: - * James Ashton (jaa101@syseng.anu.edu.au), - * Alan Cox (alan@lxorguk.ukuu.org.uk), - * Allan Creighton (allanc@cs.usyd.edu.au), - * Matthew Geier (matthew@cs.usyd.edu.au), - * Remo di Giovanni (remo@cs.usyd.edu.au), - * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), - * Vipul Gupta (vgupta@cs.binghamton.edu), - * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), - * Tim Nicholson (tim@cs.usyd.edu.au), - * Ian Parkin (ian@cs.usyd.edu.au), - * John Rosenberg (johnr@cs.usyd.edu.au), - * George Rossi (george@phm.gov.au), - * Arthur Scott (arthur@cs.usyd.edu.au), - * Peter Storey, - * for their assistance and advice. - * - * Please send bug reports, updates, comments to: - * - * Bruce Janson Email: bruce@cs.usyd.edu.au - * Basser Department of Computer Science Phone: +61-2-9351-3423 - * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838 - */ diff --git a/drivers/net/wireless/wavelan.h b/drivers/net/wireless/wavelan.h deleted file mode 100644 index 9ab360558ffd..000000000000 --- a/drivers/net/wireless/wavelan.h +++ /dev/null @@ -1,370 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * Original copyright follows. See wavelan.p.h for details. - * - * This file contains the declarations for the WaveLAN hardware. Note that - * the WaveLAN ISA includes a i82586 controller (see definitions in - * file i82586.h). - * - * The main difference between the ISA hardware and the PCMCIA one is - * the Ethernet controller (i82586 instead of i82593). - * The i82586 allows multiple transmit buffers. The PSA needs to be accessed - * through the host interface. - */ - -#ifndef _WAVELAN_H -#define _WAVELAN_H - -/************************** MAGIC NUMBERS ***************************/ - -/* Detection of the WaveLAN card is done by reading the MAC - * address from the card and checking it. If you have a non-AT&T - * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson), - * you might need to modify this part to accommodate your hardware. - */ -static const char MAC_ADDRESSES[][3] = -{ - { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */ - { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */ - { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ - { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ - /* Add your card here and send me the patch! */ -}; - -#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */ - -#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */ - -#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU) - -/* - * Constants used to convert channels to frequencies - */ - -/* Frequency available in the 2.0 modem, in units of 250 kHz - * (as read in the offset register of the dac area). - * Used to map channel numbers used by `wfreqsel' to frequencies - */ -static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, - 0xD0, 0xF0, 0xF8, 0x150 }; - -/* Frequencies of the 1.0 modem (fixed frequencies). - * Use to map the PSA `subband' to a frequency - * Note : all frequencies apart from the first one need to be multiplied by 10 - */ -static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; - - - -/*************************** PC INTERFACE ****************************/ - -/* - * Host Adaptor structure. - * (base is board port address). - */ -typedef union hacs_u hacs_u; -union hacs_u -{ - unsigned short hu_command; /* Command register */ -#define HACR_RESET 0x0001 /* Reset board */ -#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */ -#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */ -#define HACR_OUT0 0x0008 /* General purpose output pin 0 */ - /* not used - must be 1 */ -#define HACR_OUT1 0x0010 /* General purpose output pin 1 */ - /* not used - must be 1 */ -#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */ -#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */ -#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */ - unsigned short hu_status; /* Status Register */ -#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */ -#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */ -#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */ -#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */ -} __attribute__ ((packed)); - -typedef struct ha_t ha_t; -struct ha_t -{ - hacs_u ha_cs; /* Command and status registers */ -#define ha_command ha_cs.hu_command -#define ha_status ha_cs.hu_status - unsigned short ha_mmcr; /* Modem Management Ctrl Register */ - unsigned short ha_pior0; /* Program I/O Address Register Port 0 */ - unsigned short ha_piop0; /* Program I/O Port 0 */ - unsigned short ha_pior1; /* Program I/O Address Register Port 1 */ - unsigned short ha_piop1; /* Program I/O Port 1 */ - unsigned short ha_pior2; /* Program I/O Address Register Port 2 */ - unsigned short ha_piop2; /* Program I/O Port 2 */ -}; - -#define HA_SIZE 16 - -#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0) -#define HACR(p) hoff(p, ha_command) -#define HASR(p) hoff(p, ha_status) -#define MMCR(p) hoff(p, ha_mmcr) -#define PIOR0(p) hoff(p, ha_pior0) -#define PIOP0(p) hoff(p, ha_piop0) -#define PIOR1(p) hoff(p, ha_pior1) -#define PIOP1(p) hoff(p, ha_piop1) -#define PIOR2(p) hoff(p, ha_pior2) -#define PIOP2(p) hoff(p, ha_piop2) - -/* - * Program I/O Mode Register values. - */ -#define STATIC_PIO 0 /* Mode 1: static mode */ - /* RAM access ??? */ -#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */ - /* RAM access ??? */ -#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */ - /* RAM access ??? */ -#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */ - /* Parameter access. */ -#define PIO_MASK 3 /* register mask */ -#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2)) - -#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2)) -#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE) - -/************************** MEMORY LAYOUT **************************/ - -/* - * Onboard 64 k RAM layout. - * (Offsets from 0x0000.) - */ -#define OFFSET_RU 0x0000 /* 75% memory */ -#define OFFSET_CU 0xC000 /* 25% memory */ -#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t)) -#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t)) -#define OFFSET_SCP I82586_SCP_ADDR - -#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ) -#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ) - -#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ) -#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ) - -/********************** PARAMETER STORAGE AREA **********************/ - -/* - * Parameter Storage Area (PSA). - */ -typedef struct psa_t psa_t; -struct psa_t -{ - unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */ - unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */ - unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */ - unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */ - unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */ - unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */ - unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */ - unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */ - unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */ - unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */ - - unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */ - unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */ - unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */ -#define PSA_UNIVERSAL 0 /* Universal (factory) */ -#define PSA_LOCAL 1 /* Local */ - unsigned char psa_comp_number; /* [0x1D] Compatibility Number: */ -#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */ -#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */ -#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */ -#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */ -#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */ - unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */ - unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */ -#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */ - unsigned char psa_subband; /* [0x20] Subband */ -#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */ -#define PSA_SUBBAND_2425 1 /* 2425 MHz */ -#define PSA_SUBBAND_2460 2 /* 2460 MHz */ -#define PSA_SUBBAND_2484 3 /* 2484 MHz */ -#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */ - unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */ - unsigned char psa_mod_delay; /* [0x22] Modem Delay (?) (reserved) */ - unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */ - unsigned char psa_nwid_select; /* [0x25] Network ID Select On/Off */ - unsigned char psa_encryption_select; /* [0x26] Encryption On/Off */ - unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */ - unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */ - unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */ - unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */ - unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */ - unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/ - unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */ - unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */ -}; - -#define PSA_SIZE 64 - -/* Calculate offset of a field in the above structure. - * Warning: only even addresses are used. */ -#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) - -/******************** MODEM MANAGEMENT INTERFACE ********************/ - -/* - * Modem Management Controller (MMC) write structure. - */ -typedef struct mmw_t mmw_t; -struct mmw_t -{ - unsigned char mmw_encr_key[8]; /* encryption key */ - unsigned char mmw_encr_enable; /* Enable or disable encryption. */ -#define MMW_ENCR_ENABLE_MODE 0x02 /* mode of security option */ -#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option. */ - unsigned char mmw_unused0[1]; /* unused */ - unsigned char mmw_des_io_invert; /* encryption option */ -#define MMW_DES_IO_INVERT_RES 0x0F /* reserved */ -#define MMW_DES_IO_INVERT_CTRL 0xF0 /* control (?) (set to 0) */ - unsigned char mmw_unused1[5]; /* unused */ - unsigned char mmw_loopt_sel; /* looptest selection */ -#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* Disable NWID filtering. */ -#define MMW_LOOPT_SEL_INT 0x20 /* Activate Attention Request. */ -#define MMW_LOOPT_SEL_LS 0x10 /* looptest, no collision avoidance */ -#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */ -#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */ -#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */ -#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */ - unsigned char mmw_jabber_enable; /* jabber timer enable */ - /* Abort transmissions > 200 ms */ - unsigned char mmw_freeze; /* freeze or unfreeze signal level */ - /* 0 : signal level & qual updated for every new message, 1 : frozen */ - unsigned char mmw_anten_sel; /* antenna selection */ -#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */ -#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */ - unsigned char mmw_ifs; /* inter frame spacing */ - /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ - unsigned char mmw_mod_delay; /* modem delay (synchro) */ - unsigned char mmw_jam_time; /* jamming time (after collision) */ - unsigned char mmw_unused2[1]; /* unused */ - unsigned char mmw_thr_pre_set; /* level threshold preset */ - /* Discard all packet with signal < this value (4) */ - unsigned char mmw_decay_prm; /* decay parameters */ - unsigned char mmw_decay_updat_prm; /* decay update parameters */ - unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */ - /* Discard all packet with quality < this value (3) */ - unsigned char mmw_netw_id_l; /* NWID low order byte */ - unsigned char mmw_netw_id_h; /* NWID high order byte */ - /* Network ID or Domain : create virtual net on the air */ - - /* 2.0 Hardware extension - frequency selection support */ - unsigned char mmw_mode_select; /* for analog tests (set to 0) */ - unsigned char mmw_unused3[1]; /* unused */ - unsigned char mmw_fee_ctrl; /* frequency EEPROM control */ -#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions. */ -#define MMW_FEE_CTRL_DWLD 0x08 /* Download EEPROM to mmc. */ -#define MMW_FEE_CTRL_CMD 0x07 /* EEPROM commands: */ -#define MMW_FEE_CTRL_READ 0x06 /* Read */ -#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */ -#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address. */ -#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses. */ -#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */ -#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */ -#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */ -#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers. */ -#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write address in protect register */ -#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ - /* Never issue the PRDS command: it's irreversible! */ - - unsigned char mmw_fee_addr; /* EEPROM address */ -#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel. */ -#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */ -#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */ -#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */ -#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */ -#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */ - - unsigned char mmw_fee_data_l; /* Write data to EEPROM. */ - unsigned char mmw_fee_data_h; /* high octet */ - unsigned char mmw_ext_ant; /* Setting for external antenna */ -#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */ -#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */ -#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */ -#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */ -#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */ -} __attribute__ ((packed)); - -#define MMW_SIZE 37 - -#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) - -/* - * Modem Management Controller (MMC) read structure. - */ -typedef struct mmr_t mmr_t; -struct mmr_t -{ - unsigned char mmr_unused0[8]; /* unused */ - unsigned char mmr_des_status; /* encryption status */ - unsigned char mmr_des_avail; /* encryption available (0x55 read) */ -#define MMR_DES_AVAIL_DES 0x55 /* DES available */ -#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */ - unsigned char mmr_des_io_invert; /* des I/O invert register */ - unsigned char mmr_unused1[5]; /* unused */ - unsigned char mmr_dce_status; /* DCE status */ -#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */ -#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ -#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ -#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ -#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ - unsigned char mmr_dsp_id; /* DSP ID (AA = Daedalus rev A) */ - unsigned char mmr_unused2[2]; /* unused */ - unsigned char mmr_correct_nwid_l; /* # of correct NWIDs rxd (low) */ - unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */ - /* Warning: read high-order octet first! */ - unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */ - unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs rxd (high) */ - unsigned char mmr_thr_pre_set; /* level threshold preset */ -#define MMR_THR_PRE_SET 0x3F /* level threshold preset */ -#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */ - unsigned char mmr_signal_lvl; /* signal level */ -#define MMR_SIGNAL_LVL 0x3F /* signal level */ -#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */ - unsigned char mmr_silence_lvl; /* silence level (noise) */ -#define MMR_SILENCE_LVL 0x3F /* silence level */ -#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */ - unsigned char mmr_sgnl_qual; /* signal quality */ -#define MMR_SGNL_QUAL 0x0F /* signal quality */ -#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */ - unsigned char mmr_netw_id_l; /* NWID low order byte (?) */ - unsigned char mmr_unused3[3]; /* unused */ - - /* 2.0 Hardware extension - frequency selection support */ - unsigned char mmr_fee_status; /* Status of frequency EEPROM */ -#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision ID */ -#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */ -#define MMR_FEE_STATUS_BUSY 0x04 /* EEPROM busy */ - unsigned char mmr_unused4[1]; /* unused */ - unsigned char mmr_fee_data_l; /* Read data from EEPROM (low) */ - unsigned char mmr_fee_data_h; /* Read data from EEPROM (high) */ -} __attribute__ ((packed)); - -#define MMR_SIZE 36 - -#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) - -/* Make the two above structures one */ -typedef union mm_t -{ - struct mmw_t w; /* Write to the mmc */ - struct mmr_t r; /* Read from the mmc */ -} mm_t; - -#endif /* _WAVELAN_H */ - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * For more details, see wavelan.c. - */ diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h deleted file mode 100644 index dbe8de6e5f52..000000000000 --- a/drivers/net/wireless/wavelan.p.h +++ /dev/null @@ -1,696 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * - * This file contains all definitions and declarations necessary for the - * WaveLAN ISA driver. This file is a private header, so it should - * be included only in wavelan.c! - */ - -#ifndef WAVELAN_P_H -#define WAVELAN_P_H - -/************************** DOCUMENTATION ***************************/ -/* - * This driver provides a Linux interface to the WaveLAN ISA hardware. - * The WaveLAN is a product of Lucent (http://www.wavelan.com/). - * This division was formerly part of NCR and then AT&T. - * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean. - * - * To learn how to use this driver, read the NET3 HOWTO. - * If you want to exploit the many other functionalities, read the comments - * in the code. - * - * This driver is the result of the effort of many people (see below). - */ - -/* ------------------------ SPECIFIC NOTES ------------------------ */ -/* - * Web page - * -------- - * I try to maintain a web page with the Wireless LAN Howto at : - * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html - * - * SMP - * --- - * We now are SMP compliant (I eventually fixed the remaining bugs). - * The driver has been tested on a dual P6-150 and survived my usual - * set of torture tests. - * Anyway, I spent enough time chasing interrupt re-entrancy during - * errors or reconfigure, and I designed the locked/unlocked sections - * of the driver with great care, and with the recent addition of - * the spinlock (thanks to the new API), we should be quite close to - * the truth. - * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), - * but better safe than sorry (especially at 2 Mb/s ;-). - * - * I have also looked into disabling only our interrupt on the card - * (via HACR) instead of all interrupts in the processor (via cli), - * so that other driver are not impacted, and it look like it's - * possible, but it's very tricky to do right (full of races). As - * the gain would be mostly for SMP systems, it can wait... - * - * Debugging and options - * --------------------- - * You will find below a set of '#define" allowing a very fine control - * on the driver behaviour and the debug messages printed. - * The main options are : - * o SET_PSA_CRC, to have your card correctly recognised by - * an access point and the Point-to-Point diagnostic tool. - * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) - * (otherwise we always start afresh with some defaults) - * - * wavelan.o is too darned big - * --------------------------- - * That's true! There is a very simple way to reduce the driver - * object by 33%! Comment out the following line: - * #include - * Other compile options can also reduce the size of it... - * - * MAC address and hardware detection: - * ----------------------------------- - * The detection code for the WaveLAN checks that the first three - * octets of the MAC address fit the company code. This type of - * detection works well for AT&T cards (because the AT&T code is - * hardcoded in wavelan.h), but of course will fail for other - * manufacturers. - * - * If you are sure that your card is derived from the WaveLAN, - * here is the way to configure it: - * 1) Get your MAC address - * a) With your card utilities (wfreqsel, instconf, etc.) - * b) With the driver: - * o compile the kernel with DEBUG_CONFIG_INFO enabled - * o Boot and look the card messages - * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h) - * 3) Compile and verify - * 4) Send me the MAC code. I will include it in the next version. - * - */ - -/* --------------------- WIRELESS EXTENSIONS --------------------- */ -/* - * This driver is the first to support "wireless extensions". - * This set of extensions provides a standard way to control the wireless - * characteristics of the hardware. Applications such as mobile IP may - * take advantage of it. - * - * It might be a good idea as well to fetch the wireless tools to - * configure the device and play a bit. - */ - -/* ---------------------------- FILES ---------------------------- */ -/* - * wavelan.c: actual code for the driver: C functions - * - * wavelan.p.h: private header: local types and variables for driver - * - * wavelan.h: description of the hardware interface and structs - * - * i82586.h: description of the Ethernet controller - */ - -/* --------------------------- HISTORY --------------------------- */ -/* - * This is based on information in the drivers' headers. It may not be - * accurate, and I guarantee only my best effort. - * - * The history of the WaveLAN drivers is as complicated as the history of - * the WaveLAN itself (NCR -> AT&T -> Lucent). - * - * It all started with Anders Klemets - * writing a WaveLAN ISA driver for the Mach microkernel. Girish - * Welling had also worked on it. - * Keith Moore modified this for the PCMCIA hardware. - * - * Robert Morris ported these two drivers to BSDI - * and added specific PCMCIA support (there is currently no equivalent - * of the PCMCIA package under BSD). - * - * Jim Binkley ported both BSDI drivers to FreeBSD. - * - * Bruce Janson ported the BSDI ISA driver to Linux. - * - * Anthony D. Joseph started to modify Bruce's driver - * (with help of the BSDI PCMCIA driver) for PCMCIA. - * Yunzhou Li finished this work. - * Joe Finney patched the driver to start - * 2.00 cards correctly (2.4 GHz with frequency selection). - * David Hinds integrated the whole in his - * PCMCIA package (and bug corrections). - * - * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some - * patches to the PCMCIA driver. Later, I added code in the ISA driver - * for Wireless Extensions and full support of frequency selection - * cards. Then, I did the same to the PCMCIA driver, and did some - * reorganisation. Finally, I came back to the ISA driver to - * upgrade it at the same level as the PCMCIA one and reorganise - * the code. - * Loeke Brederveld from Lucent has given me - * much needed information on the WaveLAN hardware. - */ - -/* The original copyrights and literature mention others' names and - * credits. I don't know what their part in this development was. - */ - -/* By the way, for the copyright and legal stuff: - * almost everybody wrote code under the GNU or BSD license (or similar), - * and want their original copyright to remain somewhere in the - * code (for myself, I go with the GPL). - * Nobody wants to take responsibility for anything, except the fame. - */ - -/* --------------------------- CREDITS --------------------------- */ -/* - * This software was developed as a component of the - * Linux operating system. - * It is based on other device drivers and information - * either written or supplied by: - * Ajay Bakre , - * Donald Becker , - * Loeke Brederveld , - * Brent Elphick , - * Anders Klemets , - * Vladimir V. Kolpakov , - * Marc Meertens , - * Pauline Middelink , - * Robert Morris , - * Jean Tourrilhes , - * Girish Welling , - * Clark Woodworth - * Yongguang Zhang - * - * Thanks go also to: - * James Ashton , - * Alan Cox , - * Allan Creighton , - * Matthew Geier , - * Remo di Giovanni , - * Eckhard Grah , - * Vipul Gupta , - * Mark Hagan , - * Tim Nicholson , - * Ian Parkin , - * John Rosenberg , - * George Rossi , - * Arthur Scott , - * Stanislav Sinyagin - * and Peter Storey for their assistance and advice. - * - * Additional Credits: - * - * My development has been done initially under Debian 1.1 (Linux 2.0.x) - * and now under Debian 2.2, initially with an HP Vectra XP/60, and now - * an HP Vectra XP/90. - * - */ - -/* ------------------------- IMPROVEMENTS ------------------------- */ -/* - * I proudly present: - * - * Changes made in first pre-release: - * ---------------------------------- - * - reorganisation of the code, function name change - * - creation of private header (wavelan.p.h) - * - reorganised debug messages - * - more comments, history, etc. - * - mmc_init: configure the PSA if not done - * - mmc_init: correct default value of level threshold for PCMCIA - * - mmc_init: 2.00 detection better code for 2.00 initialization - * - better info at startup - * - IRQ setting (note: this setting is permanent) - * - watchdog: change strategy (and solve module removal problems) - * - add wireless extensions (ioctl and get_wireless_stats) - * get/set nwid/frequency on fly, info for /proc/net/wireless - * - more wireless extensions: SETSPY and GETSPY - * - make wireless extensions optional - * - private ioctl to set/get quality and level threshold, histogram - * - remove /proc/net/wavelan - * - suppress useless stuff from lp (net_local) - * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) - * - add message level (debug stuff in /var/adm/debug and errors not - * displayed at console and still in /var/adm/messages) - * - multi device support - * - start fixing the probe (init code) - * - more inlines - * - man page - * - many other minor details and cleanups - * - * Changes made in second pre-release: - * ----------------------------------- - * - clean up init code (probe and module init) - * - better multiple device support (module) - * - name assignment (module) - * - * Changes made in third pre-release: - * ---------------------------------- - * - be more conservative on timers - * - preliminary support for multicast (I still lack some details) - * - * Changes made in fourth pre-release: - * ----------------------------------- - * - multicast (revisited and finished) - * - avoid reset in set_multicast_list (a really big hack) - * if somebody could apply this code for other i82586 based drivers - * - share onboard memory 75% RU and 25% CU (instead of 50/50) - * - * Changes made for release in 2.1.15: - * ----------------------------------- - * - change the detection code for multi manufacturer code support - * - * Changes made for release in 2.1.17: - * ----------------------------------- - * - update to wireless extensions changes - * - silly bug in card initial configuration (psa_conf_status) - * - * Changes made for release in 2.1.27 & 2.0.30: - * -------------------------------------------- - * - small bug in debug code (probably not the last one...) - * - remove extern keyword for wavelan_probe() - * - level threshold is now a standard wireless extension (version 4 !) - * - modules parameters types (new module interface) - * - * Changes made for release in 2.1.36: - * ----------------------------------- - * - byte count stats (courtesy of David Hinds) - * - remove dev_tint stuff (courtesy of David Hinds) - * - encryption setting from Brent Elphick (thanks a lot!) - * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) - * - * Other changes (not by me) : - * ------------------------- - * - Spelling and gramar "rectification". - * - * Changes made for release in 2.0.37 & 2.2.2 : - * ------------------------------------------ - * - Correct status in /proc/net/wireless - * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray) - * - Module init code don't fail if we found at least one card in - * the address list (Karlis Peisenieks) - * - Missing parenthesis (Christopher Peterson) - * - Correct i82586 configuration parameters - * - Encryption initialisation bug (Robert McCormack) - * - New mac addresses detected in the probe - * - Increase watchdog for busy environments - * - * Changes made for release in 2.0.38 & 2.2.7 : - * ------------------------------------------ - * - Correct the reception logic to better report errors and avoid - * sending bogus packet up the stack - * - Delay RU config to avoid corrupting first received packet - * - Change config completion code (to actually check something) - * - Avoid reading out of bound in skbuf to transmit - * - Rectify a lot of (useless) debugging code - * - Change the way to `#ifdef SET_PSA_CRC' - * - * Changes made for release in 2.2.11 & 2.3.13 : - * ------------------------------------------- - * - Change e-mail and web page addresses - * - Watchdog timer is now correctly expressed in HZ, not in jiffies - * - Add channel number to the list of frequencies in range - * - Add the (short) list of bit-rates in range - * - Developp a new sensitivity... (sens.value & sens.fixed) - * - * Changes made for release in 2.2.14 & 2.3.23 : - * ------------------------------------------- - * - Fix check for root permission (break instead of exit) - * - New nwid & encoding setting (Wireless Extension 9) - * - * Changes made for release in 2.3.49 : - * ---------------------------------- - * - Indentation reformating (Alan) - * - Update to new network API (softnet - 2.3.43) : - * o replace dev->tbusy (Alan) - * o replace dev->tstart (Alan) - * o remove dev->interrupt (Alan) - * o add SMP locking via spinlock in splxx (me) - * o add spinlock in interrupt handler (me) - * o use kernel watchdog instead of ours (me) - * o increase watchdog timeout (kernel is more sensitive) (me) - * o verify that all the changes make sense and work (me) - * - Fixup a potential gotcha when reconfiguring and thighten a bit - * the interactions with Tx queue. - * - * Changes made for release in 2.4.0 : - * --------------------------------- - * - Fix spinlock stupid bugs that I left in. The driver is now SMP - * compliant and doesn't lockup at startup. - * - * Changes made for release in 2.5.2 : - * --------------------------------- - * - Use new driver API for Wireless Extensions : - * o got rid of wavelan_ioctl() - * o use a bunch of iw_handler instead - * - * Changes made for release in 2.5.35 : - * ---------------------------------- - * - Set dev->trans_start to avoid filling the logs - * - Handle better spurious/bogus interrupt - * - Avoid deadlocks in mmc_out()/mmc_in() - * - * Wishes & dreams: - * ---------------- - * - roaming (see Pcmcia driver) - */ - -/***************************** INCLUDES *****************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* Wireless extensions */ -#include /* Wireless handlers */ - -/* WaveLAN declarations */ -#include "i82586.h" -#include "wavelan.h" - -/************************** DRIVER OPTIONS **************************/ -/* - * `#define' or `#undef' the following constant to change the behaviour - * of the driver... - */ -#undef SET_PSA_CRC /* Calculate and set the CRC on PSA (slower) */ -#define USE_PSA_CONFIG /* Use info from the PSA. */ -#undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */ -#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical). */ -#undef SET_MAC_ADDRESS /* Experimental */ - -/* Warning: this stuff will slow down the driver. */ -#define WIRELESS_SPY /* Enable spying addresses. */ -#undef HISTOGRAM /* Enable histogram of signal level. */ - -/****************************** DEBUG ******************************/ - -#undef DEBUG_MODULE_TRACE /* module insertion/removal */ -#undef DEBUG_CALLBACK_TRACE /* calls made by Linux */ -#undef DEBUG_INTERRUPT_TRACE /* calls to handler */ -#undef DEBUG_INTERRUPT_INFO /* type of interrupt and so on */ -#define DEBUG_INTERRUPT_ERROR /* problems */ -#undef DEBUG_CONFIG_TRACE /* Trace the config functions. */ -#undef DEBUG_CONFIG_INFO /* what's going on */ -#define DEBUG_CONFIG_ERROR /* errors on configuration */ -#undef DEBUG_TX_TRACE /* transmission calls */ -#undef DEBUG_TX_INFO /* header of the transmitted packet */ -#undef DEBUG_TX_FAIL /* Normal failure conditions */ -#define DEBUG_TX_ERROR /* Unexpected conditions */ -#undef DEBUG_RX_TRACE /* transmission calls */ -#undef DEBUG_RX_INFO /* header of the received packet */ -#undef DEBUG_RX_FAIL /* Normal failure conditions */ -#define DEBUG_RX_ERROR /* Unexpected conditions */ - -#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */ -#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */ -#undef DEBUG_IOCTL_INFO /* various debugging info */ -#define DEBUG_IOCTL_ERROR /* what's going wrong */ -#define DEBUG_BASIC_SHOW /* Show basic startup info. */ -#undef DEBUG_VERSION_SHOW /* Print version info. */ -#undef DEBUG_PSA_SHOW /* Dump PSA to screen. */ -#undef DEBUG_MMC_SHOW /* Dump mmc to screen. */ -#undef DEBUG_SHOW_UNUSED /* Show unused fields too. */ -#undef DEBUG_I82586_SHOW /* Show i82586 status. */ -#undef DEBUG_DEVICE_SHOW /* Show device parameters. */ - -/************************ CONSTANTS & MACROS ************************/ - -#ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v24 (SMP + wireless extensions) 11/12/01\n"; -#endif - -/* Watchdog temporisation */ -#define WATCHDOG_JIFFIES (512*HZ/100) - -/* ------------------------ PRIVATE IOCTL ------------------------ */ - -#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ -#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ - -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 2 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 3 /* Get histogram values */ - -/****************************** TYPES ******************************/ - -/* Shortcuts */ -typedef struct iw_statistics iw_stats; -typedef struct iw_quality iw_qual; -typedef struct iw_freq iw_freq;typedef struct net_local net_local; -typedef struct timer_list timer_list; - -/* Basic types */ -typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ - -/* - * Static specific data for the interface. - * - * For each network interface, Linux keeps data in two structures: "device" - * keeps the generic data (same format for everybody) and "net_local" keeps - * additional specific data. - */ -struct net_local -{ - net_local * next; /* linked list of the devices */ - struct net_device * dev; /* reverse link */ - spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - int nresets; /* number of hardware resets */ - u_char reconfig_82586; /* We need to reconfigure the controller. */ - u_char promiscuous; /* promiscuous mode */ - int mc_count; /* number of multicast addresses */ - u_short hacr; /* current host interface state */ - - int tx_n_in_use; - u_short rx_head; - u_short rx_last; - u_short tx_first_free; - u_short tx_first_in_use; - - iw_stats wstats; /* Wireless-specific statistics */ - - struct iw_spy_data spy_data; - struct iw_public_data wireless_data; - -#ifdef HISTOGRAM - int his_number; /* number of intervals */ - u_char his_range[16]; /* boundaries of interval ]n-1; n] */ - u_long his_sum[16]; /* sum in interval */ -#endif /* HISTOGRAM */ -}; - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- MISC. SUBROUTINES ------------------------ */ -static u_char - wv_irq_to_psa(int); -static int - wv_psa_to_irq(u_char); -/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ -static inline u_short /* data */ - hasr_read(u_long); /* Read the host interface: base address */ -static inline void - hacr_write(u_long, /* Write to host interface: base address */ - u_short), /* data */ - hacr_write_slow(u_long, - u_short), - set_chan_attn(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_hacr_reset(u_long), /* ioaddr */ - wv_16_off(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_16_on(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_ints_off(struct net_device *), - wv_ints_on(struct net_device *); -/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ -static void - psa_read(u_long, /* Read the Parameter Storage Area. */ - u_short, /* hacr */ - int, /* offset in PSA */ - u_char *, /* buffer to fill */ - int), /* size to read */ - psa_write(u_long, /* Write to the PSA. */ - u_short, /* hacr */ - int, /* offset in PSA */ - u_char *, /* buffer in memory */ - int); /* length of buffer */ -static inline void - mmc_out(u_long, /* Write 1 byte to the Modem Manag Control. */ - u_short, - u_char), - mmc_write(u_long, /* Write n bytes to the MMC. */ - u_char, - u_char *, - int); -static inline u_char /* Read 1 byte from the MMC. */ - mmc_in(u_long, - u_short); -static inline void - mmc_read(u_long, /* Read n bytes from the MMC. */ - u_char, - u_char *, - int), - fee_wait(u_long, /* Wait for frequency EEPROM: base address */ - int, /* base delay to wait for */ - int); /* time to wait */ -static void - fee_read(u_long, /* Read the frequency EEPROM: base address */ - u_short, /* destination offset */ - u_short *, /* data buffer */ - int); /* number of registers */ -/* ---------------------- I82586 SUBROUTINES ----------------------- */ -static /*inline*/ void - obram_read(u_long, /* ioaddr */ - u_short, /* o */ - u_char *, /* b */ - int); /* n */ -static inline void - obram_write(u_long, /* ioaddr */ - u_short, /* o */ - u_char *, /* b */ - int); /* n */ -static void - wv_ack(struct net_device *); -static inline int - wv_synchronous_cmd(struct net_device *, - const char *), - wv_config_complete(struct net_device *, - u_long, - net_local *); -static int - wv_complete(struct net_device *, - u_long, - net_local *); -static inline void - wv_82586_reconfig(struct net_device *); -/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ -#ifdef DEBUG_I82586_SHOW -static void - wv_scb_show(unsigned short); -#endif -static inline void - wv_init_info(struct net_device *); /* display startup info */ -/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ -static iw_stats * - wavelan_get_wireless_stats(struct net_device *); -static void - wavelan_set_multicast_list(struct net_device *); -/* ----------------------- PACKET RECEPTION ----------------------- */ -static inline void - wv_packet_read(struct net_device *, /* Read a packet from a frame. */ - u_short, - int), - wv_receive(struct net_device *); /* Read all packets waiting. */ -/* --------------------- PACKET TRANSMISSION --------------------- */ -static inline int - wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer. */ - void *, - short); -static netdev_tx_t - wavelan_packet_xmit(struct sk_buff *, /* Send a packet. */ - struct net_device *); -/* -------------------- HARDWARE CONFIGURATION -------------------- */ -static inline int - wv_mmc_init(struct net_device *), /* Initialize the modem. */ - wv_ru_start(struct net_device *), /* Start the i82586 receiver unit. */ - wv_cu_start(struct net_device *), /* Start the i82586 command unit. */ - wv_82586_start(struct net_device *); /* Start the i82586. */ -static void - wv_82586_config(struct net_device *); /* Configure the i82586. */ -static inline void - wv_82586_stop(struct net_device *); -static int - wv_hw_reset(struct net_device *), /* Reset the WaveLAN hardware. */ - wv_check_ioaddr(u_long, /* ioaddr */ - u_char *); /* mac address (read) */ -/* ---------------------- INTERRUPT HANDLING ---------------------- */ -static irqreturn_t - wavelan_interrupt(int, /* interrupt handler */ - void *); -static void - wavelan_watchdog(struct net_device *); /* transmission watchdog */ -/* ------------------- CONFIGURATION CALLBACKS ------------------- */ -static int - wavelan_open(struct net_device *), /* Open the device. */ - wavelan_close(struct net_device *), /* Close the device. */ - wavelan_config(struct net_device *, unsigned short);/* Configure one device. */ -extern struct net_device *wavelan_probe(int unit); /* See Space.c. */ - -/**************************** VARIABLES ****************************/ - -/* - * This is the root of the linked list of WaveLAN drivers - * It is use to verify that we don't reuse the same base address - * for two different drivers and to clean up when removing the module. - */ -static net_local * wavelan_list = (net_local *) NULL; - -/* - * This table is used to translate the PSA value to IRQ number - * and vice versa. - */ -static u_char irqvals[] = -{ - 0, 0, 0, 0x01, - 0x02, 0x04, 0, 0x08, - 0, 0, 0x10, 0x20, - 0x40, 0, 0, 0x80, -}; - -/* - * Table of the available I/O addresses (base addresses) for WaveLAN - */ -static unsigned short iobase[] = -{ -#if 0 - /* Leave out 0x3C0 for now -- seems to clash with some video - * controllers. - * Leave out the others too -- we will always use 0x390 and leave - * 0x300 for the Ethernet device. - * Jean II: 0x3E0 is fine as well. - */ - 0x300, 0x390, 0x3E0, 0x3C0 -#endif /* 0 */ - 0x390, 0x3E0 -}; - -#ifdef MODULE -/* Parameters set by insmod */ -static int io[4]; -static int irq[4]; -static char *name[4]; -module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param_array(name, charp, NULL, 0); - -MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required"); -MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)"); -MODULE_PARM_DESC(name, "WaveLAN interface neme(s)"); -#endif /* MODULE */ - -#endif /* WAVELAN_P_H */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c deleted file mode 100644 index 431a20ec6db6..000000000000 --- a/drivers/net/wireless/wavelan_cs.c +++ /dev/null @@ -1,4635 +0,0 @@ -/* - * Wavelan Pcmcia driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * Original copyright follow. See wavelan_cs.p.h for details. - * - * This code is derived from Anthony D. Joseph's code and all the changes here - * are also under the original copyright below. - * - * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and - * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services - * - * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added - * critical code in the routine to initialize the Modem Management Controller. - * - * Thanks to Alan Cox and Bruce Janson for their advice. - * - * -- Yunzhou Li (scip4166@nus.sg) - * -#ifdef WAVELAN_ROAMING - * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu) - * based on patch by Joe Finney from Lancaster University. -#endif - * - * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An - * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor. - * - * A non-shared memory PCMCIA ethernet driver for linux - * - * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu) - * - * - * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu) - * - * Apr 2 '98 made changes to bring the i82593 control/int handling in line - * with offical specs... - * - **************************************************************************** - * Copyright 1995 - * Anthony D. Joseph - * Massachusetts Institute of Technology - * - * Permission to use, copy, modify, and distribute this program - * for any purpose and without fee is hereby granted, provided - * that this copyright and permission notice appear on all copies - * and supporting documentation, the name of M.I.T. not be used - * in advertising or publicity pertaining to distribution of the - * program without specific prior permission, and notice be given - * in supporting documentation that copying and distribution is - * by permission of M.I.T. M.I.T. makes no representations about - * the suitability of this software for any purpose. It is pro- - * vided "as is" without express or implied warranty. - **************************************************************************** - * - */ - -/* Do *NOT* add other headers here, you are guaranteed to be wrong - Jean II */ -#include "wavelan_cs.p.h" /* Private header */ - -#ifdef WAVELAN_ROAMING -static void wl_cell_expiry(unsigned long data); -static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp); -static void wv_nwid_filter(unsigned char mode, net_local *lp); -#endif /* WAVELAN_ROAMING */ - -/************************* MISC SUBROUTINES **************************/ -/* - * Subroutines which won't fit in one of the following category - * (wavelan modem or i82593) - */ - -/******************* MODEM MANAGEMENT SUBROUTINES *******************/ -/* - * Useful subroutines to manage the modem of the wavelan - */ - -/*------------------------------------------------------------------*/ -/* - * Read from card's Host Adaptor Status Register. - */ -static inline u_char -hasr_read(u_long base) -{ - return(inb(HASR(base))); -} /* hasr_read */ - -/*------------------------------------------------------------------*/ -/* - * Write to card's Host Adapter Command Register. - */ -static inline void -hacr_write(u_long base, - u_char hacr) -{ - outb(hacr, HACR(base)); -} /* hacr_write */ - -/*------------------------------------------------------------------*/ -/* - * Write to card's Host Adapter Command Register. Include a delay for - * those times when it is needed. - */ -static void -hacr_write_slow(u_long base, - u_char hacr) -{ - hacr_write(base, hacr); - /* delay might only be needed sometimes */ - mdelay(1); -} /* hacr_write_slow */ - -/*------------------------------------------------------------------*/ -/* - * Read the Parameter Storage Area from the WaveLAN card's memory - */ -static void -psa_read(struct net_device * dev, - int o, /* offset in PSA */ - u_char * b, /* buffer to fill */ - int n) /* size to read */ -{ - net_local *lp = netdev_priv(dev); - u_char __iomem *ptr = lp->mem + PSA_ADDR + (o << 1); - - while(n-- > 0) - { - *b++ = readb(ptr); - /* Due to a lack of address decode pins, the WaveLAN PCMCIA card - * only supports reading even memory addresses. That means the - * increment here MUST be two. - * Because of that, we can't use memcpy_fromio()... - */ - ptr += 2; - } -} /* psa_read */ - -/*------------------------------------------------------------------*/ -/* - * Write the Parameter Storage Area to the WaveLAN card's memory - */ -static void -psa_write(struct net_device * dev, - int o, /* Offset in psa */ - u_char * b, /* Buffer in memory */ - int n) /* Length of buffer */ -{ - net_local *lp = netdev_priv(dev); - u_char __iomem *ptr = lp->mem + PSA_ADDR + (o << 1); - int count = 0; - unsigned int base = dev->base_addr; - /* As there seem to have no flag PSA_BUSY as in the ISA model, we are - * oblige to verify this address to know when the PSA is ready... */ - volatile u_char __iomem *verify = lp->mem + PSA_ADDR + - (psaoff(0, psa_comp_number) << 1); - - /* Authorize writing to PSA */ - hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN); - - while(n-- > 0) - { - /* write to PSA */ - writeb(*b++, ptr); - ptr += 2; - - /* I don't have the spec, so I don't know what the correct - * sequence to write is. This hack seem to work for me... */ - count = 0; - while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100)) - mdelay(1); - } - - /* Put the host interface back in standard state */ - hacr_write(base, HACR_DEFAULT); -} /* psa_write */ - -#ifdef SET_PSA_CRC -/*------------------------------------------------------------------*/ -/* - * Calculate the PSA CRC - * Thanks to Valster, Nico for the code - * NOTE: By specifying a length including the CRC position the - * returned value should be zero. (i.e. a correct checksum in the PSA) - * - * The Windows drivers don't use the CRC, but the AP and the PtP tool - * depend on it. - */ -static u_short -psa_crc(unsigned char * psa, /* The PSA */ - int size) /* Number of short for CRC */ -{ - int byte_cnt; /* Loop on the PSA */ - u_short crc_bytes = 0; /* Data in the PSA */ - int bit_cnt; /* Loop on the bits of the short */ - - for(byte_cnt = 0; byte_cnt < size; byte_cnt++ ) - { - crc_bytes ^= psa[byte_cnt]; /* Its an xor */ - - for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ ) - { - if(crc_bytes & 0x0001) - crc_bytes = (crc_bytes >> 1) ^ 0xA001; - else - crc_bytes >>= 1 ; - } - } - - return crc_bytes; -} /* psa_crc */ -#endif /* SET_PSA_CRC */ - -/*------------------------------------------------------------------*/ -/* - * update the checksum field in the Wavelan's PSA - */ -static void -update_psa_checksum(struct net_device * dev) -{ -#ifdef SET_PSA_CRC - psa_t psa; - u_short crc; - - /* read the parameter storage area */ - psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); - - /* update the checksum */ - crc = psa_crc((unsigned char *) &psa, - sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1]) - - sizeof(psa.psa_crc_status)); - - psa.psa_crc[0] = crc & 0xFF; - psa.psa_crc[1] = (crc & 0xFF00) >> 8; - - /* Write it ! */ - psa_write(dev, (char *)&psa.psa_crc - (char *)&psa, - (unsigned char *)&psa.psa_crc, 2); - -#ifdef DEBUG_IOCTL_INFO - printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", - dev->name, psa.psa_crc[0], psa.psa_crc[1]); - - /* Check again (luxury !) */ - crc = psa_crc((unsigned char *) &psa, - sizeof(psa) - sizeof(psa.psa_crc_status)); - - if(crc != 0) - printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name); -#endif /* DEBUG_IOCTL_INFO */ -#endif /* SET_PSA_CRC */ -} /* update_psa_checksum */ - -/*------------------------------------------------------------------*/ -/* - * Write 1 byte to the MMC. - */ -static void -mmc_out(u_long base, - u_short o, - u_char d) -{ - int count = 0; - - /* Wait for MMC to go idle */ - while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) - udelay(10); - - outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base)); - outb(d, MMD(base)); -} - -/*------------------------------------------------------------------*/ -/* - * Routine to write bytes to the Modem Management Controller. - * We start by the end because it is the way it should be ! - */ -static void -mmc_write(u_long base, - u_char o, - u_char * b, - int n) -{ - o += n; - b += n; - - while(n-- > 0 ) - mmc_out(base, --o, *(--b)); -} /* mmc_write */ - -/*------------------------------------------------------------------*/ -/* - * Read 1 byte from the MMC. - * Optimised version for 1 byte, avoid using memory... - */ -static u_char -mmc_in(u_long base, - u_short o) -{ - int count = 0; - - while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) - udelay(10); - outb(o << 1, MMR(base)); /* Set the read address */ - - outb(0, MMD(base)); /* Required dummy write */ - - while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) - udelay(10); - return (u_char) (inb(MMD(base))); /* Now do the actual read */ -} - -/*------------------------------------------------------------------*/ -/* - * Routine to read bytes from the Modem Management Controller. - * The implementation is complicated by a lack of address lines, - * which prevents decoding of the low-order bit. - * (code has just been moved in the above function) - * We start by the end because it is the way it should be ! - */ -static void -mmc_read(u_long base, - u_char o, - u_char * b, - int n) -{ - o += n; - b += n; - - while(n-- > 0) - *(--b) = mmc_in(base, --o); -} /* mmc_read */ - -/*------------------------------------------------------------------*/ -/* - * Get the type of encryption available... - */ -static inline int -mmc_encr(u_long base) /* i/o port of the card */ -{ - int temp; - - temp = mmc_in(base, mmroff(0, mmr_des_avail)); - if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) - return 0; - else - return temp; -} - -/*------------------------------------------------------------------*/ -/* - * Wait for the frequency EEprom to complete a command... - */ -static void -fee_wait(u_long base, /* i/o port of the card */ - int delay, /* Base delay to wait for */ - int number) /* Number of time to wait */ -{ - int count = 0; /* Wait only a limited time */ - - while((count++ < number) && - (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY)) - udelay(delay); -} - -/*------------------------------------------------------------------*/ -/* - * Read bytes from the Frequency EEprom (frequency select cards). - */ -static void -fee_read(u_long base, /* i/o port of the card */ - u_short o, /* destination offset */ - u_short * b, /* data buffer */ - int n) /* number of registers */ -{ - b += n; /* Position at the end of the area */ - - /* Write the address */ - mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while(n-- > 0) - { - /* Write the read command */ - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ); - - /* Wait until EEprom is ready (should be quick !) */ - fee_wait(base, 10, 100); - - /* Read the value */ - *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) | - mmc_in(base, mmroff(0, mmr_fee_data_l))); - } -} - - -/*------------------------------------------------------------------*/ -/* - * Write bytes from the Frequency EEprom (frequency select cards). - * This is a bit complicated, because the frequency eeprom has to - * be unprotected and the write enabled. - * Jean II - */ -static void -fee_write(u_long base, /* i/o port of the card */ - u_short o, /* destination offset */ - u_short * b, /* data buffer */ - int n) /* number of registers */ -{ - b += n; /* Position at the end of the area */ - -#ifdef EEPROM_IS_PROTECTED /* disabled */ -#ifdef DOESNT_SEEM_TO_WORK /* disabled */ - /* Ask to read the protected register */ - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); - - fee_wait(base, 10, 100); - - /* Read the protected register */ - printk("Protected 2 : %02X-%02X\n", - mmc_in(base, mmroff(0, mmr_fee_data_h)), - mmc_in(base, mmroff(0, mmr_fee_data_l))); -#endif /* DOESNT_SEEM_TO_WORK */ - - /* Enable protected register */ - mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); - - fee_wait(base, 10, 100); - - /* Unprotect area */ - mmc_out(base, mmwoff(0, mmw_fee_addr), o + n); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); -#ifdef DOESNT_SEEM_TO_WORK /* disabled */ - /* Or use : */ - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); -#endif /* DOESNT_SEEM_TO_WORK */ - - fee_wait(base, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ - - /* Write enable */ - mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); - - fee_wait(base, 10, 100); - - /* Write the EEprom address */ - mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while(n-- > 0) - { - /* Write the value */ - mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); - mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF); - - /* Write the write command */ - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE); - - /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */ - mdelay(10); - fee_wait(base, 10, 100); - } - - /* Write disable */ - mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); - - fee_wait(base, 10, 100); - -#ifdef EEPROM_IS_PROTECTED /* disabled */ - /* Reprotect EEprom */ - mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); - - fee_wait(base, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ -} - -/******************* WaveLAN Roaming routines... ********************/ - -#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */ - -static unsigned char WAVELAN_BEACON_ADDRESS[] = {0x09,0x00,0x0e,0x20,0x03,0x00}; - -static void wv_roam_init(struct net_device *dev) -{ - net_local *lp= netdev_priv(dev); - - /* Do not remove this unless you have a good reason */ - printk(KERN_NOTICE "%s: Warning, you have enabled roaming on" - " device %s !\n", dev->name, dev->name); - printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature" - " of the Wavelan driver.\n"); - printk(KERN_NOTICE "It may work, but may also make the driver behave in" - " erratic ways or crash.\n"); - - lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */ - lp->wavepoint_table.num_wavepoints=0; - lp->wavepoint_table.locked=0; - lp->curr_point=NULL; /* No default WavePoint */ - lp->cell_search=0; - - lp->cell_timer.data=(long)lp; /* Start cell expiry timer */ - lp->cell_timer.function=wl_cell_expiry; - lp->cell_timer.expires=jiffies+CELL_TIMEOUT; - add_timer(&lp->cell_timer); - - wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */ - /* to build up a good WavePoint */ - /* table... */ - printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name); -} - -static void wv_roam_cleanup(struct net_device *dev) -{ - wavepoint_history *ptr,*old_ptr; - net_local *lp= netdev_priv(dev); - - printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name); - - /* Fixme : maybe we should check that the timer exist before deleting it */ - del_timer(&lp->cell_timer); /* Remove cell expiry timer */ - ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */ - while(ptr!=NULL) - { - old_ptr=ptr; - ptr=ptr->next; - wl_del_wavepoint(old_ptr,lp); - } -} - -/* Enable/Disable NWID promiscuous mode on a given device */ -static void wv_nwid_filter(unsigned char mode, net_local *lp) -{ - mm_t m; - unsigned long flags; - -#ifdef WAVELAN_ROAMING_DEBUG - printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name); -#endif - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&lp->spinlock, flags); - - m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00; - mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1); - - if(mode==NWID_PROMISC) - lp->cell_search=1; - else - lp->cell_search=0; - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&lp->spinlock, flags); -} - -/* Find a record in the WavePoint table matching a given NWID */ -static wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp) -{ - wavepoint_history *ptr=lp->wavepoint_table.head; - - while(ptr!=NULL){ - if(ptr->nwid==nwid) - return ptr; - ptr=ptr->next; - } - return NULL; -} - -/* Create a new wavepoint table entry */ -static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp) -{ - wavepoint_history *new_wavepoint; - -#ifdef WAVELAN_ROAMING_DEBUG - printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); -#endif - - if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS) - return NULL; - - new_wavepoint = kmalloc(sizeof(wavepoint_history),GFP_ATOMIC); - if(new_wavepoint==NULL) - return NULL; - - new_wavepoint->nwid=nwid; /* New WavePoints NWID */ - new_wavepoint->average_fast=0; /* Running Averages..*/ - new_wavepoint->average_slow=0; - new_wavepoint->qualptr=0; /* Start of ringbuffer */ - new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */ - memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */ - - new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */ - new_wavepoint->prev=NULL; - - if(lp->wavepoint_table.head!=NULL) - lp->wavepoint_table.head->prev=new_wavepoint; - - lp->wavepoint_table.head=new_wavepoint; - - lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */ - - return new_wavepoint; -} - -/* Remove a wavepoint entry from WavePoint table */ -static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp) -{ - if(wavepoint==NULL) - return; - - if(lp->curr_point==wavepoint) - lp->curr_point=NULL; - - if(wavepoint->prev!=NULL) - wavepoint->prev->next=wavepoint->next; - - if(wavepoint->next!=NULL) - wavepoint->next->prev=wavepoint->prev; - - if(lp->wavepoint_table.head==wavepoint) - lp->wavepoint_table.head=wavepoint->next; - - lp->wavepoint_table.num_wavepoints--; - kfree(wavepoint); -} - -/* Timer callback function - checks WavePoint table for stale entries */ -static void wl_cell_expiry(unsigned long data) -{ - net_local *lp=(net_local *)data; - wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point; - -#if WAVELAN_ROAMING_DEBUG > 1 - printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name); -#endif - - if(lp->wavepoint_table.locked) - { -#if WAVELAN_ROAMING_DEBUG > 1 - printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n"); -#endif - - lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */ - add_timer(&lp->cell_timer); - return; - } - - while(wavepoint!=NULL) - { - if(time_after(jiffies, wavepoint->last_seen + CELL_TIMEOUT)) - { -#ifdef WAVELAN_ROAMING_DEBUG - printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid); -#endif - - old_point=wavepoint; - wavepoint=wavepoint->next; - wl_del_wavepoint(old_point,lp); - } - else - wavepoint=wavepoint->next; - } - lp->cell_timer.expires=jiffies+CELL_TIMEOUT; - add_timer(&lp->cell_timer); -} - -/* Update SNR history of a wavepoint */ -static void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq) -{ - int i=0,num_missed=0,ptr=0; - int average_fast=0,average_slow=0; - - num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed - any beacons? */ - if(num_missed) - for(i=0;isigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */ - wavepoint->qualptr %=WAVEPOINT_HISTORY; /* in the ringbuffer. */ - } - wavepoint->last_seen=jiffies; /* Add beacon to history */ - wavepoint->last_seq=seq; - wavepoint->sigqual[wavepoint->qualptr++]=sigqual; - wavepoint->qualptr %=WAVEPOINT_HISTORY; - ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY; - - for(i=0;isigqual[ptr++]; - ptr %=WAVEPOINT_HISTORY; - } - - average_slow=average_fast; - for(i=WAVEPOINT_FAST_HISTORY;isigqual[ptr++]; - ptr %=WAVEPOINT_HISTORY; - } - - wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY; - wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY; -} - -/* Perform a handover to a new WavePoint */ -static void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp) -{ - unsigned int base = lp->dev->base_addr; - mm_t m; - unsigned long flags; - - if(wavepoint==lp->curr_point) /* Sanity check... */ - { - wv_nwid_filter(!NWID_PROMISC,lp); - return; - } - -#ifdef WAVELAN_ROAMING_DEBUG - printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name); -#endif - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&lp->spinlock, flags); - - m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF; - m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8; - - mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - wv_nwid_filter(!NWID_PROMISC,lp); - lp->curr_point=wavepoint; -} - -/* Called when a WavePoint beacon is received */ -static void wl_roam_gather(struct net_device * dev, - u_char * hdr, /* Beacon header */ - u_char * stats) /* SNR, Signal quality - of packet */ -{ - wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */ - unsigned short nwid=ntohs(beacon->nwid); - unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */ - wavepoint_history *wavepoint=NULL; /* WavePoint table entry */ - net_local *lp = netdev_priv(dev); /* Device info */ - -#ifdef I_NEED_THIS_FEATURE - /* Some people don't need this, some other may need it */ - nwid=nwid^ntohs(beacon->domain_id); -#endif - -#if WAVELAN_ROAMING_DEBUG > 1 - printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name); - printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual); -#endif - - lp->wavepoint_table.locked=1; /* */ - - wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */ - if(wavepoint==NULL) /* If no entry, Create a new one... */ - { - wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp); - if(wavepoint==NULL) - goto out; - } - if(lp->curr_point==NULL) /* If this is the only WavePoint, */ - wv_roam_handover(wavepoint, lp); /* Jump on it! */ - - wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history - stats. */ - - if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */ - if(!lp->cell_search) /* WavePoint is getting faint, */ - wv_nwid_filter(NWID_PROMISC,lp); /* start looking for a new one */ - - if(wavepoint->average_slow > - lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA) - wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */ - - if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */ - if(lp->cell_search) /* getting better, drop out of cell search mode */ - wv_nwid_filter(!NWID_PROMISC,lp); - -out: - lp->wavepoint_table.locked=0; /* :-) */ -} - -/* Test this MAC frame a WavePoint beacon */ -static inline int WAVELAN_BEACON(unsigned char *data) -{ - wavepoint_beacon *beacon= (wavepoint_beacon *)data; - static const wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00}; - - if(memcmp(beacon,&beacon_template,9)==0) - return 1; - else - return 0; -} -#endif /* WAVELAN_ROAMING */ - -/************************ I82593 SUBROUTINES *************************/ -/* - * Useful subroutines to manage the Ethernet controller - */ - -/*------------------------------------------------------------------*/ -/* - * Routine to synchronously send a command to the i82593 chip. - * Should be called with interrupts disabled. - * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(), - * wv_82593_config() & wv_diag()) - */ -static int -wv_82593_cmd(struct net_device * dev, - char * str, - int cmd, - int result) -{ - unsigned int base = dev->base_addr; - int status; - int wait_completed; - long spin; - - /* Spin until the chip finishes executing its current command (if any) */ - spin = 1000; - do - { - /* Time calibration of the loop */ - udelay(10); - - /* Read the interrupt register */ - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); - - /* If the interrupt hasn't been posted */ - if (spin < 0) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n", - str, status); -#endif - return(FALSE); - } - - /* Issue the command to the controller */ - outb(cmd, LCCR(base)); - - /* If we don't have to check the result of the command - * Note : this mean that the irq handler will deal with that */ - if(result == SR0_NO_RESULT) - return(TRUE); - - /* We are waiting for command completion */ - wait_completed = TRUE; - - /* Busy wait while the LAN controller executes the command. */ - spin = 1000; - do - { - /* Time calibration of the loop */ - udelay(10); - - /* Read the interrupt register */ - outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); - status = inb(LCSR(base)); - - /* Check if there was an interrupt posted */ - if((status & SR0_INTERRUPT)) - { - /* Acknowledge the interrupt */ - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); - - /* Check if interrupt is a command completion */ - if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) && - ((status & SR0_BOTH_RX_TX) != 0x0) && - !(status & SR0_RECEPTION)) - { - /* Signal command completion */ - wait_completed = FALSE; - } - else - { - /* Note : Rx interrupts will be handled later, because we can - * handle multiple Rx packets at once */ -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_INFO "wv_82593_cmd: not our interrupt\n"); -#endif - } - } - } - while(wait_completed && (spin-- > 0)); - - /* If the interrupt hasn't be posted */ - if(wait_completed) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n", - str, status); -#endif - return(FALSE); - } - - /* Check the return code returned by the card (see above) against - * the expected return code provided by the caller */ - if((status & SR0_EVENT_MASK) != result) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n", - str, status); -#endif - return(FALSE); - } - - return(TRUE); -} /* wv_82593_cmd */ - -/*------------------------------------------------------------------*/ -/* - * This routine does a 593 op-code number 7, and obtains the diagnose - * status for the WaveLAN. - */ -static inline int -wv_diag(struct net_device * dev) -{ - return(wv_82593_cmd(dev, "wv_diag(): diagnose", - OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)); -} /* wv_diag */ - -/*------------------------------------------------------------------*/ -/* - * Routine to read len bytes from the i82593's ring buffer, starting at - * chip address addr. The results read from the chip are stored in buf. - * The return value is the address to use for next the call. - */ -static int -read_ringbuf(struct net_device * dev, - int addr, - char * buf, - int len) -{ - unsigned int base = dev->base_addr; - int ring_ptr = addr; - int chunk_len; - char * buf_ptr = buf; - - /* Get all the buffer */ - while(len > 0) - { - /* Position the Program I/O Register at the ring buffer pointer */ - outb(ring_ptr & 0xff, PIORL(base)); - outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base)); - - /* First, determine how much we can read without wrapping around the - ring buffer */ - if((addr + len) < (RX_BASE + RX_SIZE)) - chunk_len = len; - else - chunk_len = RX_BASE + RX_SIZE - addr; - insb(PIOP(base), buf_ptr, chunk_len); - buf_ptr += chunk_len; - len -= chunk_len; - ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE; - } - return(ring_ptr); -} /* read_ringbuf */ - -/*------------------------------------------------------------------*/ -/* - * Reconfigure the i82593, or at least ask for it... - * Because wv_82593_config use the transmission buffer, we must do it - * when we are sure that there is no transmission, so we do it now - * or in wavelan_packet_xmit() (I can't find any better place, - * wavelan_interrupt is not an option...), so you may experience - * some delay sometime... - */ -static void -wv_82593_reconfig(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - struct pcmcia_device * link = lp->link; - unsigned long flags; - - /* Arm the flag, will be cleard in wv_82593_config() */ - lp->reconfig_82593 = TRUE; - - /* Check if we can do it now ! */ - if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev))) - { - spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ - wv_82593_config(dev); - spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ - } - else - { -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG - "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n", - dev->name, dev->state, link->open); -#endif - } -} - -/********************* DEBUG & INFO SUBROUTINES *********************/ -/* - * This routines are used in the code to show debug informations. - * Most of the time, it dump the content of hardware structures... - */ - -#ifdef DEBUG_PSA_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted contents of the Parameter Storage Area. - */ -static void -wv_psa_show(psa_t * p) -{ - printk(KERN_DEBUG "##### wavelan psa contents: #####\n"); - printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", - p->psa_io_base_addr_1, - p->psa_io_base_addr_2, - p->psa_io_base_addr_3, - p->psa_io_base_addr_4); - printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", - p->psa_rem_boot_addr_1, - p->psa_rem_boot_addr_2, - p->psa_rem_boot_addr_3); - printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); - printk("psa_int_req_no: %d\n", p->psa_int_req_no); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr); - printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr); - printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel); - printk("psa_comp_number: %d, ", p->psa_comp_number); - printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); - printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", - p->psa_feature_select); - printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); - printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); - printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); - printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]); - printk("psa_nwid_select: %d\n", p->psa_nwid_select); - printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select); - printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - p->psa_encryption_key[0], - p->psa_encryption_key[1], - p->psa_encryption_key[2], - p->psa_encryption_key[3], - p->psa_encryption_key[4], - p->psa_encryption_key[5], - p->psa_encryption_key[6], - p->psa_encryption_key[7]); - printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); - printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", - p->psa_call_code[0]); - printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - p->psa_call_code[0], - p->psa_call_code[1], - p->psa_call_code[2], - p->psa_call_code[3], - p->psa_call_code[4], - p->psa_call_code[5], - p->psa_call_code[6], - p->psa_call_code[7]); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n", - p->psa_reserved[0], - p->psa_reserved[1]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); - printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); - printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); -} /* wv_psa_show */ -#endif /* DEBUG_PSA_SHOW */ - -#ifdef DEBUG_MMC_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the Modem Management Controller. - * This function need to be completed... - */ -static void -wv_mmc_show(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - mmr_t m; - - /* Basic check */ - if(hasr_read(base) & HASR_NO_CLK) - { - printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n", - dev->name); - return; - } - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Read the mmc */ - mmc_out(base, mmwoff(0, mmw_freeze), 1); - mmc_read(base, 0, (u_char *)&m, sizeof(m)); - mmc_out(base, mmwoff(0, mmw_freeze), 0); - - /* Don't forget to update statistics */ - lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; - - spin_unlock_irqrestore(&lp->spinlock, flags); - - printk(KERN_DEBUG "##### wavelan modem status registers: #####\n"); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - m.mmr_unused0[0], - m.mmr_unused0[1], - m.mmr_unused0[2], - m.mmr_unused0[3], - m.mmr_unused0[4], - m.mmr_unused0[5], - m.mmr_unused0[6], - m.mmr_unused0[7]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", - m.mmr_des_avail, m.mmr_des_status); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", - m.mmr_unused1[0], - m.mmr_unused1[1], - m.mmr_unused1[2], - m.mmr_unused1[3], - m.mmr_unused1[4]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", - m.mmr_dce_status, - (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"", - (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? - "loop test indicated," : "", - (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "", - (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? - "jabber timer expired," : ""); - printk(KERN_DEBUG "Dsp ID: %02X\n", - m.mmr_dsp_id); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", - m.mmr_unused2[0], - m.mmr_unused2[1]); -#endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", - (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, - (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); - printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", - m.mmr_thr_pre_set & MMR_THR_PRE_SET, - (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below"); - printk(KERN_DEBUG "signal_lvl: %d [%s], ", - m.mmr_signal_lvl & MMR_SIGNAL_LVL, - (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg"); - printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL, - (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update"); - printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL, - (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0"); -#ifdef DEBUG_SHOW_UNUSED - printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); -#endif /* DEBUG_SHOW_UNUSED */ -} /* wv_mmc_show */ -#endif /* DEBUG_MMC_SHOW */ - -#ifdef DEBUG_I82593_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the i82593's receive unit. - */ -static void -wv_ru_show(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - - printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n"); - printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop); - /* - * Not implemented yet... - */ - printk("\n"); -} /* wv_ru_show */ -#endif /* DEBUG_I82593_SHOW */ - -#ifdef DEBUG_DEVICE_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the WaveLAN PCMCIA device driver. - */ -static void -wv_dev_show(struct net_device * dev) -{ - printk(KERN_DEBUG "dev:"); - printk(" state=%lX,", dev->state); - printk(" trans_start=%ld,", dev->trans_start); - printk(" flags=0x%x,", dev->flags); - printk("\n"); -} /* wv_dev_show */ - -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the WaveLAN PCMCIA device driver's - * private information. - */ -static void -wv_local_show(struct net_device * dev) -{ - net_local *lp = netdev_priv(dev); - - printk(KERN_DEBUG "local:"); - /* - * Not implemented yet... - */ - printk("\n"); -} /* wv_local_show */ -#endif /* DEBUG_DEVICE_SHOW */ - -#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) -/*------------------------------------------------------------------*/ -/* - * Dump packet header (and content if necessary) on the screen - */ -static void -wv_packet_info(u_char * p, /* Packet to dump */ - int length, /* Length of the packet */ - char * msg1, /* Name of the device */ - char * msg2) /* Name of the function */ -{ - int i; - int maxi; - - printk(KERN_DEBUG "%s: %s(): dest %pM, length %d\n", - msg1, msg2, p, length); - printk(KERN_DEBUG "%s: %s(): src %pM, type 0x%02X%02X\n", - msg1, msg2, &p[6], p[12], p[13]); - -#ifdef DEBUG_PACKET_DUMP - - printk(KERN_DEBUG "data=\""); - - if((maxi = length) > DEBUG_PACKET_DUMP) - maxi = DEBUG_PACKET_DUMP; - for(i = 14; i < maxi; i++) - if(p[i] >= ' ' && p[i] <= '~') - printk(" %c", p[i]); - else - printk("%02X", p[i]); - if(maxi < length) - printk(".."); - printk("\"\n"); - printk(KERN_DEBUG "\n"); -#endif /* DEBUG_PACKET_DUMP */ -} -#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ - -/*------------------------------------------------------------------*/ -/* - * This is the information which is displayed by the driver at startup - * There is a lot of flag to configure it at your will... - */ -static void -wv_init_info(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - psa_t psa; - - /* Read the parameter storage area */ - psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); - -#ifdef DEBUG_PSA_SHOW - wv_psa_show(&psa); -#endif -#ifdef DEBUG_MMC_SHOW - wv_mmc_show(dev); -#endif -#ifdef DEBUG_I82593_SHOW - wv_ru_show(dev); -#endif - -#ifdef DEBUG_BASIC_SHOW - /* Now, let's go for the basic stuff */ - printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr %pM", - dev->name, base, dev->irq, dev->dev_addr); - - /* Print current network id */ - if(psa.psa_nwid_select) - printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]); - else - printk(", nwid off"); - - /* If 2.00 card */ - if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - { - unsigned short freq; - - /* Ask the EEprom to read the frequency from the first area */ - fee_read(base, 0x00 /* 1st area - frequency... */, - &freq, 1); - - /* Print frequency */ - printk(", 2.00, %ld", (freq >> 6) + 2400L); - - /* Hack !!! */ - if(freq & 0x20) - printk(".5"); - } - else - { - printk(", PCMCIA, "); - switch (psa.psa_subband) - { - case PSA_SUBBAND_915: - printk("915"); - break; - case PSA_SUBBAND_2425: - printk("2425"); - break; - case PSA_SUBBAND_2460: - printk("2460"); - break; - case PSA_SUBBAND_2484: - printk("2484"); - break; - case PSA_SUBBAND_2430_5: - printk("2430.5"); - break; - default: - printk("unknown"); - } - } - - printk(" MHz\n"); -#endif /* DEBUG_BASIC_SHOW */ - -#ifdef DEBUG_VERSION_SHOW - /* Print version information */ - printk(KERN_NOTICE "%s", version); -#endif -} /* wv_init_info */ - -/********************* IOCTL, STATS & RECONFIG *********************/ -/* - * We found here routines that are called by Linux on differents - * occasions after the configuration and not for transmitting data - * These may be called when the user use ifconfig, /proc/net/dev - * or wireless extensions - */ - - -/*------------------------------------------------------------------*/ -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets - * num_addrs == 0 Normal mode, clear multicast list - * num_addrs > 0 Multicast mode, receive normal and MC packets, - * and do best-effort filtering. - */ - -static void -wavelan_set_multicast_list(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name); -#endif - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", - dev->name, dev->flags, dev->mc_count); -#endif - - if(dev->flags & IFF_PROMISC) - { - /* - * Enable promiscuous mode: receive all packets. - */ - if(!lp->promiscuous) - { - lp->promiscuous = 1; - lp->allmulticast = 0; - lp->mc_count = 0; - - wv_82593_reconfig(dev); - } - } - else - /* If all multicast addresses - * or too much multicast addresses for the hardware filter */ - if((dev->flags & IFF_ALLMULTI) || - (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES)) - { - /* - * Disable promiscuous mode, but active the all multicast mode - */ - if(!lp->allmulticast) - { - lp->promiscuous = 0; - lp->allmulticast = 1; - lp->mc_count = 0; - - wv_82593_reconfig(dev); - } - } - else - /* If there is some multicast addresses to send */ - if(dev->mc_list != (struct dev_mc_list *) NULL) - { - /* - * Disable promiscuous mode, but receive all packets - * in multicast list - */ -#ifdef MULTICAST_AVOID - if(lp->promiscuous || lp->allmulticast || - (dev->mc_count != lp->mc_count)) -#endif - { - lp->promiscuous = 0; - lp->allmulticast = 0; - lp->mc_count = dev->mc_count; - - wv_82593_reconfig(dev); - } - } - else - { - /* - * Switch to normal mode: disable promiscuous mode and - * clear the multicast list. - */ - if(lp->promiscuous || lp->mc_count == 0) - { - lp->promiscuous = 0; - lp->allmulticast = 0; - lp->mc_count = 0; - - wv_82593_reconfig(dev); - } - } -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * This function doesn't exist... - * (Note : it was a nice way to test the reconfigure stuff...) - */ -#ifdef SET_MAC_ADDRESS -static int -wavelan_set_mac_address(struct net_device * dev, - void * addr) -{ - struct sockaddr * mac = addr; - - /* Copy the address */ - memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); - - /* Reconfig the beast */ - wv_82593_reconfig(dev); - - return 0; -} -#endif /* SET_MAC_ADDRESS */ - - -/*------------------------------------------------------------------*/ -/* - * Frequency setting (for hardware able of it) - * It's a bit complicated and you don't really want to look into it... - */ -static int -wv_set_frequency(u_long base, /* i/o port of the card */ - iw_freq * frequency) -{ - const int BAND_NUM = 10; /* Number of bands */ - long freq = 0L; /* offset to 2.4 GHz in .5 MHz */ -#ifdef DEBUG_IOCTL_INFO - int i; -#endif - - /* Setting by frequency */ - /* Theoritically, you may set any frequency between - * the two limits with a 0.5 MHz precision. In practice, - * I don't want you to have trouble with local - * regulations... */ - if((frequency->e == 1) && - (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8)) - { - freq = ((frequency->m / 10000) - 24000L) / 5; - } - - /* Setting by channel (same as wfreqsel) */ - /* Warning : each channel is 22MHz wide, so some of the channels - * will interfere... */ - if((frequency->e == 0) && - (frequency->m >= 0) && (frequency->m < BAND_NUM)) - { - /* Get frequency offset. */ - freq = channel_bands[frequency->m] >> 1; - } - - /* Verify if the frequency is allowed */ - if(freq != 0L) - { - u_short table[10]; /* Authorized frequency table */ - - /* Read the frequency table */ - fee_read(base, 0x71 /* frequency table */, - table, 10); - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "Frequency table :"); - for(i = 0; i < 10; i++) - { - printk(" %04X", - table[i]); - } - printk("\n"); -#endif - - /* Look in the table if the frequency is allowed */ - if(!(table[9 - ((freq - 24) / 16)] & - (1 << ((freq - 24) % 16)))) - return -EINVAL; /* not allowed */ - } - else - return -EINVAL; - - /* If we get a usable frequency */ - if(freq != 0L) - { - unsigned short area[16]; - unsigned short dac[2]; - unsigned short area_verify[16]; - unsigned short dac_verify[2]; - /* Corresponding gain (in the power adjust value table) - * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8 - * & WCIN062D.DOC, page 6.2.9 */ - unsigned short power_limit[] = { 40, 80, 120, 160, 0 }; - int power_band = 0; /* Selected band */ - unsigned short power_adjust; /* Correct value */ - - /* Search for the gain */ - power_band = 0; - while((freq > power_limit[power_band]) && - (power_limit[++power_band] != 0)) - ; - - /* Read the first area */ - fee_read(base, 0x00, - area, 16); - - /* Read the DAC */ - fee_read(base, 0x60, - dac, 2); - - /* Read the new power adjust value */ - fee_read(base, 0x6B - (power_band >> 1), - &power_adjust, 1); - if(power_band & 0x1) - power_adjust >>= 8; - else - power_adjust &= 0xFF; - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); - for(i = 0; i < 16; i++) - { - printk(" %04X", - area[i]); - } - printk("\n"); - - printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", - dac[0], dac[1]); -#endif - - /* Frequency offset (for info only...) */ - area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); - - /* Receiver Principle main divider coefficient */ - area[3] = (freq >> 1) + 2400L - 352L; - area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); - - /* Transmitter Main divider coefficient */ - area[13] = (freq >> 1) + 2400L; - area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); - - /* Others part of the area are flags, bit streams or unused... */ - - /* Set the value in the DAC */ - dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); - dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); - - /* Write the first area */ - fee_write(base, 0x00, - area, 16); - - /* Write the DAC */ - fee_write(base, 0x60, - dac, 2); - - /* We now should verify here that the EEprom writing was ok */ - - /* ReRead the first area */ - fee_read(base, 0x00, - area_verify, 16); - - /* ReRead the DAC */ - fee_read(base, 0x60, - dac_verify, 2); - - /* Compare */ - if(memcmp(area, area_verify, 16 * 2) || - memcmp(dac, dac_verify, 2 * 2)) - { -#ifdef DEBUG_IOCTL_ERROR - printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (?)\n"); -#endif - return -EOPNOTSUPP; - } - - /* We must download the frequency parameters to the - * synthetisers (from the EEprom - area 1) - * Note : as the EEprom is auto decremented, we set the end - * if the area... */ - mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait until the download is finished */ - fee_wait(base, 100, 100); - - /* We must now download the power adjust value (gain) to - * the synthetisers (from the EEprom - area 7 - DAC) */ - mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61); - mmc_out(base, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait until the download is finished */ - fee_wait(base, 100, 100); - -#ifdef DEBUG_IOCTL_INFO - /* Verification of what we have done... */ - - printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); - for(i = 0; i < 16; i++) - { - printk(" %04X", - area_verify[i]); - } - printk("\n"); - - printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", - dac_verify[0], dac_verify[1]); -#endif - - return 0; - } - else - return -EINVAL; /* Bah, never get there... */ -} - -/*------------------------------------------------------------------*/ -/* - * Give the list of available frequencies - */ -static int -wv_frequency_list(u_long base, /* i/o port of the card */ - iw_freq * list, /* List of frequency to fill */ - int max) /* Maximum number of frequencies */ -{ - u_short table[10]; /* Authorized frequency table */ - long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ - int i; /* index in the table */ - const int BAND_NUM = 10; /* Number of bands */ - int c = 0; /* Channel number */ - - /* Read the frequency table */ - fee_read(base, 0x71 /* frequency table */, - table, 10); - - /* Look all frequencies */ - i = 0; - for(freq = 0; freq < 150; freq++) - /* Look in the table if the frequency is allowed */ - if(table[9 - (freq / 16)] & (1 << (freq % 16))) - { - /* Compute approximate channel number */ - while((((channel_bands[c] >> 1) - 24) < freq) && - (c < BAND_NUM)) - c++; - list[i].i = c; /* Set the list index */ - - /* put in the list */ - list[i].m = (((freq + 24) * 5) + 24000L) * 10000; - list[i++].e = 1; - - /* Check number */ - if(i >= max) - return(i); - } - - return(i); -} - -#ifdef IW_WIRELESS_SPY -/*------------------------------------------------------------------*/ -/* - * Gather wireless spy statistics : for each packet, compare the source - * address with out list, and if match, get the stats... - * Sorry, but this function really need wireless extensions... - */ -static inline void -wl_spy_gather(struct net_device * dev, - u_char * mac, /* MAC address */ - u_char * stats) /* Statistics to gather */ -{ - struct iw_quality wstats; - - wstats.qual = stats[2] & MMR_SGNL_QUAL; - wstats.level = stats[0] & MMR_SIGNAL_LVL; - wstats.noise = stats[1] & MMR_SILENCE_LVL; - wstats.updated = 0x7; - - /* Update spy records */ - wireless_spy_update(dev, mac, &wstats); -} -#endif /* IW_WIRELESS_SPY */ - -#ifdef HISTOGRAM -/*------------------------------------------------------------------*/ -/* - * This function calculate an histogram on the signal level. - * As the noise is quite constant, it's like doing it on the SNR. - * We have defined a set of interval (lp->his_range), and each time - * the level goes in that interval, we increment the count (lp->his_sum). - * With this histogram you may detect if one wavelan is really weak, - * or you may also calculate the mean and standard deviation of the level... - */ -static inline void -wl_his_gather(struct net_device * dev, - u_char * stats) /* Statistics to gather */ -{ - net_local * lp = netdev_priv(dev); - u_char level = stats[0] & MMR_SIGNAL_LVL; - int i; - - /* Find the correct interval */ - i = 0; - while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++])) - ; - - /* Increment interval counter */ - (lp->his_sum[i])++; -} -#endif /* HISTOGRAM */ - -static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strncpy(info->driver, "wavelan_cs", sizeof(info->driver)-1); -} - -static const struct ethtool_ops ops = { - .get_drvinfo = wl_get_drvinfo -}; - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get protocol name - */ -static int wavelan_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - strcpy(wrqu->name, "WaveLAN"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set NWID - */ -static int wavelan_set_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - mm_t m; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Set NWID in WaveLAN. */ - if (!wrqu->nwid.disabled) { - /* Set NWID in psa */ - psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; - psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; - psa.psa_nwid_select = 0x01; - psa_write(dev, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - - /* Set NWID in mmc. */ - m.w.mmw_netw_id_l = psa.psa_nwid[1]; - m.w.mmw_netw_id_h = psa.psa_nwid[0]; - mmc_write(base, - (char *) &m.w.mmw_netw_id_l - - (char *) &m, - (unsigned char *) &m.w.mmw_netw_id_l, 2); - mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00); - } else { - /* Disable NWID in the psa. */ - psa.psa_nwid_select = 0x00; - psa_write(dev, - (char *) &psa.psa_nwid_select - - (char *) &psa, - (unsigned char *) &psa.psa_nwid_select, - 1); - - /* Disable NWID in the mmc (no filtering). */ - mmc_out(base, mmwoff(0, mmw_loopt_sel), - MMW_LOOPT_SEL_DIS_NWID); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get NWID - */ -static int wavelan_get_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Read the NWID. */ - psa_read(dev, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; - wrqu->nwid.disabled = !(psa.psa_nwid_select); - wrqu->nwid.fixed = 1; /* Superfluous */ - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set frequency - */ -static int wavelan_set_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - unsigned long flags; - int ret; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - ret = wv_set_frequency(base, &(wrqu->freq)); - else - ret = -EOPNOTSUPP; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get frequency - */ -static int wavelan_get_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). - * Does it work for everybody, especially old cards? */ - if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - unsigned short freq; - - /* Ask the EEPROM to read the frequency from the first area. */ - fee_read(base, 0x00, &freq, 1); - wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; - wrqu->freq.e = 1; - } else { - psa_read(dev, - (char *) &psa.psa_subband - (char *) &psa, - (unsigned char *) &psa.psa_subband, 1); - - if (psa.psa_subband <= 4) { - wrqu->freq.m = fixed_bands[psa.psa_subband]; - wrqu->freq.e = (psa.psa_subband != 0); - } else - ret = -EOPNOTSUPP; - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set level threshold - */ -static int wavelan_set_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Set the level threshold. */ - /* We should complain loudly if wrqu->sens.fixed = 0, because we - * can't set auto mode... */ - psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; - psa_write(dev, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev); - mmc_out(base, mmwoff(0, mmw_thr_pre_set), - psa.psa_thr_pre_set); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get level threshold - */ -static int wavelan_get_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Read the level threshold. */ - psa_read(dev, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; - wrqu->sens.fixed = 1; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set encryption key - */ -static int wavelan_set_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - unsigned long flags; - psa_t psa; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check if capable of encryption */ - if (!mmc_encr(base)) { - ret = -EOPNOTSUPP; - } - - /* Check the size of the key */ - if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { - ret = -EINVAL; - } - - if(!ret) { - /* Basic checking... */ - if (wrqu->encoding.length == 8) { - /* Copy the key in the driver */ - memcpy(psa.psa_encryption_key, extra, - wrqu->encoding.length); - psa.psa_encryption_select = 1; - - psa_write(dev, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 8 + 1); - - mmc_out(base, mmwoff(0, mmw_encr_enable), - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); - mmc_write(base, mmwoff(0, mmw_encr_key), - (unsigned char *) &psa. - psa_encryption_key, 8); - } - - /* disable encryption */ - if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { - psa.psa_encryption_select = 0; - psa_write(dev, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1); - - mmc_out(base, mmwoff(0, mmw_encr_enable), 0); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev); - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get encryption key - */ -static int wavelan_get_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check if encryption is available */ - if (!mmc_encr(base)) { - ret = -EOPNOTSUPP; - } else { - /* Read the encryption key */ - psa_read(dev, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1 + 8); - - /* encryption is enabled ? */ - if (psa.psa_encryption_select) - wrqu->encoding.flags = IW_ENCODE_ENABLED; - else - wrqu->encoding.flags = IW_ENCODE_DISABLED; - wrqu->encoding.flags |= mmc_encr(base); - - /* Copy the key to the user buffer */ - wrqu->encoding.length = 8; - memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -#ifdef WAVELAN_ROAMING_EXT -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set ESSID (domain) - */ -static int wavelan_set_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check if disable */ - if(wrqu->data.flags == 0) - lp->filter_domains = 0; - else { - char essid[IW_ESSID_MAX_SIZE + 1]; - char * endp; - - /* Terminate the string */ - memcpy(essid, extra, wrqu->data.length); - essid[IW_ESSID_MAX_SIZE] = '\0'; - -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "SetEssid : ``%s''\n", essid); -#endif /* DEBUG_IOCTL_INFO */ - - /* Convert to a number (note : Wavelan specific) */ - lp->domain_id = simple_strtoul(essid, &endp, 16); - /* Has it worked ? */ - if(endp > essid) - lp->filter_domains = 1; - else { - lp->filter_domains = 0; - ret = -EINVAL; - } - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get ESSID (domain) - */ -static int wavelan_get_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - - /* Is the domain ID active ? */ - wrqu->data.flags = lp->filter_domains; - - /* Copy Domain ID into a string (Wavelan specific) */ - /* Sound crazy, be we can't have a snprintf in the kernel !!! */ - sprintf(extra, "%lX", lp->domain_id); - extra[IW_ESSID_MAX_SIZE] = '\0'; - - /* Set the length */ - wrqu->data.length = strlen(extra); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set AP address - */ -static int wavelan_set_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG "Set AP to : %pM\n", wrqu->ap_addr.sa_data); -#endif /* DEBUG_IOCTL_INFO */ - - return -EOPNOTSUPP; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get AP address - */ -static int wavelan_get_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - /* Should get the real McCoy instead of own Ethernet address */ - memcpy(wrqu->ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE); - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - - return -EOPNOTSUPP; -} -#endif /* WAVELAN_ROAMING_EXT */ - -#ifdef WAVELAN_ROAMING -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set mode - */ -static int wavelan_set_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - int ret = 0; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Check mode */ - switch(wrqu->mode) { - case IW_MODE_ADHOC: - if(do_roaming) { - wv_roam_cleanup(dev); - do_roaming = 0; - } - break; - case IW_MODE_INFRA: - if(!do_roaming) { - wv_roam_init(dev); - do_roaming = 1; - } - break; - default: - ret = -EINVAL; - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get mode - */ -static int wavelan_get_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - if(do_roaming) - wrqu->mode = IW_MODE_INFRA; - else - wrqu->mode = IW_MODE_ADHOC; - - return 0; -} -#endif /* WAVELAN_ROAMING */ - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get range info - */ -static int wavelan_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - struct iw_range *range = (struct iw_range *) extra; - unsigned long flags; - int ret = 0; - - /* Set the length (very important for backward compatibility) */ - wrqu->data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(range, 0, sizeof(struct iw_range)); - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 9; - - /* Set information in the range struct. */ - range->throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */ - range->min_nwid = 0x0000; - range->max_nwid = 0xFFFF; - - range->sensitivity = 0x3F; - range->max_qual.qual = MMR_SGNL_QUAL; - range->max_qual.level = MMR_SIGNAL_LVL; - range->max_qual.noise = MMR_SILENCE_LVL; - range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range->avg_qual.level = 30; - range->avg_qual.noise = 8; - - range->num_bitrates = 1; - range->bitrate[0] = 2000000; /* 2 Mb/s */ - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | - IW_EVENT_CAPA_MASK(0x8B04) | - IW_EVENT_CAPA_MASK(0x8B06)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - range->num_channels = 10; - range->num_frequency = wv_frequency_list(base, range->freq, - IW_MAX_FREQUENCIES); - } else - range->num_channels = range->num_frequency = 0; - - /* Encryption supported ? */ - if (mmc_encr(base)) { - range->encoding_size[0] = 8; /* DES = 64 bits key */ - range->num_encoding_sizes = 1; - range->max_encoding_tokens = 1; /* Only one key possible */ - } else { - range->num_encoding_sizes = 0; - range->max_encoding_tokens = 0; - } - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set quality threshold - */ -static int wavelan_set_qthr(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned int base = dev->base_addr; - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - psa.psa_quality_thr = *(extra) & 0x0F; - psa_write(dev, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev); - mmc_out(base, mmwoff(0, mmw_quality_thr), - psa.psa_quality_thr); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get quality threshold - */ -static int wavelan_get_qthr(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - psa_t psa; - unsigned long flags; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - psa_read(dev, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - *(extra) = psa.psa_quality_thr & 0x0F; - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return 0; -} - -#ifdef WAVELAN_ROAMING -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set roaming - */ -static int wavelan_set_roam(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - unsigned long flags; - - /* Disable interrupts and save flags. */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Note : should check if user == root */ - if(do_roaming && (*extra)==0) - wv_roam_cleanup(dev); - else if(do_roaming==0 && (*extra)!=0) - wv_roam_init(dev); - - do_roaming = (*extra); - - /* Enable interrupts and restore flags. */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get quality threshold - */ -static int wavelan_get_roam(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - *(extra) = do_roaming; - - return 0; -} -#endif /* WAVELAN_ROAMING */ - -#ifdef HISTOGRAM -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set histogram - */ -static int wavelan_set_histo(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - - /* Check the number of intervals. */ - if (wrqu->data.length > 16) { - return(-E2BIG); - } - - /* Disable histo while we copy the addresses. - * As we don't disable interrupts, we need to do this */ - lp->his_number = 0; - - /* Are there ranges to copy? */ - if (wrqu->data.length > 0) { - /* Copy interval ranges to the driver */ - memcpy(lp->his_range, extra, wrqu->data.length); - - { - int i; - printk(KERN_DEBUG "Histo :"); - for(i = 0; i < wrqu->data.length; i++) - printk(" %d", lp->his_range[i]); - printk("\n"); - } - - /* Reset result structure. */ - memset(lp->his_sum, 0x00, sizeof(long) * 16); - } - - /* Now we can set the number of ranges */ - lp->his_number = wrqu->data.length; - - return(0); -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get histogram - */ -static int wavelan_get_histo(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - net_local *lp = netdev_priv(dev); - - /* Set the number of intervals. */ - wrqu->data.length = lp->his_number; - - /* Give back the distribution statistics */ - if(lp->his_number > 0) - memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); - - return(0); -} -#endif /* HISTOGRAM */ - -/*------------------------------------------------------------------*/ -/* - * Structures to export the Wireless Handlers - */ - -static const struct iw_priv_args wavelan_private_args[] = { -/*{ cmd, set_args, get_args, name } */ - { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, - { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, - { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setroam" }, - { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" }, - { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, - { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, -}; - -static const iw_handler wavelan_handler[] = -{ - NULL, /* SIOCSIWNAME */ - wavelan_get_name, /* SIOCGIWNAME */ - wavelan_set_nwid, /* SIOCSIWNWID */ - wavelan_get_nwid, /* SIOCGIWNWID */ - wavelan_set_freq, /* SIOCSIWFREQ */ - wavelan_get_freq, /* SIOCGIWFREQ */ -#ifdef WAVELAN_ROAMING - wavelan_set_mode, /* SIOCSIWMODE */ - wavelan_get_mode, /* SIOCGIWMODE */ -#else /* WAVELAN_ROAMING */ - NULL, /* SIOCSIWMODE */ - NULL, /* SIOCGIWMODE */ -#endif /* WAVELAN_ROAMING */ - wavelan_set_sens, /* SIOCSIWSENS */ - wavelan_get_sens, /* SIOCGIWSENS */ - NULL, /* SIOCSIWRANGE */ - wavelan_get_range, /* SIOCGIWRANGE */ - NULL, /* SIOCSIWPRIV */ - NULL, /* SIOCGIWPRIV */ - NULL, /* SIOCSIWSTATS */ - NULL, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ -#ifdef WAVELAN_ROAMING_EXT - wavelan_set_wap, /* SIOCSIWAP */ - wavelan_get_wap, /* SIOCGIWAP */ - NULL, /* -- hole -- */ - NULL, /* SIOCGIWAPLIST */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - wavelan_set_essid, /* SIOCSIWESSID */ - wavelan_get_essid, /* SIOCGIWESSID */ -#else /* WAVELAN_ROAMING_EXT */ - NULL, /* SIOCSIWAP */ - NULL, /* SIOCGIWAP */ - NULL, /* -- hole -- */ - NULL, /* SIOCGIWAPLIST */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWESSID */ - NULL, /* SIOCGIWESSID */ -#endif /* WAVELAN_ROAMING_EXT */ - NULL, /* SIOCSIWNICKN */ - NULL, /* SIOCGIWNICKN */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWRATE */ - NULL, /* SIOCGIWRATE */ - NULL, /* SIOCSIWRTS */ - NULL, /* SIOCGIWRTS */ - NULL, /* SIOCSIWFRAG */ - NULL, /* SIOCGIWFRAG */ - NULL, /* SIOCSIWTXPOW */ - NULL, /* SIOCGIWTXPOW */ - NULL, /* SIOCSIWRETRY */ - NULL, /* SIOCGIWRETRY */ - wavelan_set_encode, /* SIOCSIWENCODE */ - wavelan_get_encode, /* SIOCGIWENCODE */ -}; - -static const iw_handler wavelan_private_handler[] = -{ - wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ - wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ -#ifdef WAVELAN_ROAMING - wavelan_set_roam, /* SIOCIWFIRSTPRIV + 2 */ - wavelan_get_roam, /* SIOCIWFIRSTPRIV + 3 */ -#else /* WAVELAN_ROAMING */ - NULL, /* SIOCIWFIRSTPRIV + 2 */ - NULL, /* SIOCIWFIRSTPRIV + 3 */ -#endif /* WAVELAN_ROAMING */ -#ifdef HISTOGRAM - wavelan_set_histo, /* SIOCIWFIRSTPRIV + 4 */ - wavelan_get_histo, /* SIOCIWFIRSTPRIV + 5 */ -#endif /* HISTOGRAM */ -}; - -static const struct iw_handler_def wavelan_handler_def = -{ - .num_standard = ARRAY_SIZE(wavelan_handler), - .num_private = ARRAY_SIZE(wavelan_private_handler), - .num_private_args = ARRAY_SIZE(wavelan_private_args), - .standard = wavelan_handler, - .private = wavelan_private_handler, - .private_args = wavelan_private_args, - .get_wireless_stats = wavelan_get_wireless_stats, -}; - -/*------------------------------------------------------------------*/ -/* - * Get wireless statistics - * Called by /proc/net/wireless... - */ -static iw_stats * -wavelan_get_wireless_stats(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - mmr_t m; - iw_stats * wstats; - unsigned long flags; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); -#endif - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&lp->spinlock, flags); - - wstats = &lp->wstats; - - /* Get data from the mmc */ - mmc_out(base, mmwoff(0, mmw_freeze), 1); - - mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); - mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2); - mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4); - - mmc_out(base, mmwoff(0, mmw_freeze), 0); - - /* Copy data to wireless stuff */ - wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; - wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; - wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; - wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; - wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) | - ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) | - ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); - wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; - wstats->discard.code = 0L; - wstats->discard.misc = 0L; - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name); -#endif - return &lp->wstats; -} - -/************************* PACKET RECEPTION *************************/ -/* - * This part deal with receiving the packets. - * The interrupt handler get an interrupt when a packet has been - * successfully received and called this part... - */ - -/*------------------------------------------------------------------*/ -/* - * Calculate the starting address of the frame pointed to by the receive - * frame pointer and verify that the frame seem correct - * (called by wv_packet_rcv()) - */ -static int -wv_start_of_frame(struct net_device * dev, - int rfp, /* end of frame */ - int wrap) /* start of buffer */ -{ - unsigned int base = dev->base_addr; - int rp; - int len; - - rp = (rfp - 5 + RX_SIZE) % RX_SIZE; - outb(rp & 0xff, PIORL(base)); - outb(((rp >> 8) & PIORH_MASK), PIORH(base)); - len = inb(PIOP(base)); - len |= inb(PIOP(base)) << 8; - - /* Sanity checks on size */ - /* Frame too big */ - if(len > MAXDATAZ + 100) - { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n", - dev->name, rfp, len); -#endif - return(-1); - } - - /* Frame too short */ - if(len < 7) - { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n", - dev->name, rfp, len); -#endif - return(-1); - } - - /* Wrap around buffer */ - if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */ - { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n", - dev->name, wrap, rfp, len); -#endif - return(-1); - } - - return((rp - len + RX_SIZE) % RX_SIZE); -} /* wv_start_of_frame */ - -/*------------------------------------------------------------------*/ -/* - * This routine does the actual copy of data (including the ethernet - * header structure) from the WaveLAN card to an sk_buff chain that - * will be passed up to the network interface layer. NOTE: We - * currently don't handle trailer protocols (neither does the rest of - * the network interface), so if that is needed, it will (at least in - * part) be added here. The contents of the receive ring buffer are - * copied to a message chain that is then passed to the kernel. - * - * Note: if any errors occur, the packet is "dropped on the floor" - * (called by wv_packet_rcv()) - */ -static void -wv_packet_read(struct net_device * dev, - int fd_p, - int sksize) -{ - net_local * lp = netdev_priv(dev); - struct sk_buff * skb; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", - dev->name, fd_p, sksize); -#endif - - /* Allocate some buffer for the new packet */ - if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL) - { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n", - dev->name, sksize); -#endif - dev->stats.rx_dropped++; - /* - * Not only do we want to return here, but we also need to drop the - * packet on the floor to clear the interrupt. - */ - return; - } - - skb_reserve(skb, 2); - fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize); - skb->protocol = eth_type_trans(skb, dev); - -#ifdef DEBUG_RX_INFO - wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read"); -#endif /* DEBUG_RX_INFO */ - - /* Statistics gathering & stuff associated. - * It seem a bit messy with all the define, but it's really simple... */ - if( -#ifdef IW_WIRELESS_SPY - (lp->spy_data.spy_number > 0) || -#endif /* IW_WIRELESS_SPY */ -#ifdef HISTOGRAM - (lp->his_number > 0) || -#endif /* HISTOGRAM */ -#ifdef WAVELAN_ROAMING - (do_roaming) || -#endif /* WAVELAN_ROAMING */ - 0) - { - u_char stats[3]; /* Signal level, Noise level, Signal quality */ - - /* read signal level, silence level and signal quality bytes */ - fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE, - stats, 3); -#ifdef DEBUG_RX_INFO - printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", - dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F); -#endif - -#ifdef WAVELAN_ROAMING - if(do_roaming) - if(WAVELAN_BEACON(skb->data)) - wl_roam_gather(dev, skb->data, stats); -#endif /* WAVELAN_ROAMING */ - -#ifdef WIRELESS_SPY - wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); -#endif /* WIRELESS_SPY */ -#ifdef HISTOGRAM - wl_his_gather(dev, stats); -#endif /* HISTOGRAM */ - } - - /* - * Hand the packet to the Network Module - */ - netif_rx(skb); - - /* Keep stats up to date */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += sksize; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); -#endif - return; -} - -/*------------------------------------------------------------------*/ -/* - * This routine is called by the interrupt handler to initiate a - * packet transfer from the card to the network interface layer above - * this driver. This routine checks if a buffer has been successfully - * received by the WaveLAN card. If so, the routine wv_packet_read is - * called to do the actual transfer of the card's data including the - * ethernet header into a packet consisting of an sk_buff chain. - * (called by wavelan_interrupt()) - * Note : the spinlock is already grabbed for us and irq are disabled. - */ -static void -wv_packet_rcv(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - int newrfp; - int rp; - int len; - int f_start; - int status; - int i593_rfp; - int stat_ptr; - u_char c[4]; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name); -#endif - - /* Get the new receive frame pointer from the i82593 chip */ - outb(CR0_STATUS_2 | OP0_NOP, LCCR(base)); - i593_rfp = inb(LCSR(base)); - i593_rfp |= inb(LCSR(base)) << 8; - i593_rfp %= RX_SIZE; - - /* Get the new receive frame pointer from the WaveLAN card. - * It is 3 bytes more than the increment of the i82593 receive - * frame pointer, for each packet. This is because it includes the - * 3 roaming bytes added by the mmc. - */ - newrfp = inb(RPLL(base)); - newrfp |= inb(RPLH(base)) << 8; - newrfp %= RX_SIZE; - -#ifdef DEBUG_RX_INFO - printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n", - dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); -#endif - -#ifdef DEBUG_RX_ERROR - /* If no new frame pointer... */ - if(lp->overrunning || newrfp == lp->rfp) - printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n", - dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); -#endif - - /* Read all frames (packets) received */ - while(newrfp != lp->rfp) - { - /* A frame is composed of the packet, followed by a status word, - * the length of the frame (word) and the mmc info (SNR & qual). - * It's because the length is at the end that we can only scan - * frames backward. */ - - /* Find the first frame by skipping backwards over the frames */ - rp = newrfp; /* End of last frame */ - while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) && - (f_start != -1)) - rp = f_start; - - /* If we had a problem */ - if(f_start == -1) - { -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "wavelan_cs: cannot find start of frame "); - printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n", - i593_rfp, lp->stop, newrfp, lp->rfp); -#endif - lp->rfp = rp; /* Get to the last usable frame */ - continue; - } - - /* f_start point to the beggining of the first frame received - * and rp to the beggining of the next one */ - - /* Read status & length of the frame */ - stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE; - stat_ptr = read_ringbuf(dev, stat_ptr, c, 4); - status = c[0] | (c[1] << 8); - len = c[2] | (c[3] << 8); - - /* Check status */ - if((status & RX_RCV_OK) != RX_RCV_OK) - { - dev->stats.rx_errors++; - if(status & RX_NO_SFD) - dev->stats.rx_frame_errors++; - if(status & RX_CRC_ERR) - dev->stats.rx_crc_errors++; - if(status & RX_OVRRUN) - dev->stats.rx_over_errors++; - -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n", - dev->name, status); -#endif - } - else - /* Read the packet and transmit to Linux */ - wv_packet_read(dev, f_start, len - 2); - - /* One frame has been processed, skip it */ - lp->rfp = rp; - } - - /* - * Update the frame stop register, but set it to less than - * the full 8K to allow space for 3 bytes of signal strength - * per packet. - */ - lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; - outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); - outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); - outb(OP1_SWIT_TO_PORT_0, LCCR(base)); - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name); -#endif -} - -/*********************** PACKET TRANSMISSION ***********************/ -/* - * This part deal with sending packet through the wavelan - * We copy the packet to the send buffer and then issue the send - * command to the i82593. The result of this operation will be - * checked in wavelan_interrupt() - */ - -/*------------------------------------------------------------------*/ -/* - * This routine fills in the appropriate registers and memory - * locations on the WaveLAN card and starts the card off on - * the transmit. - * (called in wavelan_packet_xmit()) - */ -static void -wv_packet_write(struct net_device * dev, - void * buf, - short length) -{ - net_local * lp = netdev_priv(dev); - unsigned int base = dev->base_addr; - unsigned long flags; - int clen = length; - register u_short xmtdata_base = TX_BASE; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length); -#endif - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Write the length of data buffer followed by the buffer */ - outb(xmtdata_base & 0xff, PIORL(base)); - outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); - outb(clen & 0xff, PIOP(base)); /* lsb */ - outb(clen >> 8, PIOP(base)); /* msb */ - - /* Send the data */ - outsb(PIOP(base), buf, clen); - - /* Indicate end of transmit chain */ - outb(OP0_NOP, PIOP(base)); - /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */ - outb(OP0_NOP, PIOP(base)); - - /* Reset the transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - hacr_write(base, HACR_DEFAULT); - /* Send the transmit command */ - wv_82593_cmd(dev, "wv_packet_write(): transmit", - OP0_TRANSMIT, SR0_NO_RESULT); - - /* Make sure the watchdog will keep quiet for a while */ - dev->trans_start = jiffies; - - /* Keep stats up to date */ - dev->stats.tx_bytes += length; - - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_TX_INFO - wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write"); -#endif /* DEBUG_TX_INFO */ - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * This routine is called when we want to send a packet (NET3 callback) - * In this routine, we check if the harware is ready to accept - * the packet. We also prevent reentrance. Then, we call the function - * to send the packet... - */ -static netdev_tx_t -wavelan_packet_xmit(struct sk_buff * skb, - struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - unsigned long flags; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, - (unsigned) skb); -#endif - - /* - * Block a timer-based transmit from overlapping a previous transmit. - * In other words, prevent reentering this routine. - */ - netif_stop_queue(dev); - - /* If somebody has asked to reconfigure the controller, - * we can do it now */ - if(lp->reconfig_82593) - { - spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ - wv_82593_config(dev); - spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ - /* Note : the configure procedure was totally synchronous, - * so the Tx buffer is now free */ - } - - /* Check if we need some padding */ - /* Note : on wireless the propagation time is in the order of 1us, - * and we don't have the Ethernet specific requirement of beeing - * able to detect collisions, therefore in theory we don't really - * need to pad. Jean II */ - if (skb_padto(skb, ETH_ZLEN)) - return NETDEV_TX_OK; - - wv_packet_write(dev, skb->data, skb->len); - - dev_kfree_skb(skb); - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); -#endif - return NETDEV_TX_OK; -} - -/********************** HARDWARE CONFIGURATION **********************/ -/* - * This part do the real job of starting and configuring the hardware. - */ - -/*------------------------------------------------------------------*/ -/* - * Routine to initialize the Modem Management Controller. - * (called by wv_hw_config()) - */ -static int -wv_mmc_init(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - psa_t psa; - mmw_t m; - int configured; - int i; /* Loop counter */ - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); -#endif - - /* Read the parameter storage area */ - psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); - - /* - * Check the first three octets of the MAC addr for the manufacturer's code. - * Note: If you get the error message below, you've got a - * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on - * how to configure your card... - */ - for (i = 0; i < ARRAY_SIZE(MAC_ADDRESSES); i++) - if ((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) && - (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) && - (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2])) - break; - - /* If we have not found it... */ - if (i == ARRAY_SIZE(MAC_ADDRESSES)) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n", - dev->name, psa.psa_univ_mac_addr[0], - psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]); -#endif - return FALSE; - } - - /* Get the MAC address */ - memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE); - -#ifdef USE_PSA_CONFIG - configured = psa.psa_conf_status & 1; -#else - configured = 0; -#endif - - /* Is the PSA is not configured */ - if(!configured) - { - /* User will be able to configure NWID after (with iwconfig) */ - psa.psa_nwid[0] = 0; - psa.psa_nwid[1] = 0; - - /* As NWID is not set : no NWID checking */ - psa.psa_nwid_select = 0; - - /* Disable encryption */ - psa.psa_encryption_select = 0; - - /* Set to standard values - * 0x04 for AT, - * 0x01 for MCA, - * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) - */ - if (psa.psa_comp_number & 1) - psa.psa_thr_pre_set = 0x01; - else - psa.psa_thr_pre_set = 0x04; - psa.psa_quality_thr = 0x03; - - /* It is configured */ - psa.psa_conf_status |= 1; - -#ifdef USE_PSA_CONFIG - /* Write the psa */ - psa_write(dev, (char *)psa.psa_nwid - (char *)&psa, - (unsigned char *)psa.psa_nwid, 4); - psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, - (unsigned char *)&psa.psa_thr_pre_set, 1); - psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa, - (unsigned char *)&psa.psa_quality_thr, 1); - psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa, - (unsigned char *)&psa.psa_conf_status, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev); -#endif /* USE_PSA_CONFIG */ - } - - /* Zero the mmc structure */ - memset(&m, 0x00, sizeof(m)); - - /* Copy PSA info to the mmc */ - m.mmw_netw_id_l = psa.psa_nwid[1]; - m.mmw_netw_id_h = psa.psa_nwid[0]; - - if(psa.psa_nwid_select & 1) - m.mmw_loopt_sel = 0x00; - else - m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; - - memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, - sizeof(m.mmw_encr_key)); - - if(psa.psa_encryption_select) - m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; - else - m.mmw_encr_enable = 0; - - m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; - m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; - - /* - * Set default modem control parameters. - * See NCR document 407-0024326 Rev. A. - */ - m.mmw_jabber_enable = 0x01; - m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; - m.mmw_ifs = 0x20; - m.mmw_mod_delay = 0x04; - m.mmw_jam_time = 0x38; - - m.mmw_des_io_invert = 0; - m.mmw_freeze = 0; - m.mmw_decay_prm = 0; - m.mmw_decay_updat_prm = 0; - - /* Write all info to mmc */ - mmc_write(base, 0, (u_char *)&m, sizeof(m)); - - /* The following code start the modem of the 2.00 frequency - * selectable cards at power on. It's not strictly needed for the - * following boots... - * The original patch was by Joe Finney for the PCMCIA driver, but - * I've cleaned it a bit and add documentation. - * Thanks to Loeke Brederveld from Lucent for the info. - */ - - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) - * (does it work for everybody ? - especially old cards...) */ - /* Note : WFREQSEL verify that it is able to read from EEprom - * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID - * is 0xA (Xilinx version) or 0xB (Ariadne version). - * My test is more crude but do work... */ - if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - { - /* We must download the frequency parameters to the - * synthetisers (from the EEprom - area 1) - * Note : as the EEprom is auto decremented, we set the end - * if the area... */ - m.mmw_fee_addr = 0x0F; - m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; - mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, - (unsigned char *)&m.mmw_fee_ctrl, 2); - - /* Wait until the download is finished */ - fee_wait(base, 100, 100); - -#ifdef DEBUG_CONFIG_INFO - /* The frequency was in the last word downloaded... */ - mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m, - (unsigned char *)&m.mmw_fee_data_l, 2); - - /* Print some info for the user */ - printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n", - dev->name, - ((m.mmw_fee_data_h << 4) | - (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L); -#endif - - /* We must now download the power adjust value (gain) to - * the synthetisers (from the EEprom - area 7 - DAC) */ - m.mmw_fee_addr = 0x61; - m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; - mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, - (unsigned char *)&m.mmw_fee_ctrl, 2); - - /* Wait until the download is finished */ - } /* if 2.00 card */ - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); -#endif - return TRUE; -} - -/*------------------------------------------------------------------*/ -/* - * Routine to gracefully turn off reception, and wait for any commands - * to complete. - * (called in wv_ru_start() and wavelan_close() and wavelan_event()) - */ -static int -wv_ru_stop(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - unsigned long flags; - int status; - int spin; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); -#endif - - spin_lock_irqsave(&lp->spinlock, flags); - - /* First, send the LAN controller a stop receive command */ - wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv", - OP0_STOP_RCV, SR0_NO_RESULT); - - /* Then, spin until the receive unit goes idle */ - spin = 300; - do - { - udelay(10); - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0)); - - /* Now, spin until the chip finishes executing its current command */ - do - { - udelay(10); - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); - - spin_unlock_irqrestore(&lp->spinlock, flags); - - /* If there was a problem */ - if(spin <= 0) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n", - dev->name); -#endif - return FALSE; - } - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name); -#endif - return TRUE; -} /* wv_ru_stop */ - -/*------------------------------------------------------------------*/ -/* - * This routine starts the receive unit running. First, it checks if - * the card is actually ready. Then the card is instructed to receive - * packets again. - * (called in wv_hw_reset() & wavelan_open()) - */ -static int -wv_ru_start(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - unsigned long flags; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); -#endif - - /* - * We need to start from a quiescent state. To do so, we could check - * if the card is already running, but instead we just try to shut - * it down. First, we disable reception (in case it was already enabled). - */ - if(!wv_ru_stop(dev)) - return FALSE; - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Now we know that no command is being executed. */ - - /* Set the receive frame pointer and stop pointer */ - lp->rfp = 0; - outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); - - /* Reset ring management. This sets the receive frame pointer to 1 */ - outb(OP1_RESET_RING_MNGMT, LCCR(base)); - -#if 0 - /* XXX the i82593 manual page 6-4 seems to indicate that the stop register - should be set as below */ - /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/ -#elif 0 - /* but I set it 0 instead */ - lp->stop = 0; -#else - /* but I set it to 3 bytes per packet less than 8K */ - lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; -#endif - outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); - outb(OP1_INT_ENABLE, LCCR(base)); - outb(OP1_SWIT_TO_PORT_0, LCCR(base)); - - /* Reset receive DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - hacr_write_slow(base, HACR_DEFAULT); - - /* Receive DMA on channel 1 */ - wv_82593_cmd(dev, "wv_ru_start(): rcv-enable", - CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT); - -#ifdef DEBUG_I82593_SHOW - { - int status; - int opri; - int spin = 10000; - - /* spin until the chip starts receiving */ - do - { - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - if(spin-- <= 0) - break; - } - while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) && - ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY)); - printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n", - (status & SR3_RCV_STATE_MASK), i); - } -#endif - - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); -#endif - return TRUE; -} - -/*------------------------------------------------------------------*/ -/* - * This routine does a standard config of the WaveLAN controller (i82593). - * In the ISA driver, this is integrated in wavelan_hardware_reset() - * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit()) - */ -static int -wv_82593_config(struct net_device * dev) -{ - unsigned int base = dev->base_addr; - net_local * lp = netdev_priv(dev); - struct i82593_conf_block cfblk; - int ret = TRUE; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name); -#endif - - /* Create & fill i82593 config block - * - * Now conform to Wavelan document WCIN085B - */ - memset(&cfblk, 0x00, sizeof(struct i82593_conf_block)); - cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */ - cfblk.fifo_limit = 5; /* = 56 B rx and 40 B tx fifo thresholds */ - cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ - cfblk.fifo_32 = 1; - cfblk.throttle_enb = FALSE; - cfblk.contin = TRUE; /* enable continuous mode */ - cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */ - cfblk.addr_len = WAVELAN_ADDR_SIZE; - cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ - cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */ - cfblk.loopback = FALSE; - cfblk.lin_prio = 0; /* conform to 802.3 backoff algorithm */ - cfblk.exp_prio = 5; /* conform to 802.3 backoff algorithm */ - cfblk.bof_met = 1; /* conform to 802.3 backoff algorithm */ - cfblk.ifrm_spc = 0x20 >> 4; /* 32 bit times interframe spacing */ - cfblk.slottim_low = 0x20 >> 5; /* 32 bit times slot time */ - cfblk.slottim_hi = 0x0; - cfblk.max_retr = 15; - cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE); /* Promiscuous mode */ - cfblk.bc_dis = FALSE; /* Enable broadcast reception */ - cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ - cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */ - cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ - cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ - cfblk.cs_filter = 0; /* CS is recognized immediately */ - cfblk.crs_src = FALSE; /* External carrier sense */ - cfblk.cd_filter = 0; /* CD is recognized immediately */ - cfblk.min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length 64 bytes */ - cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ - cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ - cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ - cfblk.artx = TRUE; /* Disable automatic retransmission */ - cfblk.sarec = TRUE; /* Disable source addr trig of CD */ - cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ - cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ - cfblk.lbpkpol = TRUE; /* Loopback pin active high */ - cfblk.fdx = FALSE; /* Disable full duplex operation */ - cfblk.dummy_6 = 0x3f; /* all ones */ - cfblk.mult_ia = FALSE; /* No multiple individual addresses */ - cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ - cfblk.dummy_1 = TRUE; /* set to 1 */ - cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ -#ifdef MULTICAST_ALL - cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE); /* Allow all multicasts */ -#else - cfblk.mc_all = FALSE; /* No multicast all mode */ -#endif - cfblk.rcv_mon = 0; /* Monitor mode disabled */ - cfblk.frag_acpt = TRUE; /* Do not accept fragments */ - cfblk.tstrttrs = FALSE; /* No start transmission threshold */ - cfblk.fretx = TRUE; /* FIFO automatic retransmission */ - cfblk.syncrqs = FALSE; /* Synchronous DRQ deassertion... */ - cfblk.sttlen = TRUE; /* 6 byte status registers */ - cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ - cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ - cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ - cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ - -#ifdef DEBUG_I82593_SHOW - print_hex_dump(KERN_DEBUG, "wavelan_cs: config block: ", DUMP_PREFIX_NONE, - 16, 1, &cfblk, sizeof(struct i82593_conf_block), false); -#endif - - /* Copy the config block to the i82593 */ - outb(TX_BASE & 0xff, PIORL(base)); - outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); - outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base)); /* lsb */ - outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base)); /* msb */ - outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block)); - - /* reset transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - hacr_write(base, HACR_DEFAULT); - if(!wv_82593_cmd(dev, "wv_82593_config(): configure", - OP0_CONFIGURE, SR0_CONFIGURE_DONE)) - ret = FALSE; - - /* Initialize adapter's ethernet MAC address */ - outb(TX_BASE & 0xff, PIORL(base)); - outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); - outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */ - outb(0, PIOP(base)); /* byte count msb */ - outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE); - - /* reset transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - hacr_write(base, HACR_DEFAULT); - if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup", - OP0_IA_SETUP, SR0_IA_SETUP_DONE)) - ret = FALSE; - -#ifdef WAVELAN_ROAMING - /* If roaming is enabled, join the "Beacon Request" multicast group... */ - /* But only if it's not in there already! */ - if(do_roaming) - dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1); -#endif /* WAVELAN_ROAMING */ - - /* If any multicast address to set */ - if(lp->mc_count) - { - struct dev_mc_list * dmi; - int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n", - dev->name, lp->mc_count); - for(dmi=dev->mc_list; dmi; dmi=dmi->next) - printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); -#endif - - /* Initialize adapter's ethernet multicast addresses */ - outb(TX_BASE & 0xff, PIORL(base)); - outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); - outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */ - outb((addrs_len >> 8), PIOP(base)); /* byte count msb */ - for(dmi=dev->mc_list; dmi; dmi=dmi->next) - outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen); - - /* reset transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - hacr_write(base, HACR_DEFAULT); - if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup", - OP0_MC_SETUP, SR0_MC_SETUP_DONE)) - ret = FALSE; - lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */ - } - - /* Job done, clear the flag */ - lp->reconfig_82593 = FALSE; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name); -#endif - return(ret); -} - -/*------------------------------------------------------------------*/ -/* - * Read the Access Configuration Register, perform a software reset, - * and then re-enable the card's software. - * - * If I understand correctly : reset the pcmcia interface of the - * wavelan. - * (called by wv_config()) - */ -static int -wv_pcmcia_reset(struct net_device * dev) -{ - int i; - conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 }; - struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name); -#endif - - i = pcmcia_access_configuration_register(link, ®); - if (i != 0) - { - cs_error(link, AccessConfigurationRegister, i); - return FALSE; - } - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n", - dev->name, (u_int) reg.Value); -#endif - - reg.Action = CS_WRITE; - reg.Value = reg.Value | COR_SW_RESET; - i = pcmcia_access_configuration_register(link, ®); - if (i != 0) - { - cs_error(link, AccessConfigurationRegister, i); - return FALSE; - } - - reg.Action = CS_WRITE; - reg.Value = COR_LEVEL_IRQ | COR_CONFIG; - i = pcmcia_access_configuration_register(link, ®); - if (i != 0) - { - cs_error(link, AccessConfigurationRegister, i); - return FALSE; - } - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name); -#endif - return TRUE; -} - -/*------------------------------------------------------------------*/ -/* - * wavelan_hw_config() is called after a CARD_INSERTION event is - * received, to configure the wavelan hardware. - * Note that the reception will be enabled in wavelan->open(), so the - * device is configured but idle... - * Performs the following actions: - * 1. A pcmcia software reset (using wv_pcmcia_reset()) - * 2. A power reset (reset DMA) - * 3. Reset the LAN controller - * 4. Initialize the radio modem (using wv_mmc_init) - * 5. Configure LAN controller (using wv_82593_config) - * 6. Perform a diagnostic on the LAN controller - * (called by wavelan_event() & wv_hw_reset()) - */ -static int -wv_hw_config(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - unsigned int base = dev->base_addr; - unsigned long flags; - int ret = FALSE; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name); -#endif - - /* compile-time check the sizes of structures */ - BUILD_BUG_ON(sizeof(psa_t) != PSA_SIZE); - BUILD_BUG_ON(sizeof(mmw_t) != MMW_SIZE); - BUILD_BUG_ON(sizeof(mmr_t) != MMR_SIZE); - - /* Reset the pcmcia interface */ - if(wv_pcmcia_reset(dev) == FALSE) - return FALSE; - - /* Disable interrupts */ - spin_lock_irqsave(&lp->spinlock, flags); - - /* Disguised goto ;-) */ - do - { - /* Power UP the module + reset the modem + reset host adapter - * (in fact, reset DMA channels) */ - hacr_write_slow(base, HACR_RESET); - hacr_write(base, HACR_DEFAULT); - - /* Check if the module has been powered up... */ - if(hasr_read(base) & HASR_NO_CLK) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n", - dev->name); -#endif - break; - } - - /* initialize the modem */ - if(wv_mmc_init(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n", - dev->name); -#endif - break; - } - - /* reset the LAN controller (i82593) */ - outb(OP0_RESET, LCCR(base)); - mdelay(1); /* A bit crude ! */ - - /* Initialize the LAN controller */ - if(wv_82593_config(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", - dev->name); -#endif - break; - } - - /* Diagnostic */ - if(wv_diag(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n", - dev->name); -#endif - break; - } - - /* - * insert code for loopback test here - */ - - /* The device is now configured */ - lp->configured = 1; - ret = TRUE; - } - while(0); - - /* Re-enable interrupts */ - spin_unlock_irqrestore(&lp->spinlock, flags); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); -#endif - return(ret); -} - -/*------------------------------------------------------------------*/ -/* - * Totally reset the wavelan and restart it. - * Performs the following actions: - * 1. Call wv_hw_config() - * 2. Start the LAN controller's receive unit - * (called by wavelan_event(), wavelan_watchdog() and wavelan_open()) - */ -static void -wv_hw_reset(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name); -#endif - - lp->nresets++; - lp->configured = 0; - - /* Call wv_hw_config() for most of the reset & init stuff */ - if(wv_hw_config(dev) == FALSE) - return; - - /* start receive unit */ - wv_ru_start(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * wv_pcmcia_config() is called after a CARD_INSERTION event is - * received, to configure the PCMCIA socket, and to make the ethernet - * device available to the system. - * (called by wavelan_event()) - */ -static int -wv_pcmcia_config(struct pcmcia_device * link) -{ - struct net_device * dev = (struct net_device *) link->priv; - int i; - win_req_t req; - memreq_t mem; - net_local * lp = netdev_priv(dev); - - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link); -#endif - - do - { - i = pcmcia_request_io(link, &link->io); - if (i != 0) - { - cs_error(link, RequestIO, i); - break; - } - - /* - * Now allocate an interrupt line. Note that this does not - * actually assign a handler to the interrupt. - */ - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) - { - cs_error(link, RequestIRQ, i); - break; - } - - /* - * This actually configures the PCMCIA socket -- setting up - * the I/O windows and the interrupt mapping. - */ - link->conf.ConfigIndex = 1; - i = pcmcia_request_configuration(link, &link->conf); - if (i != 0) - { - cs_error(link, RequestConfiguration, i); - break; - } - - /* - * Allocate a small memory window. Note that the struct pcmcia_device - * structure provides space for one window handle -- if your - * device needs several windows, you'll need to keep track of - * the handles in your private data structure, link->priv. - */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = req.Size = 0; - req.AccessSpeed = mem_speed; - i = pcmcia_request_window(&link, &req, &link->win); - if (i != 0) - { - cs_error(link, RequestWindow, i); - break; - } - - lp->mem = ioremap(req.Base, req.Size); - dev->mem_start = (u_long)lp->mem; - dev->mem_end = dev->mem_start + req.Size; - - mem.CardOffset = 0; mem.Page = 0; - i = pcmcia_map_mem_page(link->win, &mem); - if (i != 0) - { - cs_error(link, MapMemPage, i); - break; - } - - /* Feed device with this info... */ - dev->irq = link->irq.AssignedIRQ; - dev->base_addr = link->io.BasePort1; - netif_start_queue(dev); - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART %p IRQ %d IOPORT 0x%x\n", - lp->mem, dev->irq, (u_int) dev->base_addr); -#endif - - SET_NETDEV_DEV(dev, &handle_to_dev(link)); - i = register_netdev(dev); - if(i != 0) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n"); -#endif - break; - } - } - while(0); /* Humm... Disguised goto !!! */ - - /* If any step failed, release any partially configured state */ - if(i != 0) - { - wv_pcmcia_release(link); - return FALSE; - } - - strcpy(((net_local *) netdev_priv(dev))->node.dev_name, dev->name); - link->dev_node = &((net_local *) netdev_priv(dev))->node; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "<-wv_pcmcia_config()\n"); -#endif - return TRUE; -} - -/*------------------------------------------------------------------*/ -/* - * After a card is removed, wv_pcmcia_release() will unregister the net - * device, and release the PCMCIA configuration. If the device is - * still open, this will be postponed until it is closed. - */ -static void -wv_pcmcia_release(struct pcmcia_device *link) -{ - struct net_device * dev = (struct net_device *) link->priv; - net_local * lp = netdev_priv(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link); -#endif - - iounmap(lp->mem); - pcmcia_disable_device(link); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); -#endif -} - -/************************ INTERRUPT HANDLING ************************/ - -/* - * This function is the interrupt handler for the WaveLAN card. This - * routine will be called whenever: - * 1. A packet is received. - * 2. A packet has successfully been transferred and the unit is - * ready to transmit another packet. - * 3. A command has completed execution. - */ -static irqreturn_t -wavelan_interrupt(int irq, - void * dev_id) -{ - struct net_device * dev = dev_id; - net_local * lp; - unsigned int base; - int status0; - u_int tx_status; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); -#endif - - lp = netdev_priv(dev); - base = dev->base_addr; - -#ifdef DEBUG_INTERRUPT_INFO - /* Check state of our spinlock (it should be cleared) */ - if(spin_is_locked(&lp->spinlock)) - printk(KERN_DEBUG - "%s: wavelan_interrupt(): spinlock is already locked !!!\n", - dev->name); -#endif - - /* Prevent reentrancy. We need to do that because we may have - * multiple interrupt handler running concurently. - * It is safe because interrupts are disabled before aquiring - * the spinlock. */ - spin_lock(&lp->spinlock); - - /* Treat all pending interrupts */ - while(1) - { - /* ---------------- INTERRUPT CHECKING ---------------- */ - /* - * Look for the interrupt and verify the validity - */ - outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); - status0 = inb(LCSR(base)); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, - (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT); - if(status0&SR0_INTERRUPT) - { - printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" : - ((status0 & SR0_EXECUTION) ? "cmd" : - ((status0 & SR0_RECEPTION) ? "recv" : "unknown")), - (status0 & SR0_EVENT_MASK)); - } - else - printk("\n"); -#endif - - /* Return if no actual interrupt from i82593 (normal exit) */ - if(!(status0 & SR0_INTERRUPT)) - break; - - /* If interrupt is both Rx and Tx or none... - * This code in fact is there to catch the spurious interrupt - * when you remove the wavelan pcmcia card from the socket */ - if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) || - ((status0 & SR0_BOTH_RX_TX) == 0x0)) - { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n", - dev->name, status0); -#endif - /* Acknowledge the interrupt */ - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); - break; - } - - /* ----------------- RECEIVING PACKET ----------------- */ - /* - * When the wavelan signal the reception of a new packet, - * we call wv_packet_rcv() to copy if from the buffer and - * send it to NET3 - */ - if(status0 & SR0_RECEPTION) - { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name); -#endif - - if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n", - dev->name); -#endif - dev->stats.rx_over_errors++; - lp->overrunning = 1; - } - - /* Get the packet */ - wv_packet_rcv(dev); - lp->overrunning = 0; - - /* Acknowledge the interrupt */ - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); - continue; - } - - /* ---------------- COMMAND COMPLETION ---------------- */ - /* - * Interrupts issued when the i82593 has completed a command. - * Most likely : transmission done - */ - - /* If a transmission has been done */ - if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || - (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE || - (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) - { -#ifdef DEBUG_TX_ERROR - if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) - printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n", - dev->name); -#endif - - /* Get transmission status */ - tx_status = inb(LCSR(base)); - tx_status |= (inb(LCSR(base)) << 8); -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n", - dev->name); - { - u_int rcv_bytes; - u_char status3; - rcv_bytes = inb(LCSR(base)); - rcv_bytes |= (inb(LCSR(base)) << 8); - status3 = inb(LCSR(base)); - printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n", - tx_status, rcv_bytes, (u_int) status3); - } -#endif - /* Check for possible errors */ - if((tx_status & TX_OK) != TX_OK) - { - dev->stats.tx_errors++; - - if(tx_status & TX_FRTL) - { -#ifdef DEBUG_TX_ERROR - printk(KERN_INFO "%s: wv_interrupt(): frame too long\n", - dev->name); -#endif - } - if(tx_status & TX_UND_RUN) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n", - dev->name); -#endif - dev->stats.tx_aborted_errors++; - } - if(tx_status & TX_LOST_CTS) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name); -#endif - dev->stats.tx_carrier_errors++; - } - if(tx_status & TX_LOST_CRS) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n", - dev->name); -#endif - dev->stats.tx_carrier_errors++; - } - if(tx_status & TX_HRT_BEAT) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name); -#endif - dev->stats.tx_heartbeat_errors++; - } - if(tx_status & TX_DEFER) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n", - dev->name); -#endif - } - /* Ignore late collisions since they're more likely to happen - * here (the WaveLAN design prevents the LAN controller from - * receiving while it is transmitting). We take action only when - * the maximum retransmit attempts is exceeded. - */ - if(tx_status & TX_COLL) - { - if(tx_status & TX_MAX_COL) - { -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n", - dev->name); -#endif - if(!(tx_status & TX_NCOL_MASK)) - { - dev->stats.collisions += 0x10; - } - } - } - } /* if(!(tx_status & TX_OK)) */ - - dev->stats.collisions += (tx_status & TX_NCOL_MASK); - dev->stats.tx_packets++; - - netif_wake_queue(dev); - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ - } - else /* if interrupt = transmit done or retransmit done */ - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n", - status0); -#endif - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ - } - } /* while(1) */ - - spin_unlock(&lp->spinlock); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); -#endif - - /* We always return IRQ_HANDLED, because we will receive empty - * interrupts under normal operations. Anyway, it doesn't matter - * as we are dealing with an ISA interrupt that can't be shared. - * - * Explanation : under heavy receive, the following happens : - * ->wavelan_interrupt() - * (status0 & SR0_INTERRUPT) != 0 - * ->wv_packet_rcv() - * (status0 & SR0_INTERRUPT) != 0 - * ->wv_packet_rcv() - * (status0 & SR0_INTERRUPT) == 0 // i.e. no more event - * <-wavelan_interrupt() - * ->wavelan_interrupt() - * (status0 & SR0_INTERRUPT) == 0 // i.e. empty interrupt - * <-wavelan_interrupt() - * Jean II */ - return IRQ_HANDLED; -} /* wv_interrupt */ - -/*------------------------------------------------------------------*/ -/* - * Watchdog: when we start a transmission, a timer is set for us in the - * kernel. If the transmission completes, this timer is disabled. If - * the timer expires, we are called and we try to unlock the hardware. - * - * Note : This watchdog is move clever than the one in the ISA driver, - * because it try to abort the current command before reseting - * everything... - * On the other hand, it's a bit simpler, because we don't have to - * deal with the multiple Tx buffers... - */ -static void -wavelan_watchdog(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - unsigned int base = dev->base_addr; - unsigned long flags; - int aborted = FALSE; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); -#endif - -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", - dev->name); -#endif - - spin_lock_irqsave(&lp->spinlock, flags); - - /* Ask to abort the current command */ - outb(OP0_ABORT, LCCR(base)); - - /* Wait for the end of the command (a bit hackish) */ - if(wv_82593_cmd(dev, "wavelan_watchdog(): abort", - OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED)) - aborted = TRUE; - - /* Release spinlock here so that wv_hw_reset() can grab it */ - spin_unlock_irqrestore(&lp->spinlock, flags); - - /* Check if we were successful in aborting it */ - if(!aborted) - { - /* It seem that it wasn't enough */ -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n", - dev->name); -#endif - wv_hw_reset(dev); - } - -#ifdef DEBUG_PSA_SHOW - { - psa_t psa; - psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); - wv_psa_show(&psa); - } -#endif -#ifdef DEBUG_MMC_SHOW - wv_mmc_show(dev); -#endif -#ifdef DEBUG_I82593_SHOW - wv_ru_show(dev); -#endif - - /* We are no more waiting for something... */ - netif_wake_queue(dev); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); -#endif -} - -/********************* CONFIGURATION CALLBACKS *********************/ -/* - * Here are the functions called by the pcmcia package (cardmgr) and - * linux networking (NET3) for initialization, configuration and - * deinstallations of the Wavelan Pcmcia Hardware. - */ - -/*------------------------------------------------------------------*/ -/* - * Configure and start up the WaveLAN PCMCIA adaptor. - * Called by NET3 when it "open" the device. - */ -static int -wavelan_open(struct net_device * dev) -{ - net_local * lp = netdev_priv(dev); - struct pcmcia_device * link = lp->link; - unsigned int base = dev->base_addr; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* Check if the modem is powered up (wavelan_close() power it down */ - if(hasr_read(base) & HASR_NO_CLK) - { - /* Power up (power up time is 250us) */ - hacr_write(base, HACR_DEFAULT); - - /* Check if the module has been powered up... */ - if(hasr_read(base) & HASR_NO_CLK) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n", - dev->name); -#endif - return FALSE; - } - } - - /* Start reception and declare the driver ready */ - if(!lp->configured) - return FALSE; - if(!wv_ru_start(dev)) - wv_hw_reset(dev); /* If problem : reset */ - netif_start_queue(dev); - - /* Mark the device as used */ - link->open++; - -#ifdef WAVELAN_ROAMING - if(do_roaming) - wv_roam_init(dev); -#endif /* WAVELAN_ROAMING */ - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Shutdown the WaveLAN PCMCIA adaptor. - * Called by NET3 when it "close" the device. - */ -static int -wavelan_close(struct net_device * dev) -{ - struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; - unsigned int base = dev->base_addr; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* If the device isn't open, then nothing to do */ - if(!link->open) - { -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name); -#endif - return 0; - } - -#ifdef WAVELAN_ROAMING - /* Cleanup of roaming stuff... */ - if(do_roaming) - wv_roam_cleanup(dev); -#endif /* WAVELAN_ROAMING */ - - link->open--; - - /* If the card is still present */ - if(netif_running(dev)) - { - netif_stop_queue(dev); - - /* Stop receiving new messages and wait end of transmission */ - wv_ru_stop(dev); - - /* Power down the module */ - hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT)); - } - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); -#endif - return 0; -} - -static const struct net_device_ops wavelan_netdev_ops = { - .ndo_open = wavelan_open, - .ndo_stop = wavelan_close, - .ndo_start_xmit = wavelan_packet_xmit, - .ndo_set_multicast_list = wavelan_set_multicast_list, -#ifdef SET_MAC_ADDRESS - .ndo_set_mac_address = wavelan_set_mac_address, -#endif - .ndo_tx_timeout = wavelan_watchdog, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -/*------------------------------------------------------------------*/ -/* - * wavelan_attach() creates an "instance" of the driver, allocating - * local data structures for one device (one interface). The device - * is registered with Card Services. - * - * The dev_link structure is initialized, but we don't actually - * configure the card at this point -- we wait until we receive a - * card insertion event. - */ -static int -wavelan_probe(struct pcmcia_device *p_dev) -{ - struct net_device * dev; /* Interface generic data */ - net_local * lp; /* Interface specific data */ - int ret; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "-> wavelan_attach()\n"); -#endif - - /* The io structure describes IO port mapping */ - p_dev->io.NumPorts1 = 8; - p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - p_dev->io.IOAddrLines = 3; - - /* Interrupt setup */ - p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; - p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; - p_dev->irq.Handler = wavelan_interrupt; - - /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; - p_dev->conf.IntType = INT_MEMORY_AND_IO; - - /* Allocate the generic data structure */ - dev = alloc_etherdev(sizeof(net_local)); - if (!dev) - return -ENOMEM; - - p_dev->priv = p_dev->irq.Instance = dev; - - lp = netdev_priv(dev); - - /* Init specific data */ - lp->configured = 0; - lp->reconfig_82593 = FALSE; - lp->nresets = 0; - /* Multicast stuff */ - lp->promiscuous = 0; - lp->allmulticast = 0; - lp->mc_count = 0; - - /* Init spinlock */ - spin_lock_init(&lp->spinlock); - - /* back links */ - lp->dev = dev; - - /* wavelan NET3 callbacks */ - dev->netdev_ops = &wavelan_netdev_ops; - dev->watchdog_timeo = WATCHDOG_JIFFIES; - SET_ETHTOOL_OPS(dev, &ops); - - dev->wireless_handlers = &wavelan_handler_def; - lp->wireless_data.spy_data = &lp->spy_data; - dev->wireless_data = &lp->wireless_data; - - /* Other specific data */ - dev->mtu = WAVELAN_MTU; - - ret = wv_pcmcia_config(p_dev); - if (ret) - return ret; - - ret = wv_hw_config(dev); - if (ret) { - dev->irq = 0; - pcmcia_disable_device(p_dev); - return ret; - } - - wv_init_info(dev); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "<- wavelan_attach()\n"); -#endif - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This deletes a driver "instance". The device is de-registered with - * Card Services. If it has been released, all local data structures - * are freed. Otherwise, the structures will be freed when the device - * is released. - */ -static void -wavelan_detach(struct pcmcia_device *link) -{ -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link); -#endif - - /* Some others haven't done their job : give them another chance */ - wv_pcmcia_release(link); - - /* Free pieces */ - if(link->priv) - { - struct net_device * dev = (struct net_device *) link->priv; - - /* Remove ourselves from the kernel list of ethernet devices */ - /* Warning : can't be called from interrupt, timer or wavelan_close() */ - if (link->dev_node) - unregister_netdev(dev); - link->dev_node = NULL; - ((net_local *)netdev_priv(dev))->link = NULL; - ((net_local *)netdev_priv(dev))->dev = NULL; - free_netdev(dev); - } - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "<- wavelan_detach()\n"); -#endif -} - -static int wavelan_suspend(struct pcmcia_device *link) -{ - struct net_device * dev = (struct net_device *) link->priv; - - /* NB: wavelan_close will be called, but too late, so we are - * obliged to close nicely the wavelan here. David, could you - * close the device before suspending them ? And, by the way, - * could you, on resume, add a "route add -net ..." after the - * ifconfig up ? Thanks... */ - - /* Stop receiving new messages and wait end of transmission */ - wv_ru_stop(dev); - - if (link->open) - netif_device_detach(dev); - - /* Power down the module */ - hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT)); - - return 0; -} - -static int wavelan_resume(struct pcmcia_device *link) -{ - struct net_device * dev = (struct net_device *) link->priv; - - if (link->open) { - wv_hw_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -static struct pcmcia_device_id wavelan_ids[] = { - PCMCIA_DEVICE_PROD_ID12("AT&T","WaveLAN/PCMCIA", 0xe7c5affd, 0x1bc50975), - PCMCIA_DEVICE_PROD_ID12("Digital", "RoamAbout/DS", 0x9999ab35, 0x00d05e06), - PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/PCMCIA", 0x23eb9949, 0x1bc50975), - PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/PCMCIA", 0x24358cd4, 0x1bc50975), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, wavelan_ids); - -static struct pcmcia_driver wavelan_driver = { - .owner = THIS_MODULE, - .drv = { - .name = "wavelan_cs", - }, - .probe = wavelan_probe, - .remove = wavelan_detach, - .id_table = wavelan_ids, - .suspend = wavelan_suspend, - .resume = wavelan_resume, -}; - -static int __init -init_wavelan_cs(void) -{ - return pcmcia_register_driver(&wavelan_driver); -} - -static void __exit -exit_wavelan_cs(void) -{ - pcmcia_unregister_driver(&wavelan_driver); -} - -module_init(init_wavelan_cs); -module_exit(exit_wavelan_cs); diff --git a/drivers/net/wireless/wavelan_cs.h b/drivers/net/wireless/wavelan_cs.h deleted file mode 100644 index 2e4bfe4147c6..000000000000 --- a/drivers/net/wireless/wavelan_cs.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Wavelan Pcmcia driver - * - * Jean II - HPLB '96 - * - * Reorganization and extension of the driver. - * Original copyright follow. See wavelan_cs.h for details. - * - * This file contain the declarations of the Wavelan hardware. Note that - * the Pcmcia Wavelan include a i82593 controller (see definitions in - * file i82593.h). - * - * The main difference between the pcmcia hardware and the ISA one is - * the Ethernet Controller (i82593 instead of i82586). The i82593 allow - * only one send buffer. The PSA (Parameter Storage Area : EEprom for - * permanent storage of various info) is memory mapped, but not the - * MMI (Modem Management Interface). - */ - -/* - * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: - * An Ethernet-like radio transceiver controlled by an Intel 82593 - * coprocessor. - * - * - **************************************************************************** - * Copyright 1995 - * Anthony D. Joseph - * Massachusetts Institute of Technology - * - * Permission to use, copy, modify, and distribute this program - * for any purpose and without fee is hereby granted, provided - * that this copyright and permission notice appear on all copies - * and supporting documentation, the name of M.I.T. not be used - * in advertising or publicity pertaining to distribution of the - * program without specific prior permission, and notice be given - * in supporting documentation that copying and distribution is - * by permission of M.I.T. M.I.T. makes no representations about - * the suitability of this software for any purpose. It is pro- - * vided "as is" without express or implied warranty. - **************************************************************************** - * - * - * Credits: - * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for - * providing extremely useful information about WaveLAN PCMCIA hardware - * - * This driver is based upon several other drivers, in particular: - * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter - * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter - * Anders Klemets' PCMCIA WaveLAN adapter driver - * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter - */ - -#ifndef _WAVELAN_CS_H -#define _WAVELAN_CS_H - -/************************** MAGIC NUMBERS ***************************/ - -/* The detection of the wavelan card is made by reading the MAC address - * from the card and checking it. If you have a non AT&T product (OEM, - * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this - * part to accommodate your hardware... - */ -static const unsigned char MAC_ADDRESSES[][3] = -{ - { 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */ - { 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */ - { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ - { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ - /* Add your card here and send me the patch ! */ -}; - -/* - * Constants used to convert channels to frequencies - */ - -/* Frequency available in the 2.0 modem, in units of 250 kHz - * (as read in the offset register of the dac area). - * Used to map channel numbers used by `wfreqsel' to frequencies - */ -static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, - 0xD0, 0xF0, 0xF8, 0x150 }; - -/* Frequencies of the 1.0 modem (fixed frequencies). - * Use to map the PSA `subband' to a frequency - * Note : all frequencies apart from the first one need to be multiplied by 10 - */ -static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; - - -/*************************** PC INTERFACE ****************************/ - -/* WaveLAN host interface definitions */ - -#define LCCR(base) (base) /* LAN Controller Command Register */ -#define LCSR(base) (base) /* LAN Controller Status Register */ -#define HACR(base) (base+0x1) /* Host Adapter Command Register */ -#define HASR(base) (base+0x1) /* Host Adapter Status Register */ -#define PIORL(base) (base+0x2) /* Program I/O Register Low */ -#define RPLL(base) (base+0x2) /* Receive Pointer Latched Low */ -#define PIORH(base) (base+0x3) /* Program I/O Register High */ -#define RPLH(base) (base+0x3) /* Receive Pointer Latched High */ -#define PIOP(base) (base+0x4) /* Program I/O Port */ -#define MMR(base) (base+0x6) /* MMI Address Register */ -#define MMD(base) (base+0x7) /* MMI Data Register */ - -/* Host Adaptor Command Register bit definitions */ - -#define HACR_LOF (1 << 3) /* Lock Out Flag, toggle every 250ms */ -#define HACR_PWR_STAT (1 << 4) /* Power State, 1=active, 0=sleep */ -#define HACR_TX_DMA_RESET (1 << 5) /* Reset transmit DMA ptr on high */ -#define HACR_RX_DMA_RESET (1 << 6) /* Reset receive DMA ptr on high */ -#define HACR_ROM_WEN (1 << 7) /* EEPROM write enabled when true */ - -#define HACR_RESET (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET) -#define HACR_DEFAULT (HACR_PWR_STAT) - -/* Host Adapter Status Register bit definitions */ - -#define HASR_MMI_BUSY (1 << 2) /* MMI is busy when true */ -#define HASR_LOF (1 << 3) /* Lock out flag status */ -#define HASR_NO_CLK (1 << 4) /* active when modem not connected */ - -/* Miscellaneous bit definitions */ - -#define PIORH_SEL_TX (1 << 5) /* PIOR points to 0=rx/1=tx buffer */ -#define MMR_MMI_WR (1 << 0) /* Next MMI cycle is 0=read, 1=write */ -#define PIORH_MASK 0x1f /* only low 5 bits are significant */ -#define RPLH_MASK 0x1f /* only low 5 bits are significant */ -#define MMI_ADDR_MASK 0x7e /* Bits 1-6 of MMR are significant */ - -/* Attribute Memory map */ - -#define CIS_ADDR 0x0000 /* Card Information Status Register */ -#define PSA_ADDR 0x0e00 /* Parameter Storage Area address */ -#define EEPROM_ADDR 0x1000 /* EEPROM address (unused ?) */ -#define COR_ADDR 0x4000 /* Configuration Option Register */ - -/* Configuration Option Register bit definitions */ - -#define COR_CONFIG (1 << 0) /* Config Index, 0 when unconfigured */ -#define COR_SW_RESET (1 << 7) /* Software Reset on true */ -#define COR_LEVEL_IRQ (1 << 6) /* Level IRQ */ - -/* Local Memory map */ - -#define RX_BASE 0x0000 /* Receive memory, 8 kB */ -#define TX_BASE 0x2000 /* Transmit memory, 2 kB */ -#define UNUSED_BASE 0x2800 /* Unused, 22 kB */ -#define RX_SIZE (TX_BASE-RX_BASE) /* Size of receive area */ -#define RX_SIZE_SHIFT 6 /* Bits to shift in stop register */ - -#define TRUE 1 -#define FALSE 0 - -#define MOD_ENAL 1 -#define MOD_PROM 2 - -/* Size of a MAC address */ -#define WAVELAN_ADDR_SIZE 6 - -/* Maximum size of Wavelan packet */ -#define WAVELAN_MTU 1500 - -#define MAXDATAZ (6 + 6 + 2 + WAVELAN_MTU) - -/********************** PARAMETER STORAGE AREA **********************/ - -/* - * Parameter Storage Area (PSA). - */ -typedef struct psa_t psa_t; -struct psa_t -{ - /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */ - unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */ - unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */ - unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */ - unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */ - unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */ - unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */ - unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */ - unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */ - unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */ - unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */ - - unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */ - unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */ - unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */ -#define PSA_UNIVERSAL 0 /* Universal (factory) */ -#define PSA_LOCAL 1 /* Local */ - unsigned char psa_comp_number; /* [0x1D] Compatability Number: */ -#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */ -#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */ -#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */ -#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */ -#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */ - unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */ - unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */ -#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */ - unsigned char psa_subband; /* [0x20] Subband */ -#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */ -#define PSA_SUBBAND_2425 1 /* 2425 MHz */ -#define PSA_SUBBAND_2460 2 /* 2460 MHz */ -#define PSA_SUBBAND_2484 3 /* 2484 MHz */ -#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */ - unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */ - unsigned char psa_mod_delay; /* [0x22] Modem Delay ??? (reserved) */ - unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */ - unsigned char psa_nwid_select; /* [0x25] Network ID Select On Off */ - unsigned char psa_encryption_select; /* [0x26] Encryption On Off */ - unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */ - unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */ - unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */ - unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */ - unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */ - unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/ - unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */ - unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */ -}; - -/* Size for structure checking (if padding is correct) */ -#define PSA_SIZE 64 - -/* Calculate offset of a field in the above structure - * Warning : only even addresses are used */ -#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) - -/******************** MODEM MANAGEMENT INTERFACE ********************/ - -/* - * Modem Management Controller (MMC) write structure. - */ -typedef struct mmw_t mmw_t; -struct mmw_t -{ - unsigned char mmw_encr_key[8]; /* encryption key */ - unsigned char mmw_encr_enable; /* enable/disable encryption */ -#define MMW_ENCR_ENABLE_MODE 0x02 /* Mode of security option */ -#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option */ - unsigned char mmw_unused0[1]; /* unused */ - unsigned char mmw_des_io_invert; /* Encryption option */ -#define MMW_DES_IO_INVERT_RES 0x0F /* Reserved */ -#define MMW_DES_IO_INVERT_CTRL 0xF0 /* Control ??? (set to 0) */ - unsigned char mmw_unused1[5]; /* unused */ - unsigned char mmw_loopt_sel; /* looptest selection */ -#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* disable NWID filtering */ -#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */ -#define MMW_LOOPT_SEL_LS 0x10 /* looptest w/o collision avoidance */ -#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */ -#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */ -#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */ -#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */ - unsigned char mmw_jabber_enable; /* jabber timer enable */ - /* Abort transmissions > 200 ms */ - unsigned char mmw_freeze; /* freeze / unfreeeze signal level */ - /* 0 : signal level & qual updated for every new message, 1 : frozen */ - unsigned char mmw_anten_sel; /* antenna selection */ -#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */ -#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */ - unsigned char mmw_ifs; /* inter frame spacing */ - /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ - unsigned char mmw_mod_delay; /* modem delay (synchro) */ - unsigned char mmw_jam_time; /* jamming time (after collision) */ - unsigned char mmw_unused2[1]; /* unused */ - unsigned char mmw_thr_pre_set; /* level threshold preset */ - /* Discard all packet with signal < this value (4) */ - unsigned char mmw_decay_prm; /* decay parameters */ - unsigned char mmw_decay_updat_prm; /* decay update parameterz */ - unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */ - /* Discard all packet with quality < this value (3) */ - unsigned char mmw_netw_id_l; /* NWID low order byte */ - unsigned char mmw_netw_id_h; /* NWID high order byte */ - /* Network ID or Domain : create virtual net on the air */ - - /* 2.0 Hardware extension - frequency selection support */ - unsigned char mmw_mode_select; /* for analog tests (set to 0) */ - unsigned char mmw_unused3[1]; /* unused */ - unsigned char mmw_fee_ctrl; /* frequency eeprom control */ -#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions */ -#define MMW_FEE_CTRL_DWLD 0x08 /* Download eeprom to mmc */ -#define MMW_FEE_CTRL_CMD 0x07 /* EEprom commands : */ -#define MMW_FEE_CTRL_READ 0x06 /* Read */ -#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */ -#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address */ -#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses */ -#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */ -#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */ -#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */ -#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers */ -#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write addr in protect register */ -#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ - /* Never issue this command (PRDS) : it's irreversible !!! */ - - unsigned char mmw_fee_addr; /* EEprom address */ -#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel */ -#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */ -#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */ -#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */ -#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */ -#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */ - - unsigned char mmw_fee_data_l; /* Write data to EEprom */ - unsigned char mmw_fee_data_h; /* high octet */ - unsigned char mmw_ext_ant; /* Setting for external antenna */ -#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */ -#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */ -#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */ -#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */ -#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */ -} __attribute__((packed)); - -/* Size for structure checking (if padding is correct) */ -#define MMW_SIZE 37 - -/* Calculate offset of a field in the above structure */ -#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) - - -/* - * Modem Management Controller (MMC) read structure. - */ -typedef struct mmr_t mmr_t; -struct mmr_t -{ - unsigned char mmr_unused0[8]; /* unused */ - unsigned char mmr_des_status; /* encryption status */ - unsigned char mmr_des_avail; /* encryption available (0x55 read) */ -#define MMR_DES_AVAIL_DES 0x55 /* DES available */ -#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */ - unsigned char mmr_des_io_invert; /* des I/O invert register */ - unsigned char mmr_unused1[5]; /* unused */ - unsigned char mmr_dce_status; /* DCE status */ -#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */ -#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ -#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ -#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ -#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ - unsigned char mmr_dsp_id; /* DSP id (AA = Daedalus rev A) */ - unsigned char mmr_unused2[2]; /* unused */ - unsigned char mmr_correct_nwid_l; /* # of correct NWID's rxd (low) */ - unsigned char mmr_correct_nwid_h; /* # of correct NWID's rxd (high) */ - /* Warning : Read high order octet first !!! */ - unsigned char mmr_wrong_nwid_l; /* # of wrong NWID's rxd (low) */ - unsigned char mmr_wrong_nwid_h; /* # of wrong NWID's rxd (high) */ - unsigned char mmr_thr_pre_set; /* level threshold preset */ -#define MMR_THR_PRE_SET 0x3F /* level threshold preset */ -#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */ - unsigned char mmr_signal_lvl; /* signal level */ -#define MMR_SIGNAL_LVL 0x3F /* signal level */ -#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */ - unsigned char mmr_silence_lvl; /* silence level (noise) */ -#define MMR_SILENCE_LVL 0x3F /* silence level */ -#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */ - unsigned char mmr_sgnl_qual; /* signal quality */ -#define MMR_SGNL_QUAL 0x0F /* signal quality */ -#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */ - unsigned char mmr_netw_id_l; /* NWID low order byte ??? */ - unsigned char mmr_unused3[3]; /* unused */ - - /* 2.0 Hardware extension - frequency selection support */ - unsigned char mmr_fee_status; /* Status of frequency eeprom */ -#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision id */ -#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */ -#define MMR_FEE_STATUS_BUSY 0x04 /* EEprom busy */ - unsigned char mmr_unused4[1]; /* unused */ - unsigned char mmr_fee_data_l; /* Read data from eeprom (low) */ - unsigned char mmr_fee_data_h; /* Read data from eeprom (high) */ -}; - -/* Size for structure checking (if padding is correct) */ -#define MMR_SIZE 36 - -/* Calculate offset of a field in the above structure */ -#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) - - -/* Make the two above structures one */ -typedef union mm_t -{ - struct mmw_t w; /* Write to the mmc */ - struct mmr_t r; /* Read from the mmc */ -} mm_t; - -#endif /* _WAVELAN_CS_H */ diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h deleted file mode 100644 index 81d91531c4f9..000000000000 --- a/drivers/net/wireless/wavelan_cs.p.h +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Wavelan Pcmcia driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * - * This file contain all definition and declarations necessary for the - * wavelan pcmcia driver. This file is a private header, so it should - * be included only on wavelan_cs.c !!! - */ - -#ifndef WAVELAN_CS_P_H -#define WAVELAN_CS_P_H - -/************************** DOCUMENTATION **************************/ -/* - * This driver provide a Linux interface to the Wavelan Pcmcia hardware - * The Wavelan is a product of Lucent (http://www.wavelan.com/). - * This division was formerly part of NCR and then AT&T. - * Wavelan are also distributed by DEC (RoamAbout DS)... - * - * To know how to use this driver, read the PCMCIA HOWTO. - * If you want to exploit the many other fonctionalities, look comments - * in the code... - * - * This driver is the result of the effort of many peoples (see below). - */ - -/* ------------------------ SPECIFIC NOTES ------------------------ */ -/* - * Web page - * -------- - * I try to maintain a web page with the Wireless LAN Howto at : - * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html - * - * SMP - * --- - * We now are SMP compliant (I eventually fixed the remaining bugs). - * The driver has been tested on a dual P6-150 and survived my usual - * set of torture tests. - * Anyway, I spent enough time chasing interrupt re-entrancy during - * errors or reconfigure, and I designed the locked/unlocked sections - * of the driver with great care, and with the recent addition of - * the spinlock (thanks to the new API), we should be quite close to - * the truth. - * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), - * but better safe than sorry (especially at 2 Mb/s ;-). - * - * I have also looked into disabling only our interrupt on the card - * (via HACR) instead of all interrupts in the processor (via cli), - * so that other driver are not impacted, and it look like it's - * possible, but it's very tricky to do right (full of races). As - * the gain would be mostly for SMP systems, it can wait... - * - * Debugging and options - * --------------------- - * You will find below a set of '#define" allowing a very fine control - * on the driver behaviour and the debug messages printed. - * The main options are : - * o WAVELAN_ROAMING, for the experimental roaming support. - * o SET_PSA_CRC, to have your card correctly recognised by - * an access point and the Point-to-Point diagnostic tool. - * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) - * (otherwise we always start afresh with some defaults) - * - * wavelan_cs.o is darn too big - * ------------------------- - * That's true ! There is a very simple way to reduce the driver - * object by 33% (yes !). Comment out the following line : - * #include - * Other compile options can also reduce the size of it... - * - * MAC address and hardware detection : - * ---------------------------------- - * The detection code of the wavelan chech that the first 3 - * octets of the MAC address fit the company code. This type of - * detection work well for AT&T cards (because the AT&T code is - * hardcoded in wavelan_cs.h), but of course will fail for other - * manufacturer. - * - * If you are sure that your card is derived from the wavelan, - * here is the way to configure it : - * 1) Get your MAC address - * a) With your card utilities (wfreqsel, instconf, ...) - * b) With the driver : - * o compile the kernel with DEBUG_CONFIG_INFO enabled - * o Boot and look the card messages - * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan_cs.h) - * 3) Compile & verify - * 4) Send me the MAC code - I will include it in the next version... - * - */ - -/* --------------------- WIRELESS EXTENSIONS --------------------- */ -/* - * This driver is the first one to support "wireless extensions". - * This set of extensions provide you some way to control the wireless - * caracteristics of the hardware in a standard way and support for - * applications for taking advantage of it (like Mobile IP). - * - * It might be a good idea as well to fetch the wireless tools to - * configure the device and play a bit. - */ - -/* ---------------------------- FILES ---------------------------- */ -/* - * wavelan_cs.c : The actual code for the driver - C functions - * - * wavelan_cs.p.h : Private header : local types / vars for the driver - * - * wavelan_cs.h : Description of the hardware interface & structs - * - * i82593.h : Description if the Ethernet controller - */ - -/* --------------------------- HISTORY --------------------------- */ -/* - * The history of the Wavelan drivers is as complicated as history of - * the Wavelan itself (NCR -> AT&T -> Lucent). - * - * All started with Anders Klemets , - * writing a Wavelan ISA driver for the MACH microkernel. Girish - * Welling had also worked on it. - * Keith Moore modify this for the Pcmcia hardware. - * - * Robert Morris port these two drivers to BSDI - * and add specific Pcmcia support (there is currently no equivalent - * of the PCMCIA package under BSD...). - * - * Jim Binkley port both BSDI drivers to FreeBSD. - * - * Bruce Janson port the BSDI ISA driver to Linux. - * - * Anthony D. Joseph started modify Bruce driver - * (with help of the BSDI PCMCIA driver) for PCMCIA. - * Yunzhou Li finished is work. - * Joe Finney patched the driver to start - * correctly 2.00 cards (2.4 GHz with frequency selection). - * David Hinds integrated the whole in his - * Pcmcia package (+ bug corrections). - * - * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some - * patchs to the Pcmcia driver. After, I added code in the ISA driver - * for Wireless Extensions and full support of frequency selection - * cards. Now, I'm doing the same to the Pcmcia driver + some - * reorganisation. - * Loeke Brederveld from Lucent has given me - * much needed informations on the Wavelan hardware. - */ - -/* By the way : for the copyright & legal stuff : - * Almost everybody wrote code under GNU or BSD license (or alike), - * and want that their original copyright remain somewhere in the - * code (for myself, I go with the GPL). - * Nobody want to take responsibility for anything, except the fame... - */ - -/* --------------------------- CREDITS --------------------------- */ -/* - * Credits: - * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and - * Loeke Brederveld of Lucent for providing extremely useful - * information about WaveLAN PCMCIA hardware - * - * This driver is based upon several other drivers, in particular: - * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter - * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter - * Anders Klemets' PCMCIA WaveLAN adapter driver - * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter - * - * Additional Credits: - * - * This software was originally developed under Linux 1.2.3 - * (Slackware 2.0 distribution). - * And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+) - * with an HP OmniBook 4000 and then a 5500. - * - * It is based on other device drivers and information either written - * or supplied by: - * James Ashton (jaa101@syseng.anu.edu.au), - * Ajay Bakre (bakre@paul.rutgers.edu), - * Donald Becker (becker@super.org), - * Jim Binkley , - * Loeke Brederveld , - * Allan Creighton (allanc@cs.su.oz.au), - * Brent Elphick , - * Joe Finney , - * Matthew Geier (matthew@cs.su.oz.au), - * Remo di Giovanni (remo@cs.su.oz.au), - * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), - * David Hinds , - * Jan Hoogendoorn (c/o marteijn@lucent.com), - * Bruce Janson , - * Anthony D. Joseph , - * Anders Klemets (klemets@paul.rutgers.edu), - * Yunzhou Li , - * Marc Meertens (mmeertens@lucent.com), - * Keith Moore, - * Robert Morris (rtm@das.harvard.edu), - * Ian Parkin (ian@cs.su.oz.au), - * John Rosenberg (johnr@cs.su.oz.au), - * George Rossi (george@phm.gov.au), - * Arthur Scott (arthur@cs.su.oz.au), - * Stanislav Sinyagin - * Peter Storey, - * Jean Tourrilhes , - * Girish Welling (welling@paul.rutgers.edu) - * Clark Woodworth - * Yongguang Zhang ... - */ - -/* ------------------------- IMPROVEMENTS ------------------------- */ -/* - * I proudly present : - * - * Changes made in 2.8.22 : - * ---------------------- - * - improved wv_set_multicast_list - * - catch spurious interrupt - * - correct release of the device - * - * Changes mades in release : - * ------------------------ - * - Reorganisation of the code, function name change - * - Creation of private header (wavelan_cs.h) - * - Reorganised debug messages - * - More comments, history, ... - * - Configure earlier (in "insert" instead of "open") - * and do things only once - * - mmc_init : configure the PSA if not done - * - mmc_init : 2.00 detection better code for 2.00 init - * - better info at startup - * - Correct a HUGE bug (volatile & uncalibrated busy loop) - * in wv_82593_cmd => config speedup - * - Stop receiving & power down on close (and power up on open) - * use "ifconfig down" & "ifconfig up ; route add -net ..." - * - Send packets : add watchdog instead of pooling - * - Receive : check frame wrap around & try to recover some frames - * - wavelan_set_multicast_list : avoid reset - * - add wireless extensions (ioctl & get_wireless_stats) - * get/set nwid/frequency on fly, info for /proc/net/wireless - * - Suppress useless stuff from lp (net_local), but add link - * - More inlines - * - Lot of others minor details & cleanups - * - * Changes made in second release : - * ------------------------------ - * - Optimise wv_85893_reconfig stuff, fix potential problems - * - Change error values for ioctl - * - Non blocking wv_ru_stop() + call wv_reset() in case of problems - * - Remove development printk from wavelan_watchdog() - * - Remove of the watchdog to wavelan_close instead of wavelan_release - * fix potential problems... - * - Start debugging suspend stuff (but it's still a bit weird) - * - Debug & optimize dump header/packet in Rx & Tx (debug) - * - Use "readb" and "writeb" to be kernel 2.1 compliant - * - Better handling of bogus interrupts - * - Wireless extension : SETSPY and GETSPY - * - Remove old stuff (stats - for those needing it, just ask me...) - * - Make wireless extensions optional - * - * Changes made in third release : - * ----------------------------- - * - cleanups & typos - * - modif wireless ext (spy -> only one pointer) - * - new private ioctl to set/get quality & level threshold - * - Init : correct default value of level threshold for pcmcia - * - kill watchdog in hw_reset - * - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) - * - Add message level (debug stuff in /var/adm/debug & errors not - * displayed at console and still in /var/adm/messages) - * - * Changes made in fourth release : - * ------------------------------ - * - multicast support (yes !) thanks to Yongguang Zhang. - * - * Changes made in fifth release (2.9.0) : - * ------------------------------------- - * - Revisited multicast code (it was mostly wrong). - * - protect code in wv_82593_reconfig with dev->tbusy (oups !) - * - * Changes made in sixth release (2.9.1a) : - * -------------------------------------- - * - Change the detection code for multi manufacturer code support - * - Correct bug (hang kernel) in init when we were "rejecting" a card - * - * Changes made in seventh release (2.9.1b) : - * ---------------------------------------- - * - Update to wireless extensions changes - * - Silly bug in card initial configuration (psa_conf_status) - * - * Changes made in eigth release : - * ----------------------------- - * - Small bug in debug code (probably not the last one...) - * - 1.2.13 support (thanks to Clark Woodworth) - * - * Changes made for release in 2.9.2b : - * ---------------------------------- - * - Level threshold is now a standard wireless extension (version 4 !) - * - modules parameters types for kernel > 2.1.17 - * - updated man page - * - Others cleanup from David Hinds - * - * Changes made for release in 2.9.5 : - * --------------------------------- - * - byte count stats (courtesy of David Hinds) - * - Remove dev_tint stuff (courtesy of David Hinds) - * - Others cleanup from David Hinds - * - Encryption setting from Brent Elphick (thanks a lot !) - * - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) - * - * Changes made for release in 2.9.6 : - * --------------------------------- - * - fix bug : no longuer disable watchdog in case of bogus interrupt - * - increase timeout in config code for picky hardware - * - mask unused bits in status (Wireless Extensions) - * - * Changes integrated by Justin Seger & David Hinds : - * ----------------------------------------------------------------- - * - Roaming "hack" from Joe Finney - * - PSA CRC code from Bob Gray - * - Better initialisation of the i82593 controller - * from Joseph K. O'Sullivan - * - * Changes made for release in 3.0.10 : - * ---------------------------------- - * - Fix eject "hang" of the driver under 2.2.X : - * o create wv_flush_stale_links() - * o Rename wavelan_release to wv_pcmcia_release & move up - * o move unregister_netdev to wavelan_detach() - * o wavelan_release() no longer call wavelan_detach() - * o Suppress "release" timer - * o Other cleanups & fixes - * - New MAC address in the probe - * - Reorg PSA_CRC code (endian neutral & cleaner) - * - Correct initialisation of the i82593 from Lucent manual - * - Put back the watchdog, with larger timeout - * - TRANSMIT_NO_CRC is a "normal" error, so recover from it - * from Derrick J Brashear - * - Better handling of TX and RX normal failure conditions - * - #ifdef out all the roaming code - * - Add ESSID & "AP current address" ioctl stubs - * - General cleanup of the code - * - * Changes made for release in 3.0.13 : - * ---------------------------------- - * - Re-enable compilation of roaming code by default, but with - * do_roaming = 0 - * - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather - * at the demand of John Carol Langford - * - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff. - * - * Changes made for release in 3.0.15 : - * ---------------------------------- - * - Change e-mail and web page addresses - * - Watchdog timer is now correctly expressed in HZ, not in jiffies - * - Add channel number to the list of frequencies in range - * - Add the (short) list of bit-rates in range - * - Developp a new sensitivity... (sens.value & sens.fixed) - * - * Changes made for release in 3.1.2 : - * --------------------------------- - * - Fix check for root permission (break instead of exit) - * - New nwid & encoding setting (Wireless Extension 9) - * - * Changes made for release in 3.1.12 : - * ---------------------------------- - * - reworked wv_82593_cmd to avoid using the IRQ handler and doing - * ugly things with interrupts. - * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog - * - Update to new network API (softnet - 2.3.43) : - * o replace dev->tbusy (David + me) - * o replace dev->tstart (David + me) - * o remove dev->interrupt (David) - * o add SMP locking via spinlock in splxx (me) - * o add spinlock in interrupt handler (me) - * o use kernel watchdog instead of ours (me) - * o verify that all the changes make sense and work (me) - * - Re-sync kernel/pcmcia versions (not much actually) - * - A few other cleanups (David & me)... - * - * Changes made for release in 3.1.22 : - * ---------------------------------- - * - Check that SMP works, remove annoying log message - * - * Changes made for release in 3.1.24 : - * ---------------------------------- - * - Fix unfrequent card lockup when watchdog was reseting the hardware : - * o control first busy loop in wv_82593_cmd() - * o Extend spinlock protection in wv_hw_config() - * - * Changes made for release in 3.1.33 : - * ---------------------------------- - * - Optional use new driver API for Wireless Extensions : - * o got rid of wavelan_ioctl() - * o use a bunch of iw_handler instead - * - * Changes made for release in 3.2.1 : - * --------------------------------- - * - Set dev->trans_start to avoid filling the logs - * (and generating useless abort commands) - * - Avoid deadlocks in mmc_out()/mmc_in() - * - * Wishes & dreams: - * ---------------- - * - Cleanup and integrate the roaming code - * (std debug, set DomainID, decay avg and co...) - */ - -/***************************** INCLUDES *****************************/ - -/* Linux headers that we need */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* Wireless extensions */ -#include /* New driver API */ - -/* Pcmcia headers that we need */ -#include -#include -#include -#include -#include - -/* Wavelan declarations */ -#include "i82593.h" /* Definitions for the Intel chip */ - -#include "wavelan_cs.h" /* Others bits of the hardware */ - -/************************** DRIVER OPTIONS **************************/ -/* - * `#define' or `#undef' the following constant to change the behaviour - * of the driver... - */ -#define WAVELAN_ROAMING /* Include experimental roaming code */ -#undef WAVELAN_ROAMING_EXT /* Enable roaming wireless extensions */ -#undef SET_PSA_CRC /* Set the CRC in PSA (slower) */ -#define USE_PSA_CONFIG /* Use info from the PSA */ -#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */ -#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */ -#undef SET_MAC_ADDRESS /* Experimental */ - -/* Warning : these stuff will slow down the driver... */ -#define WIRELESS_SPY /* Enable spying addresses */ -#undef HISTOGRAM /* Enable histogram of sig level... */ - -/****************************** DEBUG ******************************/ - -#undef DEBUG_MODULE_TRACE /* Module insertion/removal */ -#undef DEBUG_CALLBACK_TRACE /* Calls made by Linux */ -#undef DEBUG_INTERRUPT_TRACE /* Calls to handler */ -#undef DEBUG_INTERRUPT_INFO /* type of interrupt & so on */ -#define DEBUG_INTERRUPT_ERROR /* problems */ -#undef DEBUG_CONFIG_TRACE /* Trace the config functions */ -#undef DEBUG_CONFIG_INFO /* What's going on... */ -#define DEBUG_CONFIG_ERRORS /* Errors on configuration */ -#undef DEBUG_TX_TRACE /* Transmission calls */ -#undef DEBUG_TX_INFO /* Header of the transmitted packet */ -#undef DEBUG_TX_FAIL /* Normal failure conditions */ -#define DEBUG_TX_ERROR /* Unexpected conditions */ -#undef DEBUG_RX_TRACE /* Transmission calls */ -#undef DEBUG_RX_INFO /* Header of the transmitted packet */ -#undef DEBUG_RX_FAIL /* Normal failure conditions */ -#define DEBUG_RX_ERROR /* Unexpected conditions */ -#undef DEBUG_PACKET_DUMP /* Dump packet on the screen */ -#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */ -#undef DEBUG_IOCTL_INFO /* Various debug info */ -#define DEBUG_IOCTL_ERROR /* What's going wrong */ -#define DEBUG_BASIC_SHOW /* Show basic startup info */ -#undef DEBUG_VERSION_SHOW /* Print version info */ -#undef DEBUG_PSA_SHOW /* Dump psa to screen */ -#undef DEBUG_MMC_SHOW /* Dump mmc to screen */ -#undef DEBUG_SHOW_UNUSED /* Show also unused fields */ -#undef DEBUG_I82593_SHOW /* Show i82593 status */ -#undef DEBUG_DEVICE_SHOW /* Show device parameters */ - -/************************ CONSTANTS & MACROS ************************/ - -#ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan_cs.c : v24 (SMP + wireless extensions) 11/1/02\n"; -#endif - -/* Watchdog temporisation */ -#define WATCHDOG_JIFFIES (256*HZ/100) - -/* Fix a bug in some old wireless extension definitions */ -#ifndef IW_ESSID_MAX_SIZE -#define IW_ESSID_MAX_SIZE 32 -#endif - -/* ------------------------ PRIVATE IOCTL ------------------------ */ - -#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ -#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ -#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ -#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ - -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 4 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 5 /* Get histogram values */ - -/*************************** WaveLAN Roaming **************************/ -#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ - -#define WAVELAN_ROAMING_DEBUG 0 /* 1 = Trace of handover decisions */ - /* 2 = Info on each beacon rcvd... */ -#define MAX_WAVEPOINTS 7 /* Max visible at one time */ -#define WAVEPOINT_HISTORY 5 /* SNR sample history slow search */ -#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample history fast search */ -#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */ -#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */ -#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */ -#define CELL_TIMEOUT 2*HZ /* in jiffies */ - -#define FAST_CELL_SEARCH 1 /* Boolean values... */ -#define NWID_PROMISC 1 /* for code clarity. */ - -typedef struct wavepoint_beacon -{ - unsigned char dsap, /* Unused */ - ssap, /* Unused */ - ctrl, /* Unused */ - O,U,I, /* Unused */ - spec_id1, /* Unused */ - spec_id2, /* Unused */ - pdu_type, /* Unused */ - seq; /* WavePoint beacon sequence number */ - __be16 domain_id, /* WavePoint Domain ID */ - nwid; /* WavePoint NWID */ -} wavepoint_beacon; - -typedef struct wavepoint_history -{ - unsigned short nwid; /* WavePoint's NWID */ - int average_slow; /* SNR running average */ - int average_fast; /* SNR running average */ - unsigned char sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */ - unsigned char qualptr; /* Index into ringbuffer */ - unsigned char last_seq; /* Last seq. no seen for WavePoint */ - struct wavepoint_history *next; /* Next WavePoint in table */ - struct wavepoint_history *prev; /* Previous WavePoint in table */ - unsigned long last_seen; /* Time of last beacon recvd, jiffies */ -} wavepoint_history; - -struct wavepoint_table -{ - wavepoint_history *head; /* Start of ringbuffer */ - int num_wavepoints; /* No. of WavePoints visible */ - unsigned char locked; /* Table lock */ -}; - -#endif /* WAVELAN_ROAMING */ - -/****************************** TYPES ******************************/ - -/* Shortcuts */ -typedef struct iw_statistics iw_stats; -typedef struct iw_quality iw_qual; -typedef struct iw_freq iw_freq; -typedef struct net_local net_local; -typedef struct timer_list timer_list; - -/* Basic types */ -typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ - -/* - * Static specific data for the interface. - * - * For each network interface, Linux keep data in two structure. "device" - * keep the generic data (same format for everybody) and "net_local" keep - * the additional specific data. - */ -struct net_local -{ - dev_node_t node; /* ???? What is this stuff ???? */ - struct net_device * dev; /* Reverse link... */ - spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - struct pcmcia_device * link; /* pcmcia structure */ - int nresets; /* Number of hw resets */ - u_char configured; /* If it is configured */ - u_char reconfig_82593; /* Need to reconfigure the controller */ - u_char promiscuous; /* Promiscuous mode */ - u_char allmulticast; /* All Multicast mode */ - int mc_count; /* Number of multicast addresses */ - - int stop; /* Current i82593 Stop Hit Register */ - int rfp; /* Last DMA machine receive pointer */ - int overrunning; /* Receiver overrun flag */ - - iw_stats wstats; /* Wireless specific stats */ - - struct iw_spy_data spy_data; - struct iw_public_data wireless_data; - -#ifdef HISTOGRAM - int his_number; /* Number of intervals */ - u_char his_range[16]; /* Boundaries of interval ]n-1; n] */ - u_long his_sum[16]; /* Sum in interval */ -#endif /* HISTOGRAM */ -#ifdef WAVELAN_ROAMING - u_long domain_id; /* Domain ID we lock on for roaming */ - int filter_domains; /* Check Domain ID of beacon found */ - struct wavepoint_table wavepoint_table; /* Table of visible WavePoints*/ - wavepoint_history * curr_point; /* Current wavepoint */ - int cell_search; /* Searching for new cell? */ - struct timer_list cell_timer; /* Garbage collection */ -#endif /* WAVELAN_ROAMING */ - void __iomem *mem; -}; - -/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ -static inline u_char /* data */ - hasr_read(u_long); /* Read the host interface : base address */ -static void - hacr_write(u_long, /* Write to host interface : base address */ - u_char), /* data */ - hacr_write_slow(u_long, - u_char); -static void - psa_read(struct net_device *, /* Read the Parameter Storage Area */ - int, /* offset in PSA */ - u_char *, /* buffer to fill */ - int), /* size to read */ - psa_write(struct net_device *, /* Write to the PSA */ - int, /* Offset in psa */ - u_char *, /* Buffer in memory */ - int); /* Length of buffer */ -static void - mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */ - u_short, - u_char), - mmc_write(u_long, /* Write n bytes to the MMC */ - u_char, - u_char *, - int); -static u_char /* Read 1 byte from the MMC */ - mmc_in(u_long, - u_short); -static void - mmc_read(u_long, /* Read n bytes from the MMC */ - u_char, - u_char *, - int), - fee_wait(u_long, /* Wait for frequency EEprom : base address */ - int, /* Base delay to wait for */ - int); /* Number of time to wait */ -static void - fee_read(u_long, /* Read the frequency EEprom : base address */ - u_short, /* destination offset */ - u_short *, /* data buffer */ - int); /* number of registers */ -/* ---------------------- I82593 SUBROUTINES ----------------------- */ -static int - wv_82593_cmd(struct net_device *, /* synchronously send a command to i82593 */ - char *, - int, - int); -static inline int - wv_diag(struct net_device *); /* Diagnostique the i82593 */ -static int - read_ringbuf(struct net_device *, /* Read a receive buffer */ - int, - char *, - int); -static void - wv_82593_reconfig(struct net_device *); /* Reconfigure the controller */ -/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ -static void - wv_init_info(struct net_device *); /* display startup info */ -/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ -static iw_stats * - wavelan_get_wireless_stats(struct net_device *); -/* ----------------------- PACKET RECEPTION ----------------------- */ -static int - wv_start_of_frame(struct net_device *, /* Seek beggining of current frame */ - int, /* end of frame */ - int); /* start of buffer */ -static void - wv_packet_read(struct net_device *, /* Read a packet from a frame */ - int, - int), - wv_packet_rcv(struct net_device *); /* Read all packets waiting */ -/* --------------------- PACKET TRANSMISSION --------------------- */ -static void - wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer */ - void *, - short); -static netdev_tx_t - wavelan_packet_xmit(struct sk_buff *, /* Send a packet */ - struct net_device *); -/* -------------------- HARDWARE CONFIGURATION -------------------- */ -static int - wv_mmc_init(struct net_device *); /* Initialize the modem */ -static int - wv_ru_stop(struct net_device *), /* Stop the i82593 receiver unit */ - wv_ru_start(struct net_device *); /* Start the i82593 receiver unit */ -static int - wv_82593_config(struct net_device *); /* Configure the i82593 */ -static int - wv_pcmcia_reset(struct net_device *); /* Reset the pcmcia interface */ -static int - wv_hw_config(struct net_device *); /* Reset & configure the whole hardware */ -static void - wv_hw_reset(struct net_device *); /* Same, + start receiver unit */ -static int - wv_pcmcia_config(struct pcmcia_device *); /* Configure the pcmcia interface */ -static void - wv_pcmcia_release(struct pcmcia_device *);/* Remove a device */ -/* ---------------------- INTERRUPT HANDLING ---------------------- */ -static irqreturn_t - wavelan_interrupt(int, /* Interrupt handler */ - void *); -static void - wavelan_watchdog(struct net_device *); /* Transmission watchdog */ -/* ------------------- CONFIGURATION CALLBACKS ------------------- */ -static int - wavelan_open(struct net_device *), /* Open the device */ - wavelan_close(struct net_device *); /* Close the device */ -static void - wavelan_detach(struct pcmcia_device *p_dev); /* Destroy a removed device */ - -/**************************** VARIABLES ****************************/ - -/* - * Parameters that can be set with 'insmod' - * The exact syntax is 'insmod wavelan_cs.o =' - */ - -/* Shared memory speed, in ns */ -static int mem_speed = 0; - -/* New module interface */ -module_param(mem_speed, int, 0); - -#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ -/* Enable roaming mode ? No ! Please keep this to 0 */ -static int do_roaming = 0; -module_param(do_roaming, bool, 0); -#endif /* WAVELAN_ROAMING */ - -MODULE_LICENSE("GPL"); - -#endif /* WAVELAN_CS_P_H */ - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index ca946ca3823b..c1082f20534c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -135,5 +135,7 @@ source "drivers/staging/strip/Kconfig" source "drivers/staging/arlan/Kconfig" +source "drivers/staging/wavelan/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ed92324178bd..7bccdd3e7fab 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -49,3 +49,5 @@ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_COWLOOP) += cowloop/ obj-$(CONFIG_STRIP) += strip/ obj-$(CONFIG_ARLAN) += arlan/ +obj-$(CONFIG_WAVELAN) += wavelan/ +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan/ diff --git a/drivers/staging/wavelan/Kconfig b/drivers/staging/wavelan/Kconfig new file mode 100644 index 000000000000..786060e025c0 --- /dev/null +++ b/drivers/staging/wavelan/Kconfig @@ -0,0 +1,38 @@ +config WAVELAN + tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" + depends on ISA + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + ---help--- + The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is + a Radio LAN (wireless Ethernet-like Local Area Network) using the + radio frequencies 900 MHz and 2.4 GHz. + + If you want to use an ISA WaveLAN card under Linux, say Y and read + the Ethernet-HOWTO, available from + . Some more specific + information is contained in + and in the source code + . + + You will also need the wireless tools package available from + . + Please read the man pages contained therein. + + To compile this driver as a module, choose M here: the module will be + called wavelan. + +config PCMCIA_WAVELAN + tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" + depends on PCMCIA + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + help + Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA + (PC-card) wireless Ethernet networking card to your computer. This + driver is for the non-IEEE-802.11 Wavelan cards. + + To compile this driver as a module, choose M here: the module will be + called wavelan_cs. If unsure, say N. diff --git a/drivers/staging/wavelan/Makefile b/drivers/staging/wavelan/Makefile new file mode 100644 index 000000000000..1cde17c69a43 --- /dev/null +++ b/drivers/staging/wavelan/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_WAVELAN) += wavelan.o +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o diff --git a/drivers/staging/wavelan/i82586.h b/drivers/staging/wavelan/i82586.h new file mode 100644 index 000000000000..5f65b250646f --- /dev/null +++ b/drivers/staging/wavelan/i82586.h @@ -0,0 +1,413 @@ +/* + * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor. + * + * See: + * Intel Microcommunications 1991 + * p1-1 to p1-37 + * Intel order No. 231658 + * ISBN 1-55512-119-5 + * + * Unfortunately, the above chapter mentions neither + * the System Configuration Pointer (SCP) nor the + * Intermediate System Configuration Pointer (ISCP), + * so we probably need to look elsewhere for the + * whole story -- some recommend the "Intel LAN + * Components manual" but I have neither a copy + * nor a full reference. But "elsewhere" may be + * in the same publication... + * The description of a later device, the + * "82596CA High-Performance 32-Bit Local Area Network + * Coprocessor", (ibid. p1-38 to p1-109) does mention + * the SCP and ISCP and also has an i82586 compatibility + * mode. Even more useful is "AP-235 An 82586 Data Link + * Driver" (ibid. p1-337 to p1-417). + */ + +#define I82586_MEMZ (64 * 1024) + +#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t)) + +#define ADDR_LEN 6 +#define I82586NULL 0xFFFF + +#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * System Configuration Pointer (SCP). + */ +typedef struct scp_t scp_t; +struct scp_t +{ + unsigned short scp_sysbus; /* 82586 bus width: */ +#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */ +#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */ + unsigned short scp_junk[2]; /* Unused */ + unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */ + unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */ +}; + +/* + * Intermediate System Configuration Pointer (ISCP). + */ +typedef struct iscp_t iscp_t; +struct iscp_t +{ + unsigned short iscp_busy; /* set by CPU before first CA, */ + /* cleared by 82586 after read. */ + unsigned short iscp_offset; /* offset of SCB */ + unsigned short iscp_basel; /* base of SCB */ + unsigned short iscp_baseh; /* " */ +}; + +/* + * System Control Block (SCB). + * The 82586 writes its status to scb_status and then + * raises an interrupt to alert the CPU. + * The CPU writes a command to scb_command and + * then issues a Channel Attention (CA) to alert the 82586. + */ +typedef struct scb_t scb_t; +struct scb_t +{ + unsigned short scb_status; /* Status of 82586 */ +#define SCB_ST_INT (0xF << 12) /* Some of: */ +#define SCB_ST_CX (0x1 << 15) /* Cmd completed */ +#define SCB_ST_FR (0x1 << 14) /* Frame received */ +#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */ +#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */ +#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */ +#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */ +#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */ +#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */ +#define SCB_ST_CUS_ACTV (2 << 8) /* Active */ +#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */ +#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */ +#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */ +#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */ +#define SCB_ST_RUS_NRES (2 << 4) /* No resources */ +#define SCB_ST_RUS_RDY (4 << 4) /* Ready */ + unsigned short scb_command; /* Next command */ +#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */ +#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */ +#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */ +#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */ +#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */ +#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */ +#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */ +#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */ +#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */ +#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */ +#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */ +#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */ +#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */ +#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */ +#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */ +#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */ +#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */ +#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */ + unsigned short scb_cbl_offset; /* Offset of first command unit */ + /* Action Command */ + unsigned short scb_rfa_offset; /* Offset of first Receive */ + /* Frame Descriptor in the */ + /* Receive Frame Area */ + unsigned short scb_crcerrs; /* Properly aligned frames */ + /* received with a CRC error */ + unsigned short scb_alnerrs; /* Misaligned frames received */ + /* with a CRC error */ + unsigned short scb_rscerrs; /* Frames lost due to no space */ + unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */ +}; + +#define scboff(p,f) toff(scb_t, p, f) + +/* + * The eight Action Commands. + */ +typedef enum acmd_e acmd_e; +enum acmd_e +{ + acmd_nop = 0, /* Do nothing */ + acmd_ia_setup = 1, /* Load an (ethernet) address into the */ + /* 82586 */ + acmd_configure = 2, /* Update the 82586 operating parameters */ + acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */ + /* addresses into the 82586 */ + acmd_transmit = 4, /* Transmit a frame */ + acmd_tdr = 5, /* Perform a Time Domain Reflectometer */ + /* test on the serial link */ + acmd_dump = 6, /* Copy 82586 registers to memory */ + acmd_diagnose = 7, /* Run an internal self test */ +}; + +/* + * Generic Action Command header. + */ +typedef struct ach_t ach_t; +struct ach_t +{ + unsigned short ac_status; /* Command status: */ +#define AC_SFLD_C (0x1 << 15) /* Command completed */ +#define AC_SFLD_B (0x1 << 14) /* Busy executing */ +#define AC_SFLD_OK (0x1 << 13) /* Completed error free */ +#define AC_SFLD_A (0x1 << 12) /* Command aborted */ +#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */ +#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */ + /* during transmission */ +#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */ + /* (stopped) lost CTS */ +#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */ + /* (stopped) slow DMA */ +#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */ + /* other link traffic */ +#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */ + /* detect after last tx */ +#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */ + /* excessive collisions */ +#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */ + unsigned short ac_command; /* Command specifier: */ +#define AC_CFLD_EL (0x1 << 15) /* End of command list */ +#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */ +#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */ +#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */ + unsigned short ac_link; /* Next Action Command */ +}; + +#define acoff(p,f) toff(ach_t, p, f) + +/* + * The Nop Action Command. + */ +typedef struct ac_nop_t ac_nop_t; +struct ac_nop_t +{ + ach_t nop_h; +}; + +/* + * The IA-Setup Action Command. + */ +typedef struct ac_ias_t ac_ias_t; +struct ac_ias_t +{ + ach_t ias_h; + unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */ +}; + +/* + * The Configure Action Command. + */ +typedef struct ac_cfg_t ac_cfg_t; +struct ac_cfg_t +{ + ach_t cfg_h; + unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */ +#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0) + unsigned char cfg_fifolim; /* FIFO threshold */ +#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0) + unsigned char cfg_byte8; +#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */ +#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */ + /* external sync. */ + unsigned char cfg_byte9; +#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */ +#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */ +#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */ +#define AC_CFG_PLEN_2 0 /* 2 bytes */ +#define AC_CFG_PLEN_4 1 /* 4 bytes */ +#define AC_CFG_PLEN_8 2 /* 8 bytes */ +#define AC_CFG_PLEN_16 3 /* 16 bytes */ +#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */ + /* explicit in buffers */ +#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */ + unsigned char cfg_byte10; +#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */ + /* backoff method */ +#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */ +#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */ + unsigned char cfg_ifs; /* Interframe spacing */ + unsigned char cfg_slotl; /* Slot time (low byte) */ + unsigned char cfg_byte13; +#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */ +#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */ + unsigned char cfg_byte14; +#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */ +#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */ +#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */ +#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */ +#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */ +#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */ +#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */ +#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */ + unsigned char cfg_byte15; +#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */ + /* detect source */ +#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */ + /* filter in bit times */ +#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */ + /* sense source */ +#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */ + /* filter in bit times */ + unsigned short cfg_min_frm_len; +#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */ +}; + +/* + * The MC-Setup Action Command. + */ +typedef struct ac_mcs_t ac_mcs_t; +struct ac_mcs_t +{ + ach_t mcs_h; + unsigned short mcs_cnt; /* No. of bytes of MC addresses */ +#if 0 + unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */ + ... +#endif +}; + +#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ + +/* + * The Transmit Action Command. + */ +typedef struct ac_tx_t ac_tx_t; +struct ac_tx_t +{ + ach_t tx_h; + unsigned short tx_tbd_offset; /* Address of list of buffers. */ +#if 0 +Linux packets are passed down with the destination MAC address +and length/type field already prepended to the data, +so we do not need to insert it. Consistent with this +we must also set the AC_CFG_ALOC(..) flag during the +ac_cfg_t action command. + unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */ + unsigned short tx_length; /* The frame length */ +#endif /* 0 */ +}; + +/* + * The Time Domain Reflectometer Action Command. + */ +typedef struct ac_tdr_t ac_tdr_t; +struct ac_tdr_t +{ + ach_t tdr_h; + unsigned short tdr_result; /* Result. */ +#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */ +#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */ +#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */ +#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */ +#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */ + /* site in transmit */ + /* clock cycles */ +}; + +/* + * The Dump Action Command. + */ +typedef struct ac_dmp_t ac_dmp_t; +struct ac_dmp_t +{ + ach_t dmp_h; + unsigned short dmp_offset; /* Result. */ +}; + +/* + * Size of the result of the dump command. + */ +#define DUMPBYTES 170 + +/* + * The Diagnose Action Command. + */ +typedef struct ac_dgn_t ac_dgn_t; +struct ac_dgn_t +{ + ach_t dgn_h; +}; + +/* + * Transmit Buffer Descriptor (TBD). + */ +typedef struct tbd_t tbd_t; +struct tbd_t +{ + unsigned short tbd_status; /* Written by the CPU */ +#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */ + /* last for this frame */ +#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */ + /* bytes in this buffer */ + unsigned short tbd_next_bd_offset; /* Next in list */ + unsigned short tbd_bufl; /* Buffer address (low) */ + unsigned short tbd_bufh; /* " " (high) */ +}; + +/* + * Receive Buffer Descriptor (RBD). + */ +typedef struct rbd_t rbd_t; +struct rbd_t +{ + unsigned short rbd_status; /* Written by the 82586 */ +#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */ + /* last for this frame */ +#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */ +#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */ + /* bytes in this buffer */ + unsigned short rbd_next_rbd_offset; /* Next rbd in list */ + unsigned short rbd_bufl; /* Data pointer (low) */ + unsigned short rbd_bufh; /* " " (high) */ + unsigned short rbd_el_size; /* EL+Data buf. size */ +#define RBD_EL (0x1 << 15) /* This BD is the */ + /* last in the list */ +#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */ + /* buffer can hold */ +}; + +#define rbdoff(p,f) toff(rbd_t, p, f) + +/* + * Frame Descriptor (FD). + */ +typedef struct fd_t fd_t; +struct fd_t +{ + unsigned short fd_status; /* Written by the 82586 */ +#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */ +#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */ +#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */ +#define FD_STATUS_S11 (0x1 << 11) /* CRC error */ +#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */ +#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */ +#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */ +#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */ +#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */ + unsigned short fd_command; /* Command */ +#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */ +#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */ + unsigned short fd_link_offset; /* Next FD */ + unsigned short fd_rbd_offset; /* First RBD (data) */ + /* Prepared by CPU, */ + /* updated by 82586 */ +#if 0 +I think the rest is unused since we +have set AC_CFG_ALOC(..). However, just +in case, we leave the space. +#endif /* 0 */ + unsigned char fd_dest[ADDR_LEN]; /* Destination address */ + /* Written by 82586 */ + unsigned char fd_src[ADDR_LEN]; /* Source address */ + /* Written by 82586 */ + unsigned short fd_length; /* Frame length or type */ + /* Written by 82586 */ +}; + +#define fdoff(p,f) toff(fd_t, p, f) + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * For more details, see wavelan.c. + */ diff --git a/drivers/staging/wavelan/i82593.h b/drivers/staging/wavelan/i82593.h new file mode 100644 index 000000000000..afac5c7a323d --- /dev/null +++ b/drivers/staging/wavelan/i82593.h @@ -0,0 +1,229 @@ +/* + * Definitions for Intel 82593 CSMA/CD Core LAN Controller + * The definitions are taken from the 1992 users manual with Intel + * order number 297125-001. + * + * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp + * + * Copyright 1994, Anders Klemets + * + * HISTORY + * i82593.h,v + * Revision 1.4 2005/11/4 09:15:00 baroniunas + * Modified copyright with permission of author as follows: + * + * "If I82539.H is the only file with my copyright statement + * that is included in the Source Forge project, then you have + * my approval to change the copyright statement to be a GPL + * license, in the way you proposed on October 10." + * + * Revision 1.1 1996/07/17 15:23:12 root + * Initial revision + * + * Revision 1.3 1995/04/05 15:13:58 adj + * Initial alpha release + * + * Revision 1.2 1994/06/16 23:57:31 klemets + * Mirrored all the fields in the configuration block. + * + * Revision 1.1 1994/06/02 20:25:34 klemets + * Initial revision + * + * + */ +#ifndef _I82593_H +#define _I82593_H + +/* Intel 82593 CSMA/CD Core LAN Controller */ + +/* Port 0 Command Register definitions */ + +/* Execution operations */ +#define OP0_NOP 0 /* CHNL = 0 */ +#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ +#define OP0_IA_SETUP 1 +#define OP0_CONFIGURE 2 +#define OP0_MC_SETUP 3 +#define OP0_TRANSMIT 4 +#define OP0_TDR 5 +#define OP0_DUMP 6 +#define OP0_DIAGNOSE 7 +#define OP0_TRANSMIT_NO_CRC 9 +#define OP0_RETRANSMIT 12 +#define OP0_ABORT 13 +/* Reception operations */ +#define OP0_RCV_ENABLE 8 +#define OP0_RCV_DISABLE 10 +#define OP0_STOP_RCV 11 +/* Status pointer control operations */ +#define OP0_FIX_PTR 15 /* CHNL = 1 */ +#define OP0_RLS_PTR 15 /* CHNL = 0 */ +#define OP0_RESET 14 + +#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ +#define CR0_STATUS_0 0x00 +#define CR0_STATUS_1 0x20 +#define CR0_STATUS_2 0x40 +#define CR0_STATUS_3 0x60 +#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ + +/* Port 0 Status Register definitions */ + +#define SR0_NO_RESULT 0 /* dummy */ +#define SR0_EVENT_MASK 0x0f +#define SR0_IA_SETUP_DONE 1 +#define SR0_CONFIGURE_DONE 2 +#define SR0_MC_SETUP_DONE 3 +#define SR0_TRANSMIT_DONE 4 +#define SR0_TDR_DONE 5 +#define SR0_DUMP_DONE 6 +#define SR0_DIAGNOSE_PASSED 7 +#define SR0_TRANSMIT_NO_CRC_DONE 9 +#define SR0_RETRANSMIT_DONE 12 +#define SR0_EXECUTION_ABORTED 13 +#define SR0_END_OF_FRAME 8 +#define SR0_RECEPTION_ABORTED 10 +#define SR0_DIAGNOSE_FAILED 15 +#define SR0_STOP_REG_HIT 11 + +#define SR0_CHNL (1 << 4) +#define SR0_EXECUTION (1 << 5) +#define SR0_RECEPTION (1 << 6) +#define SR0_INTERRUPT (1 << 7) +#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) + +#define SR3_EXEC_STATE_MASK 0x03 +#define SR3_EXEC_IDLE 0 +#define SR3_TX_ABORT_IN_PROGRESS 1 +#define SR3_EXEC_ACTIVE 2 +#define SR3_ABORT_IN_PROGRESS 3 +#define SR3_EXEC_CHNL (1 << 2) +#define SR3_STP_ON_NO_RSRC (1 << 3) +#define SR3_RCVING_NO_RSRC (1 << 4) +#define SR3_RCV_STATE_MASK 0x60 +#define SR3_RCV_IDLE 0x00 +#define SR3_RCV_READY 0x20 +#define SR3_RCV_ACTIVE 0x40 +#define SR3_RCV_STOP_IN_PROG 0x60 +#define SR3_RCV_CHNL (1 << 7) + +/* Port 1 Command Register definitions */ + +#define OP1_NOP 0 +#define OP1_SWIT_TO_PORT_0 1 +#define OP1_INT_DISABLE 2 +#define OP1_INT_ENABLE 3 +#define OP1_SET_TS 5 +#define OP1_RST_TS 7 +#define OP1_POWER_DOWN 8 +#define OP1_RESET_RING_MNGMT 11 +#define OP1_RESET 14 +#define OP1_SEL_RST 15 + +#define CR1_STATUS_4 0x00 +#define CR1_STATUS_5 0x20 +#define CR1_STATUS_6 0x40 +#define CR1_STOP_REG_UPDATE (1 << 7) + +/* Receive frame status bits */ + +#define RX_RCLD (1 << 0) +#define RX_IA_MATCH (1 << 1) +#define RX_NO_AD_MATCH (1 << 2) +#define RX_NO_SFD (1 << 3) +#define RX_SRT_FRM (1 << 7) +#define RX_OVRRUN (1 << 8) +#define RX_ALG_ERR (1 << 10) +#define RX_CRC_ERR (1 << 11) +#define RX_LEN_ERR (1 << 12) +#define RX_RCV_OK (1 << 13) +#define RX_TYP_LEN (1 << 15) + +/* Transmit status bits */ + +#define TX_NCOL_MASK 0x0f +#define TX_FRTL (1 << 4) +#define TX_MAX_COL (1 << 5) +#define TX_HRT_BEAT (1 << 6) +#define TX_DEFER (1 << 7) +#define TX_UND_RUN (1 << 8) +#define TX_LOST_CTS (1 << 9) +#define TX_LOST_CRS (1 << 10) +#define TX_LTCOL (1 << 11) +#define TX_OK (1 << 13) +#define TX_COLL (1 << 15) + +struct i82593_conf_block { + u_char fifo_limit : 4, + forgnesi : 1, + fifo_32 : 1, + d6mod : 1, + throttle_enb : 1; + u_char throttle : 6, + cntrxint : 1, + contin : 1; + u_char addr_len : 3, + acloc : 1, + preamb_len : 2, + loopback : 2; + u_char lin_prio : 3, + tbofstop : 1, + exp_prio : 3, + bof_met : 1; + u_char : 4, + ifrm_spc : 4; + u_char : 5, + slottim_low : 3; + u_char slottim_hi : 3, + : 1, + max_retr : 4; + u_char prmisc : 1, + bc_dis : 1, + : 1, + crs_1 : 1, + nocrc_ins : 1, + crc_1632 : 1, + : 1, + crs_cdt : 1; + u_char cs_filter : 3, + crs_src : 1, + cd_filter : 3, + : 1; + u_char : 2, + min_fr_len : 6; + u_char lng_typ : 1, + lng_fld : 1, + rxcrc_xf : 1, + artx : 1, + sarec : 1, + tx_jabber : 1, /* why is this called max_len in the manual? */ + hash_1 : 1, + lbpkpol : 1; + u_char : 6, + fdx : 1, + : 1; + u_char dummy_6 : 6, /* supposed to be ones */ + mult_ia : 1, + dis_bof : 1; + u_char dummy_1 : 1, /* supposed to be one */ + tx_ifs_retrig : 2, + mc_all : 1, + rcv_mon : 2, + frag_acpt : 1, + tstrttrs : 1; + u_char fretx : 1, + runt_eop : 1, + hw_sw_pin : 1, + big_endn : 1, + syncrqs : 1, + sttlen : 1, + tx_eop : 1, + rx_eop : 1; + u_char rbuf_size : 5, + rcvstop : 1, + : 2; +}; + +#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ + +#endif /* _I82593_H */ diff --git a/drivers/staging/wavelan/wavelan.c b/drivers/staging/wavelan/wavelan.c new file mode 100644 index 000000000000..d634b2da3b84 --- /dev/null +++ b/drivers/staging/wavelan/wavelan.c @@ -0,0 +1,4383 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follows (also see the end of this file). + * See wavelan.p.h for details. + * + * + * + * AT&T GIS (nee NCR) WaveLAN card: + * An Ethernet-like radio transceiver + * controlled by an Intel 82586 coprocessor. + */ + +#include "wavelan.p.h" /* Private header */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (WaveLAN modem or i82586) + */ + +/*------------------------------------------------------------------*/ +/* + * Translate irq number to PSA irq parameter + */ +static u8 wv_irq_to_psa(int irq) +{ + if (irq < 0 || irq >= ARRAY_SIZE(irqvals)) + return 0; + + return irqvals[irq]; +} + +/*------------------------------------------------------------------*/ +/* + * Translate PSA irq parameter to irq number + */ +static int __init wv_psa_to_irq(u8 irqval) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(irqvals); i++) + if (irqvals[i] == irqval) + return i; + + return -1; +} + +/********************* HOST ADAPTER SUBROUTINES *********************/ +/* + * Useful subroutines to manage the WaveLAN ISA interface + * + * One major difference with the PCMCIA hardware (except the port mapping) + * is that we have to keep the state of the Host Control Register + * because of the interrupt enable & bus size flags. + */ + +/*------------------------------------------------------------------*/ +/* + * Read from card's Host Adaptor Status Register. + */ +static inline u16 hasr_read(unsigned long ioaddr) +{ + return (inw(HASR(ioaddr))); +} /* hasr_read */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. + */ +static inline void hacr_write(unsigned long ioaddr, u16 hacr) +{ + outw(hacr, HACR(ioaddr)); +} /* hacr_write */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. Include a delay for + * those times when it is needed. + */ +static void hacr_write_slow(unsigned long ioaddr, u16 hacr) +{ + hacr_write(ioaddr, hacr); + /* delay might only be needed sometimes */ + mdelay(1); +} /* hacr_write_slow */ + +/*------------------------------------------------------------------*/ +/* + * Set the channel attention bit. + */ +static inline void set_chan_attn(unsigned long ioaddr, u16 hacr) +{ + hacr_write(ioaddr, hacr | HACR_CA); +} /* set_chan_attn */ + +/*------------------------------------------------------------------*/ +/* + * Reset, and then set host adaptor into default mode. + */ +static inline void wv_hacr_reset(unsigned long ioaddr) +{ + hacr_write_slow(ioaddr, HACR_RESET); + hacr_write(ioaddr, HACR_DEFAULT); +} /* wv_hacr_reset */ + +/*------------------------------------------------------------------*/ +/* + * Set the I/O transfer over the ISA bus to 8-bit mode + */ +static inline void wv_16_off(unsigned long ioaddr, u16 hacr) +{ + hacr &= ~HACR_16BITS; + hacr_write(ioaddr, hacr); +} /* wv_16_off */ + +/*------------------------------------------------------------------*/ +/* + * Set the I/O transfer over the ISA bus to 8-bit mode + */ +static inline void wv_16_on(unsigned long ioaddr, u16 hacr) +{ + hacr |= HACR_16BITS; + hacr_write(ioaddr, hacr); +} /* wv_16_on */ + +/*------------------------------------------------------------------*/ +/* + * Disable interrupts on the WaveLAN hardware. + * (called by wv_82586_stop()) + */ +static inline void wv_ints_off(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + lp->hacr &= ~HACR_INTRON; + hacr_write(ioaddr, lp->hacr); +} /* wv_ints_off */ + +/*------------------------------------------------------------------*/ +/* + * Enable interrupts on the WaveLAN hardware. + * (called by wv_hw_reset()) + */ +static inline void wv_ints_on(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + lp->hacr |= HACR_INTRON; + hacr_write(ioaddr, lp->hacr); +} /* wv_ints_on */ + +/******************* MODEM MANAGEMENT SUBROUTINES *******************/ +/* + * Useful subroutines to manage the modem of the WaveLAN + */ + +/*------------------------------------------------------------------*/ +/* + * Read the Parameter Storage Area from the WaveLAN card's memory + */ +/* + * Read bytes from the PSA. + */ +static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */ + u8 * b, /* buffer to fill */ + int n) +{ /* size to read */ + wv_16_off(ioaddr, hacr); + + while (n-- > 0) { + outw(o, PIOR2(ioaddr)); + o++; + *b++ = inb(PIOP2(ioaddr)); + } + + wv_16_on(ioaddr, hacr); +} /* psa_read */ + +/*------------------------------------------------------------------*/ +/* + * Write the Parameter Storage Area to the WaveLAN card's memory. + */ +static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */ + u8 * b, /* Buffer in memory */ + int n) +{ /* Length of buffer */ + int count = 0; + + wv_16_off(ioaddr, hacr); + + while (n-- > 0) { + outw(o, PIOR2(ioaddr)); + o++; + + outb(*b, PIOP2(ioaddr)); + b++; + + /* Wait for the memory to finish its write cycle */ + count = 0; + while ((count++ < 100) && + (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1); + } + + wv_16_on(ioaddr, hacr); +} /* psa_write */ + +#ifdef SET_PSA_CRC +/*------------------------------------------------------------------*/ +/* + * Calculate the PSA CRC + * Thanks to Valster, Nico for the code + * NOTE: By specifying a length including the CRC position the + * returned value should be zero. (i.e. a correct checksum in the PSA) + * + * The Windows drivers don't use the CRC, but the AP and the PtP tool + * depend on it. + */ +static u16 psa_crc(u8 * psa, /* The PSA */ + int size) +{ /* Number of short for CRC */ + int byte_cnt; /* Loop on the PSA */ + u16 crc_bytes = 0; /* Data in the PSA */ + int bit_cnt; /* Loop on the bits of the short */ + + for (byte_cnt = 0; byte_cnt < size; byte_cnt++) { + crc_bytes ^= psa[byte_cnt]; /* Its an xor */ + + for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) { + if (crc_bytes & 0x0001) + crc_bytes = (crc_bytes >> 1) ^ 0xA001; + else + crc_bytes >>= 1; + } + } + + return crc_bytes; +} /* psa_crc */ +#endif /* SET_PSA_CRC */ + +/*------------------------------------------------------------------*/ +/* + * update the checksum field in the Wavelan's PSA + */ +static void update_psa_checksum(struct net_device * dev, unsigned long ioaddr, u16 hacr) +{ +#ifdef SET_PSA_CRC + psa_t psa; + u16 crc; + + /* read the parameter storage area */ + psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa)); + + /* update the checksum */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc[0]) - + sizeof(psa.psa_crc[1]) + - sizeof(psa.psa_crc_status)); + + psa.psa_crc[0] = crc & 0xFF; + psa.psa_crc[1] = (crc & 0xFF00) >> 8; + + /* Write it ! */ + psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa, + (unsigned char *) &psa.psa_crc, 2); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", + dev->name, psa.psa_crc[0], psa.psa_crc[1]); + + /* Check again (luxury !) */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc_status)); + + if (crc != 0) + printk(KERN_WARNING + "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", + dev->name); +#endif /* DEBUG_IOCTL_INFO */ +#endif /* SET_PSA_CRC */ +} /* update_psa_checksum */ + +/*------------------------------------------------------------------*/ +/* + * Write 1 byte to the MMC. + */ +static void mmc_out(unsigned long ioaddr, u16 o, u8 d) +{ + int count = 0; + + /* Wait for MMC to go idle */ + while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) + udelay(10); + + outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr)); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to write bytes to the Modem Management Controller. + * We start at the end because it is the way it should be! + */ +static void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n) +{ + o += n; + b += n; + + while (n-- > 0) + mmc_out(ioaddr, --o, *(--b)); +} /* mmc_write */ + +/*------------------------------------------------------------------*/ +/* + * Read a byte from the MMC. + * Optimised version for 1 byte, avoid using memory. + */ +static u8 mmc_in(unsigned long ioaddr, u16 o) +{ + int count = 0; + + while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) + udelay(10); + outw(o << 1, MMCR(ioaddr)); + + while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY)) + udelay(10); + return (u8) (inw(MMCR(ioaddr)) >> 8); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to read bytes from the Modem Management Controller. + * The implementation is complicated by a lack of address lines, + * which prevents decoding of the low-order bit. + * (code has just been moved in the above function) + * We start at the end because it is the way it should be! + */ +static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n) +{ + o += n; + b += n; + + while (n-- > 0) + *(--b) = mmc_in(ioaddr, --o); +} /* mmc_read */ + +/*------------------------------------------------------------------*/ +/* + * Get the type of encryption available. + */ +static inline int mmc_encr(unsigned long ioaddr) +{ /* I/O port of the card */ + int temp; + + temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail)); + if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) + return 0; + else + return temp; +} + +/*------------------------------------------------------------------*/ +/* + * Wait for the frequency EEPROM to complete a command. + * I hope this one will be optimally inlined. + */ +static inline void fee_wait(unsigned long ioaddr, /* I/O port of the card */ + int delay, /* Base delay to wait for */ + int number) +{ /* Number of time to wait */ + int count = 0; /* Wait only a limited time */ + + while ((count++ < number) && + (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + MMR_FEE_STATUS_BUSY)) udelay(delay); +} + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the Frequency EEPROM (frequency select cards). + */ +static void fee_read(unsigned long ioaddr, /* I/O port of the card */ + u16 o, /* destination offset */ + u16 * b, /* data buffer */ + int n) +{ /* number of registers */ + b += n; /* Position at the end of the area */ + + /* Write the address */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while (n-- > 0) { + /* Write the read command */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ); + + /* Wait until EEPROM is ready (should be quick). */ + fee_wait(ioaddr, 10, 100); + + /* Read the value. */ + *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) | + mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); + } +} + + +/*------------------------------------------------------------------*/ +/* + * Write bytes from the Frequency EEPROM (frequency select cards). + * This is a bit complicated, because the frequency EEPROM has to + * be unprotected and the write enabled. + * Jean II + */ +static void fee_write(unsigned long ioaddr, /* I/O port of the card */ + u16 o, /* destination offset */ + u16 * b, /* data buffer */ + int n) +{ /* number of registers */ + b += n; /* Position at the end of the area. */ + +#ifdef EEPROM_IS_PROTECTED /* disabled */ +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* Ask to read the protected register */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); + + fee_wait(ioaddr, 10, 100); + + /* Read the protected register. */ + printk("Protected 2: %02X-%02X\n", + mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)), + mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); +#endif /* DOESNT_SEEM_TO_WORK */ + + /* Enable protected register. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); + + fee_wait(ioaddr, 10, 100); + + /* Unprotect area. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* or use: */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); +#endif /* DOESNT_SEEM_TO_WORK */ + + fee_wait(ioaddr, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ + + /* Write enable. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); + + fee_wait(ioaddr, 10, 100); + + /* Write the EEPROM address. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while (n-- > 0) { + /* Write the value. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); + mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF); + + /* Write the write command. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_WRITE); + + /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */ + mdelay(10); + fee_wait(ioaddr, 10, 100); + } + + /* Write disable. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); + + fee_wait(ioaddr, 10, 100); + +#ifdef EEPROM_IS_PROTECTED /* disabled */ + /* Reprotect EEPROM. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); + + fee_wait(ioaddr, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ +} + +/************************ I82586 SUBROUTINES *************************/ +/* + * Useful subroutines to manage the Ethernet controller + */ + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the on-board RAM. + * Why does inlining this function make it fail? + */ +static /*inline */ void obram_read(unsigned long ioaddr, + u16 o, u8 * b, int n) +{ + outw(o, PIOR1(ioaddr)); + insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Write bytes to the on-board RAM. + */ +static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n) +{ + outw(o, PIOR1(ioaddr)); + outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Acknowledge the reading of the status issued by the i82586. + */ +static void wv_ack(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + u16 scb_cs; + int i; + + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + scb_cs &= SCB_ST_INT; + + if (scb_cs == 0) + return; + + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + udelay(100); + +#ifdef DEBUG_CONFIG_ERROR + if (i <= 0) + printk(KERN_INFO + "%s: wv_ack(): board not accepting command.\n", + dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Set channel attention bit and busy wait until command has + * completed, then acknowledge completion of the command. + */ +static int wv_synchronous_cmd(struct net_device * dev, const char *str) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + u16 scb_cmd; + ach_t cb; + int i; + + scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cmd, sizeof(scb_cmd)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, + sizeof(cb)); + if (cb.ac_status & AC_SFLD_C) + break; + + udelay(10); + } + udelay(100); + + if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "%s: %s failed; status = 0x%x\n", + dev->name, str, cb.ac_status); +#endif +#ifdef DEBUG_I82586_SHOW + wv_scb_show(ioaddr); +#endif + return -1; + } + + /* Ack the status */ + wv_ack(dev); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Configuration commands completion interrupt. + * Check if done, and if OK. + */ +static int +wv_config_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp) +{ + unsigned short mcs_addr; + unsigned short status; + int ret; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name); +#endif + + mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t) + + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t); + + /* Read the status of the last command (set mc list). */ + obram_read(ioaddr, acoff(mcs_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + + /* If not completed -> exit */ + if ((status & AC_SFLD_C) == 0) + ret = 0; /* Not ready to be scrapped */ + else { +#ifdef DEBUG_CONFIG_ERROR + unsigned short cfg_addr; + unsigned short ias_addr; + + /* Check mc_config command */ + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n", + dev->name, status); + + /* check ia-config command */ + ias_addr = mcs_addr - sizeof(ac_ias_t); + obram_read(ioaddr, acoff(ias_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n", + dev->name, status); + + /* Check config command. */ + cfg_addr = ias_addr - sizeof(ac_cfg_t); + obram_read(ioaddr, acoff(cfg_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): configure failed; status = 0x%x\n", + dev->name, status); +#endif /* DEBUG_CONFIG_ERROR */ + + ret = 1; /* Ready to be scrapped */ + } + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, + ret); +#endif + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Command completion interrupt. + * Reclaim as many freed tx buffers as we can. + * (called in wavelan_interrupt()). + * Note : the spinlock is already grabbed for us. + */ +static int wv_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp) +{ + int nreaped = 0; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name); +#endif + + /* Loop on all the transmit buffers */ + while (lp->tx_first_in_use != I82586NULL) { + unsigned short tx_status; + + /* Read the first transmit buffer */ + obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), + (unsigned char *) &tx_status, + sizeof(tx_status)); + + /* If not completed -> exit */ + if ((tx_status & AC_SFLD_C) == 0) + break; + + /* Hack for reconfiguration */ + if (tx_status == 0xFFFF) + if (!wv_config_complete(dev, ioaddr, lp)) + break; /* Not completed */ + + /* We now remove this buffer */ + nreaped++; + --lp->tx_n_in_use; + +/* +if (lp->tx_n_in_use > 0) + printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); +*/ + + /* Was it the last one? */ + if (lp->tx_n_in_use <= 0) + lp->tx_first_in_use = I82586NULL; + else { + /* Next one in the chain */ + lp->tx_first_in_use += TXBLOCKZ; + if (lp->tx_first_in_use >= + OFFSET_CU + + NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -= + NTXBLOCKS * TXBLOCKZ; + } + + /* Hack for reconfiguration */ + if (tx_status == 0xFFFF) + continue; + + /* Now, check status of the finished command */ + if (tx_status & AC_SFLD_OK) { + int ncollisions; + + dev->stats.tx_packets++; + ncollisions = tx_status & AC_SFLD_MAXCOL; + dev->stats.collisions += ncollisions; +#ifdef DEBUG_TX_INFO + if (ncollisions > 0) + printk(KERN_DEBUG + "%s: wv_complete(): tx completed after %d collisions.\n", + dev->name, ncollisions); +#endif + } else { + dev->stats.tx_errors++; + if (tx_status & AC_SFLD_S10) { + dev->stats.tx_carrier_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: no CS.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S9) { + dev->stats.tx_carrier_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: lost CTS.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S8) { + dev->stats.tx_fifo_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: slow DMA.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S6) { + dev->stats.tx_heartbeat_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: heart beat.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S5) { + dev->stats.tx_aborted_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: too many collisions.\n", + dev->name); +#endif + } + } + +#ifdef DEBUG_TX_INFO + printk(KERN_DEBUG + "%s: wv_complete(): tx completed, tx_status 0x%04x\n", + dev->name, tx_status); +#endif + } + +#ifdef DEBUG_INTERRUPT_INFO + if (nreaped > 1) + printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", + dev->name, nreaped); +#endif + + /* + * Inform upper layers. + */ + if (lp->tx_n_in_use < NTXBLOCKS - 1) { + netif_wake_queue(dev); + } +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name); +#endif + return nreaped; +} + +/*------------------------------------------------------------------*/ +/* + * Reconfigure the i82586, or at least ask for it. + * Because wv_82586_config uses a transmission buffer, we must do it + * when we are sure that there is one left, so we do it now + * or in wavelan_packet_xmit() (I can't find any better place, + * wavelan_interrupt is not an option), so you may experience + * delays sometimes. + */ +static void wv_82586_reconfig(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + + /* Arm the flag, will be cleard in wv_82586_config() */ + lp->reconfig_82586 = 1; + + /* Check if we can do it now ! */ + if((netif_running(dev)) && !(netif_queue_stopped(dev))) { + spin_lock_irqsave(&lp->spinlock, flags); + /* May fail */ + wv_82586_config(dev); + spin_unlock_irqrestore(&lp->spinlock, flags); + } + else { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wv_82586_reconfig(): delayed (state = %lX)\n", + dev->name, dev->state); +#endif + } +} + +/********************* DEBUG & INFO SUBROUTINES *********************/ +/* + * This routine is used in the code to show information for debugging. + * Most of the time, it dumps the contents of hardware structures. + */ + +#ifdef DEBUG_PSA_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted contents of the Parameter Storage Area. + */ +static void wv_psa_show(psa_t * p) +{ + printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n"); + printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", + p->psa_io_base_addr_1, + p->psa_io_base_addr_2, + p->psa_io_base_addr_3, p->psa_io_base_addr_4); + printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", + p->psa_rem_boot_addr_1, + p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3); + printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); + printk("psa_int_req_no: %d\n", p->psa_int_req_no); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr); + printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr); + printk(KERN_DEBUG "psa_univ_local_sel: %d, ", + p->psa_univ_local_sel); + printk("psa_comp_number: %d, ", p->psa_comp_number); + printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); + printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", + p->psa_feature_select); + printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); + printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); + printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); + printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], + p->psa_nwid[1]); + printk("psa_nwid_select: %d\n", p->psa_nwid_select); + printk(KERN_DEBUG "psa_encryption_select: %d, ", + p->psa_encryption_select); + printk + ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + p->psa_encryption_key[0], p->psa_encryption_key[1], + p->psa_encryption_key[2], p->psa_encryption_key[3], + p->psa_encryption_key[4], p->psa_encryption_key[5], + p->psa_encryption_key[6], p->psa_encryption_key[7]); + printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); + printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", + p->psa_call_code[0]); + printk + ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2], + p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5], + p->psa_call_code[6], p->psa_call_code[7]); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n", + p->psa_reserved[0], + p->psa_reserved[1]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); + printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); + printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); +} /* wv_psa_show */ +#endif /* DEBUG_PSA_SHOW */ + +#ifdef DEBUG_MMC_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the Modem Management Controller. + * This function needs to be completed. + */ +static void wv_mmc_show(struct net_device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); + mmr_t m; + + /* Basic check */ + if (hasr_read(ioaddr) & HASR_NO_CLK) { + printk(KERN_WARNING + "%s: wv_mmc_show: modem not connected\n", + dev->name); + return; + } + + /* Read the mmc */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m)); + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + + /* Don't forget to update statistics */ + lp->wstats.discard.nwid += + (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; + + printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG + "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2], + m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5], + m.mmr_unused0[6], m.mmr_unused0[7]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", + m.mmr_des_avail, m.mmr_des_status); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused1[0], + m.mmr_unused1[1], + m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", + m.mmr_dce_status, + (m. + mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? + "energy detected," : "", + (m. + mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? + "loop test indicated," : "", + (m. + mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? + "transmitter on," : "", + (m. + mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? + "jabber timer expired," : ""); + printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", + m.mmr_unused2[0], m.mmr_unused2[1]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", + (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, + (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); + printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", + m.mmr_thr_pre_set & MMR_THR_PRE_SET, + (m. + mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : + "below"); + printk(KERN_DEBUG "signal_lvl: %d [%s], ", + m.mmr_signal_lvl & MMR_SIGNAL_LVL, + (m. + mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : + "no new msg"); + printk("silence_lvl: %d [%s], ", + m.mmr_silence_lvl & MMR_SILENCE_LVL, + (m. + mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : + "no new update"); + printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL, + (m. + mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : + "Antenna 0"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); +#endif /* DEBUG_SHOW_UNUSED */ +} /* wv_mmc_show */ +#endif /* DEBUG_MMC_SHOW */ + +#ifdef DEBUG_I82586_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the last block of the i82586 memory. + */ +static void wv_scb_show(unsigned long ioaddr) +{ + scb_t scb; + + obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + printk(KERN_DEBUG "##### WaveLAN system control block: #####\n"); + + printk(KERN_DEBUG "status: "); + printk("stat 0x%x[%s%s%s%s] ", + (scb. + scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | + SCB_ST_RNR)) >> 12, + (scb. + scb_status & SCB_ST_CX) ? "command completion interrupt," : + "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "", + (scb. + scb_status & SCB_ST_CNA) ? "command unit not active," : "", + (scb. + scb_status & SCB_ST_RNR) ? "receiving unit not ready," : + ""); + printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8, + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_IDLE) ? "idle" : "", + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_SUSP) ? "suspended" : "", + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_ACTV) ? "active" : ""); + printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4, + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_IDLE) ? "idle" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_SUSP) ? "suspended" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_NRES) ? "no resources" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_RDY) ? "ready" : ""); + + printk(KERN_DEBUG "command: "); + printk("ack 0x%x[%s%s%s%s] ", + (scb. + scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | + SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12, + (scb. + scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "", + (scb. + scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "", + (scb. + scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "", + (scb. + scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""); + printk("cuc 0x%x[%s%s%s%s%s] ", + (scb.scb_command & SCB_CMD_CUC) >> 8, + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_NOP) ? "nop" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_GO) ? "start cbl_offset" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_RES) ? "resume execution" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_SUS) ? "suspend execution" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_ABT) ? "abort execution" : ""); + printk("ruc 0x%x[%s%s%s%s%s]\n", + (scb.scb_command & SCB_CMD_RUC) >> 4, + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_NOP) ? "nop" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_GO) ? "start rfa_offset" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_RES) ? "resume reception" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_SUS) ? "suspend reception" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_ABT) ? "abort reception" : ""); + + printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset); + printk("rfa_offset 0x%x\n", scb.scb_rfa_offset); + + printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs); + printk("alnerrs %d ", scb.scb_alnerrs); + printk("rscerrs %d ", scb.scb_rscerrs); + printk("ovrnerrs %d\n", scb.scb_ovrnerrs); +} + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the i82586's receive unit. + */ +static void wv_ru_show(struct net_device * dev) +{ + printk(KERN_DEBUG + "##### WaveLAN i82586 receiver unit status: #####\n"); + printk(KERN_DEBUG "ru:"); + /* + * Not implemented yet + */ + printk("\n"); +} /* wv_ru_show */ + +/*------------------------------------------------------------------*/ +/* + * Display info about one control block of the i82586 memory. + */ +static void wv_cu_show_one(struct net_device * dev, net_local * lp, int i, u16 p) +{ + unsigned long ioaddr; + ac_tx_t actx; + + ioaddr = dev->base_addr; + + printk("%d: 0x%x:", i, p); + + obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx)); + printk(" status=0x%x,", actx.tx_h.ac_status); + printk(" command=0x%x,", actx.tx_h.ac_command); + + /* + { + tbd_t tbd; + + obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd)); + printk(" tbd_status=0x%x,", tbd.tbd_status); + } + */ + + printk("|"); +} + +/*------------------------------------------------------------------*/ +/* + * Print status of the command unit of the i82586. + */ +static void wv_cu_show(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned int i; + u16 p; + + printk(KERN_DEBUG + "##### WaveLAN i82586 command unit status: #####\n"); + + printk(KERN_DEBUG); + for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) { + wv_cu_show_one(dev, lp, i, p); + + p += TXBLOCKZ; + if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + p -= NTXBLOCKS * TXBLOCKZ; + } + printk("\n"); +} +#endif /* DEBUG_I82586_SHOW */ + +#ifdef DEBUG_DEVICE_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver. + */ +static void wv_dev_show(struct net_device * dev) +{ + printk(KERN_DEBUG "dev:"); + printk(" state=%lX,", dev->state); + printk(" trans_start=%ld,", dev->trans_start); + printk(" flags=0x%x,", dev->flags); + printk("\n"); +} /* wv_dev_show */ + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver's + * private information. + */ +static void wv_local_show(struct net_device * dev) +{ + net_local *lp; + + lp = netdev_priv(dev); + + printk(KERN_DEBUG "local:"); + printk(" tx_n_in_use=%d,", lp->tx_n_in_use); + printk(" hacr=0x%x,", lp->hacr); + printk(" rx_head=0x%x,", lp->rx_head); + printk(" rx_last=0x%x,", lp->rx_last); + printk(" tx_first_free=0x%x,", lp->tx_first_free); + printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use); + printk("\n"); +} /* wv_local_show */ +#endif /* DEBUG_DEVICE_SHOW */ + +#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) +/*------------------------------------------------------------------*/ +/* + * Dump packet header (and content if necessary) on the screen + */ +static inline void wv_packet_info(u8 * p, /* Packet to dump */ + int length, /* Length of the packet */ + char *msg1, /* Name of the device */ + char *msg2) +{ /* Name of the function */ + int i; + int maxi; + + printk(KERN_DEBUG + "%s: %s(): dest %pM, length %d\n", + msg1, msg2, p, length); + printk(KERN_DEBUG + "%s: %s(): src %pM, type 0x%02X%02X\n", + msg1, msg2, &p[6], p[12], p[13]); + +#ifdef DEBUG_PACKET_DUMP + + printk(KERN_DEBUG "data=\""); + + if ((maxi = length) > DEBUG_PACKET_DUMP) + maxi = DEBUG_PACKET_DUMP; + for (i = 14; i < maxi; i++) + if (p[i] >= ' ' && p[i] <= '~') + printk(" %c", p[i]); + else + printk("%02X", p[i]); + if (maxi < length) + printk(".."); + printk("\"\n"); + printk(KERN_DEBUG "\n"); +#endif /* DEBUG_PACKET_DUMP */ +} +#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ + +/*------------------------------------------------------------------*/ +/* + * This is the information which is displayed by the driver at startup. + * There are lots of flags for configuring it to your liking. + */ +static void wv_init_info(struct net_device * dev) +{ + short ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + + /* Read the parameter storage area */ + psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef DEBUG_PSA_SHOW + wv_psa_show(&psa); +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82586_SHOW + wv_cu_show(dev); +#endif + +#ifdef DEBUG_BASIC_SHOW + /* Now, let's go for the basic stuff. */ + printk(KERN_NOTICE "%s: WaveLAN at %#x, %pM, IRQ %d", + dev->name, ioaddr, dev->dev_addr, dev->irq); + + /* Print current network ID. */ + if (psa.psa_nwid_select) + printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], + psa.psa_nwid[1]); + else + printk(", nwid off"); + + /* If 2.00 card */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + unsigned short freq; + + /* Ask the EEPROM to read the frequency from the first area. */ + fee_read(ioaddr, 0x00, &freq, 1); + + /* Print frequency */ + printk(", 2.00, %ld", (freq >> 6) + 2400L); + + /* Hack! */ + if (freq & 0x20) + printk(".5"); + } else { + printk(", PC"); + switch (psa.psa_comp_number) { + case PSA_COMP_PC_AT_915: + case PSA_COMP_PC_AT_2400: + printk("-AT"); + break; + case PSA_COMP_PC_MC_915: + case PSA_COMP_PC_MC_2400: + printk("-MC"); + break; + case PSA_COMP_PCMCIA_915: + printk("MCIA"); + break; + default: + printk("?"); + } + printk(", "); + switch (psa.psa_subband) { + case PSA_SUBBAND_915: + printk("915"); + break; + case PSA_SUBBAND_2425: + printk("2425"); + break; + case PSA_SUBBAND_2460: + printk("2460"); + break; + case PSA_SUBBAND_2484: + printk("2484"); + break; + case PSA_SUBBAND_2430_5: + printk("2430.5"); + break; + default: + printk("?"); + } + } + + printk(" MHz\n"); +#endif /* DEBUG_BASIC_SHOW */ + +#ifdef DEBUG_VERSION_SHOW + /* Print version information */ + printk(KERN_NOTICE "%s", version); +#endif +} /* wv_init_info */ + +/********************* IOCTL, STATS & RECONFIG *********************/ +/* + * We found here routines that are called by Linux on different + * occasions after the configuration and not for transmitting data + * These may be called when the user use ifconfig, /proc/net/dev + * or wireless extensions + */ + + +/*------------------------------------------------------------------*/ +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ +static void wavelan_set_multicast_list(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", + dev->name); +#endif + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG + "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", + dev->name, dev->flags, dev->mc_count); +#endif + + /* Are we asking for promiscuous mode, + * or all multicast addresses (we don't have that!) + * or too many multicast addresses for the hardware filter? */ + if ((dev->flags & IFF_PROMISC) || + (dev->flags & IFF_ALLMULTI) || + (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) { + /* + * Enable promiscuous mode: receive all packets. + */ + if (!lp->promiscuous) { + lp->promiscuous = 1; + lp->mc_count = 0; + + wv_82586_reconfig(dev); + } + } else + /* Are there multicast addresses to send? */ + if (dev->mc_list != (struct dev_mc_list *) NULL) { + /* + * Disable promiscuous mode, but receive all packets + * in multicast list + */ +#ifdef MULTICAST_AVOID + if (lp->promiscuous || (dev->mc_count != lp->mc_count)) +#endif + { + lp->promiscuous = 0; + lp->mc_count = dev->mc_count; + + wv_82586_reconfig(dev); + } + } else { + /* + * Switch to normal mode: disable promiscuous mode and + * clear the multicast list. + */ + if (lp->promiscuous || lp->mc_count == 0) { + lp->promiscuous = 0; + lp->mc_count = 0; + + wv_82586_reconfig(dev); + } + } +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", + dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This function doesn't exist. + * (Note : it was a nice way to test the reconfigure stuff...) + */ +#ifdef SET_MAC_ADDRESS +static int wavelan_set_mac_address(struct net_device * dev, void *addr) +{ + struct sockaddr *mac = addr; + + /* Copy the address. */ + memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); + + /* Reconfigure the beast. */ + wv_82586_reconfig(dev); + + return 0; +} +#endif /* SET_MAC_ADDRESS */ + + +/*------------------------------------------------------------------*/ +/* + * Frequency setting (for hardware capable of it) + * It's a bit complicated and you don't really want to look into it. + * (called in wavelan_ioctl) + */ +static int wv_set_frequency(unsigned long ioaddr, /* I/O port of the card */ + iw_freq * frequency) +{ + const int BAND_NUM = 10; /* Number of bands */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz */ +#ifdef DEBUG_IOCTL_INFO + int i; +#endif + + /* Setting by frequency */ + /* Theoretically, you may set any frequency between + * the two limits with a 0.5 MHz precision. In practice, + * I don't want you to have trouble with local regulations. + */ + if ((frequency->e == 1) && + (frequency->m >= (int) 2.412e8) + && (frequency->m <= (int) 2.487e8)) { + freq = ((frequency->m / 10000) - 24000L) / 5; + } + + /* Setting by channel (same as wfreqsel) */ + /* Warning: each channel is 22 MHz wide, so some of the channels + * will interfere. */ + if ((frequency->e == 0) && (frequency->m < BAND_NUM)) { + /* Get frequency offset. */ + freq = channel_bands[frequency->m] >> 1; + } + + /* Verify that the frequency is allowed. */ + if (freq != 0L) { + u16 table[10]; /* Authorized frequency table */ + + /* Read the frequency table. */ + fee_read(ioaddr, 0x71, table, 10); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Frequency table: "); + for (i = 0; i < 10; i++) { + printk(" %04X", table[i]); + } + printk("\n"); +#endif + + /* Look in the table to see whether the frequency is allowed. */ + if (!(table[9 - ((freq - 24) / 16)] & + (1 << ((freq - 24) % 16)))) return -EINVAL; /* not allowed */ + } else + return -EINVAL; + + /* if we get a usable frequency */ + if (freq != 0L) { + unsigned short area[16]; + unsigned short dac[2]; + unsigned short area_verify[16]; + unsigned short dac_verify[2]; + /* Corresponding gain (in the power adjust value table) + * See AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8 + * and WCIN062D.DOC, page 6.2.9. */ + unsigned short power_limit[] = { 40, 80, 120, 160, 0 }; + int power_band = 0; /* Selected band */ + unsigned short power_adjust; /* Correct value */ + + /* Search for the gain. */ + power_band = 0; + while ((freq > power_limit[power_band]) && + (power_limit[++power_band] != 0)); + + /* Read the first area. */ + fee_read(ioaddr, 0x00, area, 16); + + /* Read the DAC. */ + fee_read(ioaddr, 0x60, dac, 2); + + /* Read the new power adjust value. */ + fee_read(ioaddr, 0x6B - (power_band >> 1), &power_adjust, + 1); + if (power_band & 0x1) + power_adjust >>= 8; + else + power_adjust &= 0xFF; + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "WaveLAN EEPROM Area 1: "); + for (i = 0; i < 16; i++) { + printk(" %04X", area[i]); + } + printk("\n"); + + printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", + dac[0], dac[1]); +#endif + + /* Frequency offset (for info only) */ + area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); + + /* Receiver Principle main divider coefficient */ + area[3] = (freq >> 1) + 2400L - 352L; + area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Transmitter Main divider coefficient */ + area[13] = (freq >> 1) + 2400L; + area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Other parts of the area are flags, bit streams or unused. */ + + /* Set the value in the DAC. */ + dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); + dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); + + /* Write the first area. */ + fee_write(ioaddr, 0x00, area, 16); + + /* Write the DAC. */ + fee_write(ioaddr, 0x60, dac, 2); + + /* We now should verify here that the writing of the EEPROM went OK. */ + + /* Reread the first area. */ + fee_read(ioaddr, 0x00, area_verify, 16); + + /* Reread the DAC. */ + fee_read(ioaddr, 0x60, dac_verify, 2); + + /* Compare. */ + if (memcmp(area, area_verify, 16 * 2) || + memcmp(dac, dac_verify, 2 * 2)) { +#ifdef DEBUG_IOCTL_ERROR + printk(KERN_INFO + "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n"); +#endif + return -EOPNOTSUPP; + } + + /* We must download the frequency parameters to the + * synthesizers (from the EEPROM - area 1) + * Note: as the EEPROM is automatically decremented, we set the end + * if the area... */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished. */ + fee_wait(ioaddr, 100, 100); + + /* We must now download the power adjust value (gain) to + * the synthesizers (from the EEPROM - area 7 - DAC). */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait for the download to finish. */ + fee_wait(ioaddr, 100, 100); + +#ifdef DEBUG_IOCTL_INFO + /* Verification of what we have done */ + + printk(KERN_DEBUG "WaveLAN EEPROM Area 1: "); + for (i = 0; i < 16; i++) { + printk(" %04X", area_verify[i]); + } + printk("\n"); + + printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", + dac_verify[0], dac_verify[1]); +#endif + + return 0; + } else + return -EINVAL; /* Bah, never get there... */ +} + +/*------------------------------------------------------------------*/ +/* + * Give the list of available frequencies. + */ +static int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */ + iw_freq * list, /* List of frequencies to fill */ + int max) +{ /* Maximum number of frequencies */ + u16 table[10]; /* Authorized frequency table */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ + int i; /* index in the table */ + int c = 0; /* Channel number */ + + /* Read the frequency table. */ + fee_read(ioaddr, 0x71 /* frequency table */ , table, 10); + + /* Check all frequencies. */ + i = 0; + for (freq = 0; freq < 150; freq++) + /* Look in the table if the frequency is allowed */ + if (table[9 - (freq / 16)] & (1 << (freq % 16))) { + /* Compute approximate channel number */ + while ((c < ARRAY_SIZE(channel_bands)) && + (((channel_bands[c] >> 1) - 24) < freq)) + c++; + list[i].i = c; /* Set the list index */ + + /* put in the list */ + list[i].m = (((freq + 24) * 5) + 24000L) * 10000; + list[i++].e = 1; + + /* Check number. */ + if (i >= max) + return (i); + } + + return (i); +} + +#ifdef IW_WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Gather wireless spy statistics: for each packet, compare the source + * address with our list, and if they match, get the statistics. + * Sorry, but this function really needs the wireless extensions. + */ +static inline void wl_spy_gather(struct net_device * dev, + u8 * mac, /* MAC address */ + u8 * stats) /* Statistics to gather */ +{ + struct iw_quality wstats; + + wstats.qual = stats[2] & MMR_SGNL_QUAL; + wstats.level = stats[0] & MMR_SIGNAL_LVL; + wstats.noise = stats[1] & MMR_SILENCE_LVL; + wstats.updated = 0x7; + + /* Update spy records */ + wireless_spy_update(dev, mac, &wstats); +} +#endif /* IW_WIRELESS_SPY */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * This function calculates a histogram of the signal level. + * As the noise is quite constant, it's like doing it on the SNR. + * We have defined a set of interval (lp->his_range), and each time + * the level goes in that interval, we increment the count (lp->his_sum). + * With this histogram you may detect if one WaveLAN is really weak, + * or you may also calculate the mean and standard deviation of the level. + */ +static inline void wl_his_gather(struct net_device * dev, u8 * stats) +{ /* Statistics to gather */ + net_local *lp = netdev_priv(dev); + u8 level = stats[0] & MMR_SIGNAL_LVL; + int i; + + /* Find the correct interval. */ + i = 0; + while ((i < (lp->his_number - 1)) + && (level >= lp->his_range[i++])); + + /* Increment interval counter. */ + (lp->his_sum[i])++; +} +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get protocol name + */ +static int wavelan_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + strcpy(wrqu->name, "WaveLAN"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set NWID + */ +static int wavelan_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + mm_t m; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set NWID in WaveLAN. */ + if (!wrqu->nwid.disabled) { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; + psa.psa_nwid_select = 0x01; + psa_write(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + + /* Set NWID in mmc. */ + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; + mmc_write(ioaddr, + (char *) &m.w.mmw_netw_id_l - + (char *) &m, + (unsigned char *) &m.w.mmw_netw_id_l, 2); + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); + } else { + /* Disable NWID in the psa. */ + psa.psa_nwid_select = 0x00; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_nwid_select - + (char *) &psa, + (unsigned char *) &psa.psa_nwid_select, + 1); + + /* Disable NWID in the mmc (no filtering). */ + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), + MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get NWID + */ +static int wavelan_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the NWID. */ + psa_read(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrqu->nwid.disabled = !(psa.psa_nwid_select); + wrqu->nwid.fixed = 1; /* Superfluous */ + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int wavelan_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + unsigned long flags; + int ret; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(ioaddr, &(wrqu->freq)); + else + ret = -EOPNOTSUPP; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int wavelan_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). + * Does it work for everybody, especially old cards? */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + unsigned short freq; + + /* Ask the EEPROM to read the frequency from the first area. */ + fee_read(ioaddr, 0x00, &freq, 1); + wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrqu->freq.e = 1; + } else { + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_subband - (char *) &psa, + (unsigned char *) &psa.psa_subband, 1); + + if (psa.psa_subband <= 4) { + wrqu->freq.m = fixed_bands[psa.psa_subband]; + wrqu->freq.e = (psa.psa_subband != 0); + } else + ret = -EOPNOTSUPP; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set level threshold + */ +static int wavelan_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set the level threshold. */ + /* We should complain loudly if wrqu->sens.fixed = 0, because we + * can't set auto mode... */ + psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), + psa.psa_thr_pre_set); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get level threshold + */ +static int wavelan_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the level threshold. */ + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; + wrqu->sens.fixed = 1; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set encryption key + */ +static int wavelan_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + unsigned long flags; + psa_t psa; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if capable of encryption */ + if (!mmc_encr(ioaddr)) { + ret = -EOPNOTSUPP; + } + + /* Check the size of the key */ + if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { + ret = -EINVAL; + } + + if(!ret) { + /* Basic checking... */ + if (wrqu->encoding.length == 8) { + /* Copy the key in the driver */ + memcpy(psa.psa_encryption_key, extra, + wrqu->encoding.length); + psa.psa_encryption_select = 1; + + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 8 + 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(ioaddr, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa. + psa_encryption_key, 8); + } + + /* disable encryption */ + if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { + psa.psa_encryption_select = 0; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get encryption key + */ +static int wavelan_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if encryption is available */ + if (!mmc_encr(ioaddr)) { + ret = -EOPNOTSUPP; + } else { + /* Read the encryption key */ + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1 + 8); + + /* encryption is enabled ? */ + if (psa.psa_encryption_select) + wrqu->encoding.flags = IW_ENCODE_ENABLED; + else + wrqu->encoding.flags = IW_ENCODE_DISABLED; + wrqu->encoding.flags |= mmc_encr(ioaddr); + + /* Copy the key to the user buffer */ + wrqu->encoding.length = 8; + memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int wavelan_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + struct iw_range *range = (struct iw_range *) extra; + unsigned long flags; + int ret = 0; + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; + + /* Set information in the range struct. */ + range->throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0xFFFF; + + range->sensitivity = 0x3F; + range->max_qual.qual = MMR_SGNL_QUAL; + range->max_qual.level = MMR_SIGNAL_LVL; + range->max_qual.noise = MMR_SILENCE_LVL; + range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range->avg_qual.level = 30; + range->avg_qual.noise = 8; + + range->num_bitrates = 1; + range->bitrate[0] = 2000000; /* 2 Mb/s */ + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | + IW_EVENT_CAPA_MASK(0x8B04)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + range->num_channels = 10; + range->num_frequency = wv_frequency_list(ioaddr, range->freq, + IW_MAX_FREQUENCIES); + } else + range->num_channels = range->num_frequency = 0; + + /* Encryption supported ? */ + if (mmc_encr(ioaddr)) { + range->encoding_size[0] = 8; /* DES = 64 bits key */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set quality threshold + */ +static int wavelan_set_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa.psa_quality_thr = *(extra) & 0x0F; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), + psa.psa_quality_thr); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); /* lp is not unused */ + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + *(extra) = psa.psa_quality_thr & 0x0F; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set histogram + */ +static int wavelan_set_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); /* lp is not unused */ + + /* Check the number of intervals. */ + if (wrqu->data.length > 16) { + return(-E2BIG); + } + + /* Disable histo while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->his_number = 0; + + /* Are there ranges to copy? */ + if (wrqu->data.length > 0) { + /* Copy interval ranges to the driver */ + memcpy(lp->his_range, extra, wrqu->data.length); + + { + int i; + printk(KERN_DEBUG "Histo :"); + for(i = 0; i < wrqu->data.length; i++) + printk(" %d", lp->his_range[i]); + printk("\n"); + } + + /* Reset result structure. */ + memset(lp->his_sum, 0x00, sizeof(long) * 16); + } + + /* Now we can set the number of ranges */ + lp->his_number = wrqu->data.length; + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get histogram + */ +static int wavelan_get_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); /* lp is not unused */ + + /* Set the number of intervals. */ + wrqu->data.length = lp->his_number; + + /* Give back the distribution statistics */ + if(lp->his_number > 0) + memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); + + return(0); +} +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const iw_handler wavelan_handler[] = +{ + NULL, /* SIOCSIWNAME */ + wavelan_get_name, /* SIOCGIWNAME */ + wavelan_set_nwid, /* SIOCSIWNWID */ + wavelan_get_nwid, /* SIOCGIWNWID */ + wavelan_set_freq, /* SIOCSIWFREQ */ + wavelan_get_freq, /* SIOCGIWFREQ */ + NULL, /* SIOCSIWMODE */ + NULL, /* SIOCGIWMODE */ + wavelan_set_sens, /* SIOCSIWSENS */ + wavelan_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + wavelan_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + /* Bummer ! Why those are only at the end ??? */ + wavelan_set_encode, /* SIOCSIWENCODE */ + wavelan_get_encode, /* SIOCGIWENCODE */ +}; + +static const iw_handler wavelan_private_handler[] = +{ + wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ + wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ +#ifdef HISTOGRAM + wavelan_set_histo, /* SIOCIWFIRSTPRIV + 2 */ + wavelan_get_histo, /* SIOCIWFIRSTPRIV + 3 */ +#endif /* HISTOGRAM */ +}; + +static const struct iw_priv_args wavelan_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, + { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, +}; + +static const struct iw_handler_def wavelan_handler_def = +{ + .num_standard = ARRAY_SIZE(wavelan_handler), + .num_private = ARRAY_SIZE(wavelan_private_handler), + .num_private_args = ARRAY_SIZE(wavelan_private_args), + .standard = wavelan_handler, + .private = wavelan_private_handler, + .private_args = wavelan_private_args, + .get_wireless_stats = wavelan_get_wireless_stats, +}; + +/*------------------------------------------------------------------*/ +/* + * Get wireless statistics. + * Called by /proc/net/wireless + */ +static iw_stats *wavelan_get_wireless_stats(struct net_device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); + mmr_t m; + iw_stats *wstats; + unsigned long flags; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", + dev->name); +#endif + + /* Check */ + if (lp == (net_local *) NULL) + return (iw_stats *) NULL; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + wstats = &lp->wstats; + + /* Get data from the mmc. */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + + mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); + mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, + 2); + mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, + 4); + + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + + /* Copy data to wireless stuff. */ + wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; + wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; + wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; + wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; + wstats->qual.updated = (((m. mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) + | ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) + | ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); + wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; + wstats->discard.code = 0L; + wstats->discard.misc = 0L; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", + dev->name); +#endif + return &lp->wstats; +} + +/************************* PACKET RECEPTION *************************/ +/* + * This part deals with receiving the packets. + * The interrupt handler gets an interrupt when a packet has been + * successfully received and calls this part. + */ + +/*------------------------------------------------------------------*/ +/* + * This routine does the actual copying of data (including the Ethernet + * header structure) from the WaveLAN card to an sk_buff chain that + * will be passed up to the network interface layer. NOTE: we + * currently don't handle trailer protocols (neither does the rest of + * the network interface), so if that is needed, it will (at least in + * part) be added here. The contents of the receive ring buffer are + * copied to a message chain that is then passed to the kernel. + * + * Note: if any errors occur, the packet is "dropped on the floor". + * (called by wv_packet_rcv()) + */ +static void +wv_packet_read(struct net_device * dev, u16 buf_off, int sksize) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + struct sk_buff *skb; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", + dev->name, buf_off, sksize); +#endif + + /* Allocate buffer for the data */ + if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO + "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n", + dev->name, sksize); +#endif + dev->stats.rx_dropped++; + return; + } + + /* Copy the packet to the buffer. */ + obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); + skb->protocol = eth_type_trans(skb, dev); + +#ifdef DEBUG_RX_INFO + wv_packet_info(skb_mac_header(skb), sksize, dev->name, + "wv_packet_read"); +#endif /* DEBUG_RX_INFO */ + + /* Statistics-gathering and associated stuff. + * It seem a bit messy with all the define, but it's really + * simple... */ + if ( +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + (lp->spy_data.spy_number > 0) || +#endif /* IW_WIRELESS_SPY */ +#ifdef HISTOGRAM + (lp->his_number > 0) || +#endif /* HISTOGRAM */ + 0) { + u8 stats[3]; /* signal level, noise level, signal quality */ + + /* Read signal level, silence level and signal quality bytes */ + /* Note: in the PCMCIA hardware, these are part of the frame. + * It seems that for the ISA hardware, it's nowhere to be + * found in the frame, so I'm obliged to do this (it has a + * side effect on /proc/net/wireless). + * Any ideas? + */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3); + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG + "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", + dev->name, stats[0] & 0x3F, stats[1] & 0x3F, + stats[2] & 0x0F); +#endif + + /* Spying stuff */ +#ifdef IW_WIRELESS_SPY + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, + stats); +#endif /* IW_WIRELESS_SPY */ +#ifdef HISTOGRAM + wl_his_gather(dev, stats); +#endif /* HISTOGRAM */ + } + + /* + * Hand the packet to the network module. + */ + netif_rx(skb); + + /* Keep statistics up to date */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += sksize; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Transfer as many packets as we can + * from the device RAM. + * (called in wavelan_interrupt()). + * Note : the spinlock is already grabbed for us. + */ +static void wv_receive(struct net_device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); + fd_t fd; + rbd_t rbd; + int nreaped = 0; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name); +#endif + + /* Loop on each received packet. */ + for (;;) { + obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, + sizeof(fd)); + + /* Note about the status : + * It start up to be 0 (the value we set). Then, when the RU + * grab the buffer to prepare for reception, it sets the + * FD_STATUS_B flag. When the RU has finished receiving the + * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate + * completion and set the other flags to indicate the eventual + * errors. FD_STATUS_OK indicates that the reception was OK. + */ + + /* If the current frame is not complete, we have reached the end. */ + if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) + break; /* This is how we exit the loop. */ + + nreaped++; + + /* Check whether frame was correctly received. */ + if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) { + /* Does the frame contain a pointer to the data? Let's check. */ + if (fd.fd_rbd_offset != I82586NULL) { + /* Read the receive buffer descriptor */ + obram_read(ioaddr, fd.fd_rbd_offset, + (unsigned char *) &rbd, + sizeof(rbd)); + +#ifdef DEBUG_RX_ERROR + if ((rbd.rbd_status & RBD_STATUS_EOF) != + RBD_STATUS_EOF) printk(KERN_INFO + "%s: wv_receive(): missing EOF flag.\n", + dev->name); + + if ((rbd.rbd_status & RBD_STATUS_F) != + RBD_STATUS_F) printk(KERN_INFO + "%s: wv_receive(): missing F flag.\n", + dev->name); +#endif /* DEBUG_RX_ERROR */ + + /* Read the packet and transmit to Linux */ + wv_packet_read(dev, rbd.rbd_bufl, + rbd. + rbd_status & + RBD_STATUS_ACNT); + } +#ifdef DEBUG_RX_ERROR + else /* if frame has no data */ + printk(KERN_INFO + "%s: wv_receive(): frame has no data.\n", + dev->name); +#endif + } else { /* If reception was no successful */ + + dev->stats.rx_errors++; + +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG + "%s: wv_receive(): frame not received successfully (%X).\n", + dev->name, fd.fd_status); +#endif + +#ifdef DEBUG_RX_ERROR + if ((fd.fd_status & FD_STATUS_S6) != 0) + printk(KERN_INFO + "%s: wv_receive(): no EOF flag.\n", + dev->name); +#endif + + if ((fd.fd_status & FD_STATUS_S7) != 0) { + dev->stats.rx_length_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): frame too short.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S8) != 0) { + dev->stats.rx_over_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): rx DMA overrun.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S9) != 0) { + dev->stats.rx_fifo_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): ran out of resources.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S10) != 0) { + dev->stats.rx_frame_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): alignment error.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S11) != 0) { + dev->stats.rx_crc_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): CRC error.\n", + dev->name); +#endif + } + } + + fd.fd_status = 0; + obram_write(ioaddr, fdoff(lp->rx_head, fd_status), + (unsigned char *) &fd.fd_status, + sizeof(fd.fd_status)); + + fd.fd_command = FD_COMMAND_EL; + obram_write(ioaddr, fdoff(lp->rx_head, fd_command), + (unsigned char *) &fd.fd_command, + sizeof(fd.fd_command)); + + fd.fd_command = 0; + obram_write(ioaddr, fdoff(lp->rx_last, fd_command), + (unsigned char *) &fd.fd_command, + sizeof(fd.fd_command)); + + lp->rx_last = lp->rx_head; + lp->rx_head = fd.fd_link_offset; + } /* for(;;) -> loop on all frames */ + +#ifdef DEBUG_RX_INFO + if (nreaped > 1) + printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", + dev->name, nreaped); +#endif +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name); +#endif +} + +/*********************** PACKET TRANSMISSION ***********************/ +/* + * This part deals with sending packets through the WaveLAN. + * + */ + +/*------------------------------------------------------------------*/ +/* + * This routine fills in the appropriate registers and memory + * locations on the WaveLAN card and starts the card off on + * the transmit. + * + * The principle: + * Each block contains a transmit command, a NOP command, + * a transmit block descriptor and a buffer. + * The CU read the transmit block which point to the tbd, + * read the tbd and the content of the buffer. + * When it has finish with it, it goes to the next command + * which in our case is the NOP. The NOP points on itself, + * so the CU stop here. + * When we add the next block, we modify the previous nop + * to make it point on the new tx command. + * Simple, isn't it ? + * + * (called in wavelan_packet_xmit()) + */ +static int wv_packet_write(struct net_device * dev, void *buf, short length) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + unsigned short txblock; + unsigned short txpred; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short buf_addr; + ac_tx_t tx; + ac_nop_t nop; + tbd_t tbd; + int clen = length; + unsigned long flags; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, + length); +#endif + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check nothing bad has happened */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { +#ifdef DEBUG_TX_ERROR + printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n", + dev->name); +#endif + spin_unlock_irqrestore(&lp->spinlock, flags); + return 1; + } + + /* Calculate addresses of next block and previous block. */ + txblock = lp->tx_first_free; + txpred = txblock - TXBLOCKZ; + if (txpred < OFFSET_CU) + txpred += NTXBLOCKS * TXBLOCKZ; + lp->tx_first_free += TXBLOCKZ; + if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + + lp->tx_n_in_use++; + + /* Calculate addresses of the different parts of the block. */ + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + buf_addr = tbd_addr + sizeof(tbd); + + /* + * Transmit command + */ + tx.tx_h.ac_status = 0; + obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), + (unsigned char *) &tx.tx_h.ac_status, + sizeof(tx.tx_h.ac_status)); + + /* + * NOP command + */ + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* + * Transmit buffer descriptor + */ + tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen); + tbd.tbd_next_bd_offset = I82586NULL; + tbd.tbd_bufl = buf_addr; + tbd.tbd_bufh = 0; + obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd)); + + /* + * Data + */ + obram_write(ioaddr, buf_addr, buf, length); + + /* + * Overwrite the predecessor NOP link + * so that it points to this txblock. + */ + nop_addr = txpred + sizeof(tx); + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = txblock; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Make sure the watchdog will keep quiet for a while */ + dev->trans_start = jiffies; + + /* Keep stats up to date. */ + dev->stats.tx_bytes += length; + + if (lp->tx_first_in_use == I82586NULL) + lp->tx_first_in_use = txblock; + + if (lp->tx_n_in_use < NTXBLOCKS - 1) + netif_wake_queue(dev); + + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_TX_INFO + wv_packet_info((u8 *) buf, length, dev->name, + "wv_packet_write"); +#endif /* DEBUG_TX_INFO */ + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); +#endif + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called when we want to send a packet (NET3 callback) + * In this routine, we check if the harware is ready to accept + * the packet. We also prevent reentrance. Then we call the function + * to send the packet. + */ +static netdev_tx_t wavelan_packet_xmit(struct sk_buff *skb, + struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + char data[ETH_ZLEN]; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, + (unsigned) skb); +#endif + + /* + * Block a timer-based transmit from overlapping. + * In other words, prevent reentering this routine. + */ + netif_stop_queue(dev); + + /* If somebody has asked to reconfigure the controller, + * we can do it now. + */ + if (lp->reconfig_82586) { + spin_lock_irqsave(&lp->spinlock, flags); + wv_82586_config(dev); + spin_unlock_irqrestore(&lp->spinlock, flags); + /* Check that we can continue */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) + return NETDEV_TX_BUSY; + } + + /* Do we need some padding? */ + /* Note : on wireless the propagation time is in the order of 1us, + * and we don't have the Ethernet specific requirement of beeing + * able to detect collisions, therefore in theory we don't really + * need to pad. Jean II */ + if (skb->len < ETH_ZLEN) { + memset(data, 0, ETH_ZLEN); + skb_copy_from_linear_data(skb, data, skb->len); + /* Write packet on the card */ + if(wv_packet_write(dev, data, ETH_ZLEN)) + return NETDEV_TX_BUSY; /* We failed */ + } + else if(wv_packet_write(dev, skb->data, skb->len)) + return NETDEV_TX_BUSY; /* We failed */ + + + dev_kfree_skb(skb); + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); +#endif + return NETDEV_TX_OK; +} + +/*********************** HARDWARE CONFIGURATION ***********************/ +/* + * This part does the real job of starting and configuring the hardware. + */ + +/*--------------------------------------------------------------------*/ +/* + * Routine to initialize the Modem Management Controller. + * (called by wv_hw_reset()) + */ +static int wv_mmc_init(struct net_device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + mmw_t m; + int configured; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); +#endif + + /* Read the parameter storage area. */ + psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef USE_PSA_CONFIG + configured = psa.psa_conf_status & 1; +#else + configured = 0; +#endif + + /* Is the PSA is not configured */ + if (!configured) { + /* User will be able to configure NWID later (with iwconfig). */ + psa.psa_nwid[0] = 0; + psa.psa_nwid[1] = 0; + + /* no NWID checking since NWID is not set */ + psa.psa_nwid_select = 0; + + /* Disable encryption */ + psa.psa_encryption_select = 0; + + /* Set to standard values: + * 0x04 for AT, + * 0x01 for MCA, + * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) + */ + if (psa.psa_comp_number & 1) + psa.psa_thr_pre_set = 0x01; + else + psa.psa_thr_pre_set = 0x04; + psa.psa_quality_thr = 0x03; + + /* It is configured */ + psa.psa_conf_status |= 1; + +#ifdef USE_PSA_CONFIG + /* Write the psa. */ + psa_write(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 4); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_conf_status - (char *) &psa, + (unsigned char *) &psa.psa_conf_status, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif + } + + /* Zero the mmc structure. */ + memset(&m, 0x00, sizeof(m)); + + /* Copy PSA info to the mmc. */ + m.mmw_netw_id_l = psa.psa_nwid[1]; + m.mmw_netw_id_h = psa.psa_nwid[0]; + + if (psa.psa_nwid_select & 1) + m.mmw_loopt_sel = 0x00; + else + m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; + + memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, + sizeof(m.mmw_encr_key)); + + if (psa.psa_encryption_select) + m.mmw_encr_enable = + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; + else + m.mmw_encr_enable = 0; + + m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; + m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; + + /* + * Set default modem control parameters. + * See NCR document 407-0024326 Rev. A. + */ + m.mmw_jabber_enable = 0x01; + m.mmw_freeze = 0; + m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; + m.mmw_ifs = 0x20; + m.mmw_mod_delay = 0x04; + m.mmw_jam_time = 0x38; + + m.mmw_des_io_invert = 0; + m.mmw_decay_prm = 0; + m.mmw_decay_updat_prm = 0; + + /* Write all info to MMC. */ + mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m)); + + /* The following code starts the modem of the 2.00 frequency + * selectable cards at power on. It's not strictly needed for the + * following boots. + * The original patch was by Joe Finney for the PCMCIA driver, but + * I've cleaned it up a bit and added documentation. + * Thanks to Loeke Brederveld from Lucent for the info. + */ + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) + * Does it work for everybody, especially old cards? */ + /* Note: WFREQSEL verifies that it is able to read a sensible + * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). + * My test is more crude but does work. */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + /* We must download the frequency parameters to the + * synthesizers (from the EEPROM - area 1) + * Note: as the EEPROM is automatically decremented, we set the end + * if the area... */ + m.mmw_fee_addr = 0x0F; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, + (unsigned char *) &m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished. */ + fee_wait(ioaddr, 100, 100); + +#ifdef DEBUG_CONFIG_INFO + /* The frequency was in the last word downloaded. */ + mmc_read(ioaddr, (char *) &m.mmw_fee_data_l - (char *) &m, + (unsigned char *) &m.mmw_fee_data_l, 2); + + /* Print some info for the user. */ + printk(KERN_DEBUG + "%s: WaveLAN 2.00 recognised (frequency select). Current frequency = %ld\n", + dev->name, + ((m. + mmw_fee_data_h << 4) | (m.mmw_fee_data_l >> 4)) * + 5 / 2 + 24000L); +#endif + + /* We must now download the power adjust value (gain) to + * the synthesizers (from the EEPROM - area 7 - DAC). */ + m.mmw_fee_addr = 0x61; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, + (unsigned char *) &m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished. */ + } + /* if 2.00 card */ +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Construct the fd and rbd structures. + * Start the receive unit. + * (called by wv_hw_reset()) + */ +static int wv_ru_start(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + u16 scb_cs; + fd_t fd; + rbd_t rbd; + u16 rx; + u16 rx_next; + int i; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); +#endif + + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY) + return 0; + + lp->rx_head = OFFSET_RU; + + for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) { + rx_next = + (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ; + + fd.fd_status = 0; + fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0; + fd.fd_link_offset = rx_next; + fd.fd_rbd_offset = rx + sizeof(fd); + obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd)); + + rbd.rbd_status = 0; + rbd.rbd_next_rbd_offset = I82586NULL; + rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd); + rbd.rbd_bufh = 0; + rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ); + obram_write(ioaddr, rx + sizeof(fd), + (unsigned char *) &rbd, sizeof(rbd)); + + lp->rx_last = rx; + } + + obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), + (unsigned char *) &lp->rx_head, sizeof(lp->rx_head)); + + scb_cs = SCB_CMD_RUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_ru_start(): board not accepting command.\n", + dev->name); +#endif + return -1; + } +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Initialise the transmit blocks. + * Start the command unit executing the NOP + * self-loop of the first transmit block. + * + * Here we create the list of send buffers used to transmit packets + * between the PC and the command unit. For each buffer, we create a + * buffer descriptor (pointing on the buffer), a transmit command + * (pointing to the buffer descriptor) and a NOP command. + * The transmit command is linked to the NOP, and the NOP to itself. + * When we will have finished executing the transmit command, we will + * then loop on the NOP. By releasing the NOP link to a new command, + * we may send another buffer. + * + * (called by wv_hw_reset()) + */ +static int wv_cu_start(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + int i; + u16 txblock; + u16 first_nop; + u16 scb_cs; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name); +#endif + + lp->tx_first_free = OFFSET_CU; + lp->tx_first_in_use = I82586NULL; + + for (i = 0, txblock = OFFSET_CU; + i < NTXBLOCKS; i++, txblock += TXBLOCKZ) { + ac_tx_t tx; + ac_nop_t nop; + tbd_t tbd; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short buf_addr; + + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + buf_addr = tbd_addr + sizeof(tbd); + + tx.tx_h.ac_status = 0; + tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I; + tx.tx_h.ac_link = nop_addr; + tx.tx_tbd_offset = tbd_addr; + obram_write(ioaddr, tx_addr, (unsigned char *) &tx, + sizeof(tx)); + + nop.nop_h.ac_status = 0; + nop.nop_h.ac_command = acmd_nop; + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, nop_addr, (unsigned char *) &nop, + sizeof(nop)); + + tbd.tbd_status = TBD_STATUS_EOF; + tbd.tbd_next_bd_offset = I82586NULL; + tbd.tbd_bufl = buf_addr; + tbd.tbd_bufh = 0; + obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, + sizeof(tbd)); + } + + first_nop = + OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t); + obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), + (unsigned char *) &first_nop, sizeof(first_nop)); + + scb_cs = SCB_CMD_CUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_cu_start(): board not accepting command.\n", + dev->name); +#endif + return -1; + } + + lp->tx_n_in_use = 0; + netif_start_queue(dev); +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard configuration of the WaveLAN + * controller (i82586). + * + * It initialises the scp, iscp and scb structure + * The first two are just pointers to the next. + * The last one is used for basic configuration and for basic + * communication (interrupt status). + * + * (called by wv_hw_reset()) + */ +static int wv_82586_start(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + scp_t scp; /* system configuration pointer */ + iscp_t iscp; /* intermediate scp */ + scb_t scb; /* system control block */ + ach_t cb; /* Action command header */ + u8 zeroes[512]; + int i; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name); +#endif + + /* + * Clear the onboard RAM. + */ + memset(&zeroes[0], 0x00, sizeof(zeroes)); + for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes)) + obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes)); + + /* + * Construct the command unit structures: + * scp, iscp, scb, cb. + */ + memset(&scp, 0x00, sizeof(scp)); + scp.scp_sysbus = SCP_SY_16BBUS; + scp.scp_iscpl = OFFSET_ISCP; + obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp, + sizeof(scp)); + + memset(&iscp, 0x00, sizeof(iscp)); + iscp.iscp_busy = 1; + iscp.iscp_offset = OFFSET_SCB; + obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, + sizeof(iscp)); + + /* Our first command is to reset the i82586. */ + memset(&scb, 0x00, sizeof(scb)); + scb.scb_command = SCB_CMD_RESET; + scb.scb_cbl_offset = OFFSET_CU; + scb.scb_rfa_offset = OFFSET_RU; + obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + set_chan_attn(ioaddr, lp->hacr); + + /* Wait for command to finish. */ + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, + sizeof(iscp)); + + if (iscp.iscp_busy == (unsigned short) 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): iscp_busy timeout.\n", + dev->name); +#endif + return -1; + } + + /* Check command completion. */ + for (i = 15; i > 0; i--) { + obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA)) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", + dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); +#endif + return -1; + } + + wv_ack(dev); + + /* Set the action command header. */ + memset(&cb, 0x00, sizeof(cb)); + cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose); + cb.ac_link = OFFSET_CU; + obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); + + if (wv_synchronous_cmd(dev, "diag()") == -1) + return -1; + + obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); + if (cb.ac_status & AC_SFLD_FAIL) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): i82586 Self Test failed.\n", + dev->name); +#endif + return -1; + } +#ifdef DEBUG_I82586_SHOW + wv_scb_show(ioaddr); +#endif + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard configuration of the WaveLAN + * controller (i82586). + * + * This routine is a violent hack. We use the first free transmit block + * to make our configuration. In the buffer area, we create the three + * configuration commands (linked). We make the previous NOP point to + * the beginning of the buffer instead of the tx command. After, we go + * as usual to the NOP command. + * Note that only the last command (mc_set) will generate an interrupt. + * + * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit()) + */ +static void wv_82586_config(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + unsigned short txblock; + unsigned short txpred; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short cfg_addr; + unsigned short ias_addr; + unsigned short mcs_addr; + ac_tx_t tx; + ac_nop_t nop; + ac_cfg_t cfg; /* Configure action */ + ac_ias_t ias; /* IA-setup action */ + ac_mcs_t mcs; /* Multicast setup */ + struct dev_mc_list *dmi; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); +#endif + + /* Check nothing bad has happened */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n", + dev->name); +#endif + return; + } + + /* Calculate addresses of next block and previous block. */ + txblock = lp->tx_first_free; + txpred = txblock - TXBLOCKZ; + if (txpred < OFFSET_CU) + txpred += NTXBLOCKS * TXBLOCKZ; + lp->tx_first_free += TXBLOCKZ; + if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + + lp->tx_n_in_use++; + + /* Calculate addresses of the different parts of the block. */ + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */ + ias_addr = cfg_addr + sizeof(cfg); + mcs_addr = ias_addr + sizeof(ias); + + /* + * Transmit command + */ + tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */ + obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), + (unsigned char *) &tx.tx_h.ac_status, + sizeof(tx.tx_h.ac_status)); + + /* + * NOP command + */ + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Create a configure action. */ + memset(&cfg, 0x00, sizeof(cfg)); + + /* + * For Linux we invert AC_CFG_ALOC() so as to conform + * to the way that net packets reach us from above. + * (See also ac_tx_t.) + * + * Updated from Wavelan Manual WCIN085B + */ + cfg.cfg_byte_cnt = + AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); + cfg.cfg_fifolim = AC_CFG_FIFOLIM(4); + cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0); + cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | + AC_CFG_ILPBCK(0) | + AC_CFG_PRELEN(AC_CFG_PLEN_2) | + AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); + cfg.cfg_byte10 = AC_CFG_BOFMET(1) | + AC_CFG_ACR(6) | AC_CFG_LINPRIO(0); + cfg.cfg_ifs = 0x20; + cfg.cfg_slotl = 0x0C; + cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0); + cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | + AC_CFG_BTSTF(0) | + AC_CFG_CRC16(0) | + AC_CFG_NCRC(0) | + AC_CFG_TNCRS(1) | + AC_CFG_MANCH(0) | + AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous); + cfg.cfg_byte15 = AC_CFG_ICDS(0) | + AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0); +/* + cfg.cfg_min_frm_len = AC_CFG_MNFRM(64); +*/ + cfg.cfg_min_frm_len = AC_CFG_MNFRM(8); + + cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure); + cfg.cfg_h.ac_link = ias_addr; + obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg)); + + /* Set up the MAC address */ + memset(&ias, 0x00, sizeof(ias)); + ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup); + ias.ias_h.ac_link = mcs_addr; + memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0], + sizeof(ias.ias_addr)); + obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias)); + + /* Initialize adapter's Ethernet multicast addresses */ + memset(&mcs, 0x00, sizeof(mcs)); + mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup); + mcs.mcs_h.ac_link = nop_addr; + mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count; + obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs)); + + /* Any address to set? */ + if (lp->mc_count) { + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr, + WAVELAN_ADDR_SIZE >> 1); + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wv_82586_config(): set %d multicast addresses:\n", + dev->name, lp->mc_count); + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); +#endif + } + + /* + * Overwrite the predecessor NOP link + * so that it points to the configure action. + */ + nop_addr = txpred + sizeof(tx); + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = cfg_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Job done, clear the flag */ + lp->reconfig_82586 = 0; + + if (lp->tx_first_in_use == I82586NULL) + lp->tx_first_in_use = txblock; + + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) + netif_stop_queue(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine, called by wavelan_close(), gracefully stops the + * WaveLAN controller (i82586). + * (called by wavelan_close()) + */ +static void wv_82586_stop(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + u16 scb_cmd; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name); +#endif + + /* Suspend both command unit and receive unit. */ + scb_cmd = + (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & + SCB_CMD_RUC_SUS); + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cmd, sizeof(scb_cmd)); + set_chan_attn(ioaddr, lp->hacr); + + /* No more interrupts */ + wv_ints_off(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Totally reset the WaveLAN and restart it. + * Performs the following actions: + * 1. A power reset (reset DMA) + * 2. Initialize the radio modem (using wv_mmc_init) + * 3. Reset & Configure LAN controller (using wv_82586_start) + * 4. Start the LAN controller's command unit + * 5. Start the LAN controller's receive unit + * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open()) + */ +static int wv_hw_reset(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Increase the number of resets done. */ + lp->nresets++; + + wv_hacr_reset(ioaddr); + lp->hacr = HACR_DEFAULT; + + if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0)) + return -1; + + /* Enable the card to send interrupts. */ + wv_ints_on(dev); + + /* Start card functions */ + if (wv_cu_start(dev) < 0) + return -1; + + /* Setup the controller and parameters */ + wv_82586_config(dev); + + /* Finish configuration with the receive unit */ + if (wv_ru_start(dev) < 0) + return -1; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Check if there is a WaveLAN at the specific base address. + * As a side effect, this reads the MAC address. + * (called in wavelan_probe() and init_module()) + */ +static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac) +{ + int i; /* Loop counter */ + + /* Check if the base address if available. */ + if (!request_region(ioaddr, sizeof(ha_t), "wavelan probe")) + return -EBUSY; /* ioaddr already used */ + + /* Reset host interface */ + wv_hacr_reset(ioaddr); + + /* Read the MAC address from the parameter storage area. */ + psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), + mac, 6); + + release_region(ioaddr, sizeof(ha_t)); + + /* + * Check the first three octets of the address for the manufacturer's code. + * Note: if this can't find your WaveLAN card, you've got a + * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on + * how to configure your card. + */ + for (i = 0; i < ARRAY_SIZE(MAC_ADDRESSES); i++) + if ((mac[0] == MAC_ADDRESSES[i][0]) && + (mac[1] == MAC_ADDRESSES[i][1]) && + (mac[2] == MAC_ADDRESSES[i][2])) + return 0; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_WARNING + "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n", + ioaddr, mac[0], mac[1], mac[2]); +#endif + return -ENODEV; +} + +/************************ INTERRUPT HANDLING ************************/ + +/* + * This function is the interrupt handler for the WaveLAN card. This + * routine will be called whenever: + */ +static irqreturn_t wavelan_interrupt(int irq, void *dev_id) +{ + struct net_device *dev; + unsigned long ioaddr; + net_local *lp; + u16 hasr; + u16 status; + u16 ack_cmd; + + dev = dev_id; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); +#endif + + lp = netdev_priv(dev); + ioaddr = dev->base_addr; + +#ifdef DEBUG_INTERRUPT_INFO + /* Check state of our spinlock */ + if(spin_is_locked(&lp->spinlock)) + printk(KERN_DEBUG + "%s: wavelan_interrupt(): spinlock is already locked !!!\n", + dev->name); +#endif + + /* Prevent reentrancy. We need to do that because we may have + * multiple interrupt handler running concurrently. + * It is safe because interrupts are disabled before acquiring + * the spinlock. */ + spin_lock(&lp->spinlock); + + /* We always had spurious interrupts at startup, but lately I + * saw them comming *between* the request_irq() and the + * spin_lock_irqsave() in wavelan_open(), so the spinlock + * protection is no enough. + * So, we also check lp->hacr that will tell us is we enabled + * irqs or not (see wv_ints_on()). + * We can't use netif_running(dev) because we depend on the + * proper processing of the irq generated during the config. */ + + /* Which interrupt it is ? */ + hasr = hasr_read(ioaddr); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_INFO + "%s: wavelan_interrupt(): hasr 0x%04x; hacr 0x%04x.\n", + dev->name, hasr, lp->hacr); +#endif + + /* Check modem interrupt */ + if ((hasr & HASR_MMC_INTR) && (lp->hacr & HACR_MMC_INT_ENABLE)) { + u8 dce_status; + + /* + * Interrupt from the modem management controller. + * This will clear it -- ignored for now. + */ + mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, + sizeof(dce_status)); + +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", + dev->name, dce_status); +#endif + } + + /* Check if not controller interrupt */ + if (((hasr & HASR_82586_INTR) == 0) || + ((lp->hacr & HACR_82586_INT_ENABLE) == 0)) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): interrupt not coming from i82586 - hasr 0x%04x.\n", + dev->name, hasr); +#endif + spin_unlock (&lp->spinlock); + return IRQ_NONE; + } + + /* Read interrupt data. */ + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &status, sizeof(status)); + + /* + * Acknowledge the interrupt(s). + */ + ack_cmd = status & SCB_ST_INT; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &ack_cmd, sizeof(ack_cmd)); + set_chan_attn(ioaddr, lp->hacr); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n", + dev->name, status); +#endif + + /* Command completed. */ + if ((status & SCB_ST_CX) == SCB_ST_CX) { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_interrupt(): command completed.\n", + dev->name); +#endif + wv_complete(dev, ioaddr, lp); + } + + /* Frame received. */ + if ((status & SCB_ST_FR) == SCB_ST_FR) { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_interrupt(): received packet.\n", + dev->name); +#endif + wv_receive(dev); + } + + /* Check the state of the command unit. */ + if (((status & SCB_ST_CNA) == SCB_ST_CNA) || + (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && + (netif_running(dev)))) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): CU inactive -- restarting\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* Check the state of the command unit. */ + if (((status & SCB_ST_RNR) == SCB_ST_RNR) || + (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && + (netif_running(dev)))) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): RU not ready -- restarting\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* Release spinlock */ + spin_unlock (&lp->spinlock); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif + return IRQ_HANDLED; +} + +/*------------------------------------------------------------------*/ +/* + * Watchdog: when we start a transmission, a timer is set for us in the + * kernel. If the transmission completes, this timer is disabled. If + * the timer expires, we are called and we try to unlock the hardware. + */ +static void wavelan_watchdog(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + u_long ioaddr = dev->base_addr; + unsigned long flags; + unsigned int nreaped; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); +#endif + +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", + dev->name); +#endif + + /* Check that we came here for something */ + if (lp->tx_n_in_use <= 0) { + return; + } + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Try to see if some buffers are not free (in case we missed + * an interrupt */ + nreaped = wv_complete(dev, ioaddr, lp); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_watchdog(): %d reaped, %d remain.\n", + dev->name, nreaped, lp->tx_n_in_use); +#endif + +#ifdef DEBUG_PSA_SHOW + { + psa_t psa; + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + wv_psa_show(&psa); + } +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82586_SHOW + wv_cu_show(dev); +#endif + + /* If no buffer has been freed */ + if (nreaped == 0) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_watchdog(): cleanup failed, trying reset\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* At this point, we should have some free Tx buffer ;-) */ + if (lp->tx_n_in_use < NTXBLOCKS - 1) + netif_wake_queue(dev); + + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); +#endif +} + +/********************* CONFIGURATION CALLBACKS *********************/ +/* + * Here are the functions called by the Linux networking code (NET3) + * for initialization, configuration and deinstallations of the + * WaveLAN ISA hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Configure and start up the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "opens" the device. + */ +static int wavelan_open(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Check irq */ + if (dev->irq == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", + dev->name); +#endif + return -ENXIO; + } + + if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) + { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", + dev->name); +#endif + return -EAGAIN; + } + + spin_lock_irqsave(&lp->spinlock, flags); + + if (wv_hw_reset(dev) != -1) { + netif_start_queue(dev); + } else { + free_irq(dev->irq, dev); +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_open(): impossible to start the card\n", + dev->name); +#endif + spin_unlock_irqrestore(&lp->spinlock, flags); + return -EAGAIN; + } + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Shut down the WaveLAN ISA card. + * Called by NET3 when it "closes" the device. + */ +static int wavelan_close(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + netif_stop_queue(dev); + + /* + * Flush the Tx and disable Rx. + */ + spin_lock_irqsave(&lp->spinlock, flags); + wv_82586_stop(dev); + spin_unlock_irqrestore(&lp->spinlock, flags); + + free_irq(dev->irq, dev); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); +#endif + return 0; +} + +static const struct net_device_ops wavelan_netdev_ops = { + .ndo_open = wavelan_open, + .ndo_stop = wavelan_close, + .ndo_start_xmit = wavelan_packet_xmit, + .ndo_set_multicast_list = wavelan_set_multicast_list, + .ndo_tx_timeout = wavelan_watchdog, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +#ifdef SET_MAC_ADDRESS + .ndo_set_mac_address = wavelan_set_mac_address +#else + .ndo_set_mac_address = eth_mac_addr, +#endif +}; + + +/*------------------------------------------------------------------*/ +/* + * Probe an I/O address, and if the WaveLAN is there configure the + * device structure + * (called by wavelan_probe() and via init_module()). + */ +static int __init wavelan_config(struct net_device *dev, unsigned short ioaddr) +{ + u8 irq_mask; + int irq; + net_local *lp; + mac_addr mac; + int err; + + if (!request_region(ioaddr, sizeof(ha_t), "wavelan")) + return -EADDRINUSE; + + err = wv_check_ioaddr(ioaddr, mac); + if (err) + goto out; + + memcpy(dev->dev_addr, mac, 6); + + dev->base_addr = ioaddr; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n", + dev->name, (unsigned int) dev, ioaddr); +#endif + + /* Check IRQ argument on command line. */ + if (dev->irq != 0) { + irq_mask = wv_irq_to_psa(dev->irq); + + if (irq_mask == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "%s: wavelan_config(): invalid IRQ %d ignored.\n", + dev->name, dev->irq); +#endif + dev->irq = 0; + } else { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wavelan_config(): changing IRQ to %d\n", + dev->name, dev->irq); +#endif + psa_write(ioaddr, HACR_DEFAULT, + psaoff(0, psa_int_req_no), &irq_mask, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, HACR_DEFAULT); + wv_hacr_reset(ioaddr); + } + } + + psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), + &irq_mask, 1); + if ((irq = wv_psa_to_irq(irq_mask)) == -1) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_config(): could not wavelan_map_irq(%d).\n", + dev->name, irq_mask); +#endif + err = -EAGAIN; + goto out; + } + + dev->irq = irq; + + dev->mem_start = 0x0000; + dev->mem_end = 0x0000; + dev->if_port = 0; + + /* Initialize device structures */ + memset(netdev_priv(dev), 0, sizeof(net_local)); + lp = netdev_priv(dev); + + /* Back link to the device structure. */ + lp->dev = dev; + /* Add the device at the beginning of the linked list. */ + lp->next = wavelan_list; + wavelan_list = lp; + + lp->hacr = HACR_DEFAULT; + + /* Multicast stuff */ + lp->promiscuous = 0; + lp->mc_count = 0; + + /* Init spinlock */ + spin_lock_init(&lp->spinlock); + + dev->netdev_ops = &wavelan_netdev_ops; + dev->watchdog_timeo = WATCHDOG_JIFFIES; + dev->wireless_handlers = &wavelan_handler_def; + lp->wireless_data.spy_data = &lp->spy_data; + dev->wireless_data = &lp->wireless_data; + + dev->mtu = WAVELAN_MTU; + + /* Display nice information. */ + wv_init_info(dev); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name); +#endif + return 0; +out: + release_region(ioaddr, sizeof(ha_t)); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Check for a network adaptor of this type. Return '0' iff one + * exists. There seem to be different interpretations of + * the initial value of dev->base_addr. + * We follow the example in drivers/net/ne.c. + * (called in "Space.c") + */ +struct net_device * __init wavelan_probe(int unit) +{ + struct net_device *dev; + short base_addr; + int def_irq; + int i; + int r = 0; + + /* compile-time check the sizes of structures */ + BUILD_BUG_ON(sizeof(psa_t) != PSA_SIZE); + BUILD_BUG_ON(sizeof(mmw_t) != MMW_SIZE); + BUILD_BUG_ON(sizeof(mmr_t) != MMR_SIZE); + BUILD_BUG_ON(sizeof(ha_t) != HA_SIZE); + + dev = alloc_etherdev(sizeof(net_local)); + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + base_addr = dev->base_addr; + def_irq = dev->irq; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG + "%s: ->wavelan_probe(dev=%p (base_addr=0x%x))\n", + dev->name, dev, (unsigned int) dev->base_addr); +#endif + + /* Don't probe at all. */ + if (base_addr < 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "%s: wavelan_probe(): invalid base address\n", + dev->name); +#endif + r = -ENXIO; + } else if (base_addr > 0x100) { /* Check a single specified location. */ + r = wavelan_config(dev, base_addr); +#ifdef DEBUG_CONFIG_INFO + if (r != 0) + printk(KERN_DEBUG + "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n", + dev->name, base_addr); +#endif + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); +#endif + } else { /* Scan all possible addresses of the WaveLAN hardware. */ + for (i = 0; i < ARRAY_SIZE(iobase); i++) { + dev->irq = def_irq; + if (wavelan_config(dev, iobase[i]) == 0) { +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG + "%s: <-wavelan_probe()\n", + dev->name); +#endif + break; + } + } + if (i == ARRAY_SIZE(iobase)) + r = -ENODEV; + } + if (r) + goto out; + r = register_netdev(dev); + if (r) + goto out1; + return dev; +out1: + release_region(dev->base_addr, sizeof(ha_t)); + wavelan_list = wavelan_list->next; +out: + free_netdev(dev); + return ERR_PTR(r); +} + +/****************************** MODULE ******************************/ +/* + * Module entry point: insertion and removal + */ + +#ifdef MODULE +/*------------------------------------------------------------------*/ +/* + * Insertion of the module + * I'm now quite proud of the multi-device support. + */ +int __init init_module(void) +{ + int ret = -EIO; /* Return error if no cards found */ + int i; + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> init_module()\n"); +#endif + + /* If probing is asked */ + if (io[0] == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "WaveLAN init_module(): doing device probing (bad !)\n"); + printk(KERN_WARNING + "Specify base addresses while loading module to correct the problem\n"); +#endif + + /* Copy the basic set of address to be probed. */ + for (i = 0; i < ARRAY_SIZE(iobase); i++) + io[i] = iobase[i]; + } + + + /* Loop on all possible base addresses. */ + for (i = 0; i < ARRAY_SIZE(io) && io[i] != 0; i++) { + struct net_device *dev = alloc_etherdev(sizeof(net_local)); + if (!dev) + break; + if (name[i]) + strcpy(dev->name, name[i]); /* Copy name */ + dev->base_addr = io[i]; + dev->irq = irq[i]; + + /* Check if there is something at this base address. */ + if (wavelan_config(dev, io[i]) == 0) { + if (register_netdev(dev) != 0) { + release_region(dev->base_addr, sizeof(ha_t)); + wavelan_list = wavelan_list->next; + } else { + ret = 0; + continue; + } + } + free_netdev(dev); + } + +#ifdef DEBUG_CONFIG_ERROR + if (!wavelan_list) + printk(KERN_WARNING + "WaveLAN init_module(): no device found\n"); +#endif + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- init_module()\n"); +#endif + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Removal of the module + */ +void cleanup_module(void) +{ +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> cleanup_module()\n"); +#endif + + /* Loop on all devices and release them. */ + while (wavelan_list) { + struct net_device *dev = wavelan_list->dev; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: cleanup_module(): removing device at 0x%x\n", + dev->name, (unsigned int) dev); +#endif + unregister_netdev(dev); + + release_region(dev->base_addr, sizeof(ha_t)); + wavelan_list = wavelan_list->next; + + free_netdev(dev); + } + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- cleanup_module()\n"); +#endif +} +#endif /* MODULE */ +MODULE_LICENSE("GPL"); + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + * Ajay Bakre (bakre@paul.rutgers.edu), + * Donald Becker (becker@scyld.com), + * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), + * Anders Klemets (klemets@it.kth.se), + * Vladimir V. Kolpakov (w@stier.koenig.ru), + * Marc Meertens (Marc.Meertens@Utrecht.NCR.com), + * Pauline Middelink (middelin@polyware.iaf.nl), + * Robert Morris (rtm@das.harvard.edu), + * Jean Tourrilhes (jt@hplb.hpl.hp.com), + * Girish Welling (welling@paul.rutgers.edu), + * + * Thanks go also to: + * James Ashton (jaa101@syseng.anu.edu.au), + * Alan Cox (alan@lxorguk.ukuu.org.uk), + * Allan Creighton (allanc@cs.usyd.edu.au), + * Matthew Geier (matthew@cs.usyd.edu.au), + * Remo di Giovanni (remo@cs.usyd.edu.au), + * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), + * Vipul Gupta (vgupta@cs.binghamton.edu), + * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + * Tim Nicholson (tim@cs.usyd.edu.au), + * Ian Parkin (ian@cs.usyd.edu.au), + * John Rosenberg (johnr@cs.usyd.edu.au), + * George Rossi (george@phm.gov.au), + * Arthur Scott (arthur@cs.usyd.edu.au), + * Peter Storey, + * for their assistance and advice. + * + * Please send bug reports, updates, comments to: + * + * Bruce Janson Email: bruce@cs.usyd.edu.au + * Basser Department of Computer Science Phone: +61-2-9351-3423 + * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838 + */ diff --git a/drivers/staging/wavelan/wavelan.h b/drivers/staging/wavelan/wavelan.h new file mode 100644 index 000000000000..9ab360558ffd --- /dev/null +++ b/drivers/staging/wavelan/wavelan.h @@ -0,0 +1,370 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follows. See wavelan.p.h for details. + * + * This file contains the declarations for the WaveLAN hardware. Note that + * the WaveLAN ISA includes a i82586 controller (see definitions in + * file i82586.h). + * + * The main difference between the ISA hardware and the PCMCIA one is + * the Ethernet controller (i82586 instead of i82593). + * The i82586 allows multiple transmit buffers. The PSA needs to be accessed + * through the host interface. + */ + +#ifndef _WAVELAN_H +#define _WAVELAN_H + +/************************** MAGIC NUMBERS ***************************/ + +/* Detection of the WaveLAN card is done by reading the MAC + * address from the card and checking it. If you have a non-AT&T + * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson), + * you might need to modify this part to accommodate your hardware. + */ +static const char MAC_ADDRESSES[][3] = +{ + { 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */ + { 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */ + { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ + { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ + /* Add your card here and send me the patch! */ +}; + +#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */ + +#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */ + +#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU) + +/* + * Constants used to convert channels to frequencies + */ + +/* Frequency available in the 2.0 modem, in units of 250 kHz + * (as read in the offset register of the dac area). + * Used to map channel numbers used by `wfreqsel' to frequencies + */ +static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, + 0xD0, 0xF0, 0xF8, 0x150 }; + +/* Frequencies of the 1.0 modem (fixed frequencies). + * Use to map the PSA `subband' to a frequency + * Note : all frequencies apart from the first one need to be multiplied by 10 + */ +static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; + + + +/*************************** PC INTERFACE ****************************/ + +/* + * Host Adaptor structure. + * (base is board port address). + */ +typedef union hacs_u hacs_u; +union hacs_u +{ + unsigned short hu_command; /* Command register */ +#define HACR_RESET 0x0001 /* Reset board */ +#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */ +#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */ +#define HACR_OUT0 0x0008 /* General purpose output pin 0 */ + /* not used - must be 1 */ +#define HACR_OUT1 0x0010 /* General purpose output pin 1 */ + /* not used - must be 1 */ +#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */ +#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */ +#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */ + unsigned short hu_status; /* Status Register */ +#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */ +#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */ +#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */ +#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */ +} __attribute__ ((packed)); + +typedef struct ha_t ha_t; +struct ha_t +{ + hacs_u ha_cs; /* Command and status registers */ +#define ha_command ha_cs.hu_command +#define ha_status ha_cs.hu_status + unsigned short ha_mmcr; /* Modem Management Ctrl Register */ + unsigned short ha_pior0; /* Program I/O Address Register Port 0 */ + unsigned short ha_piop0; /* Program I/O Port 0 */ + unsigned short ha_pior1; /* Program I/O Address Register Port 1 */ + unsigned short ha_piop1; /* Program I/O Port 1 */ + unsigned short ha_pior2; /* Program I/O Address Register Port 2 */ + unsigned short ha_piop2; /* Program I/O Port 2 */ +}; + +#define HA_SIZE 16 + +#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0) +#define HACR(p) hoff(p, ha_command) +#define HASR(p) hoff(p, ha_status) +#define MMCR(p) hoff(p, ha_mmcr) +#define PIOR0(p) hoff(p, ha_pior0) +#define PIOP0(p) hoff(p, ha_piop0) +#define PIOR1(p) hoff(p, ha_pior1) +#define PIOP1(p) hoff(p, ha_piop1) +#define PIOR2(p) hoff(p, ha_pior2) +#define PIOP2(p) hoff(p, ha_piop2) + +/* + * Program I/O Mode Register values. + */ +#define STATIC_PIO 0 /* Mode 1: static mode */ + /* RAM access ??? */ +#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */ + /* RAM access ??? */ +#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */ + /* RAM access ??? */ +#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */ + /* Parameter access. */ +#define PIO_MASK 3 /* register mask */ +#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2)) + +#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2)) +#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE) + +/************************** MEMORY LAYOUT **************************/ + +/* + * Onboard 64 k RAM layout. + * (Offsets from 0x0000.) + */ +#define OFFSET_RU 0x0000 /* 75% memory */ +#define OFFSET_CU 0xC000 /* 25% memory */ +#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t)) +#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t)) +#define OFFSET_SCP I82586_SCP_ADDR + +#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ) +#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ) + +#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ) +#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ) + +/********************** PARAMETER STORAGE AREA **********************/ + +/* + * Parameter Storage Area (PSA). + */ +typedef struct psa_t psa_t; +struct psa_t +{ + unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */ + unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */ + unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */ + unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */ + unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */ + unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */ + unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */ + unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */ + unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */ + unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */ + + unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */ + unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */ + unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */ +#define PSA_UNIVERSAL 0 /* Universal (factory) */ +#define PSA_LOCAL 1 /* Local */ + unsigned char psa_comp_number; /* [0x1D] Compatibility Number: */ +#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */ +#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */ +#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */ +#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */ +#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */ + unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */ + unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */ +#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */ + unsigned char psa_subband; /* [0x20] Subband */ +#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */ +#define PSA_SUBBAND_2425 1 /* 2425 MHz */ +#define PSA_SUBBAND_2460 2 /* 2460 MHz */ +#define PSA_SUBBAND_2484 3 /* 2484 MHz */ +#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */ + unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */ + unsigned char psa_mod_delay; /* [0x22] Modem Delay (?) (reserved) */ + unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */ + unsigned char psa_nwid_select; /* [0x25] Network ID Select On/Off */ + unsigned char psa_encryption_select; /* [0x26] Encryption On/Off */ + unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */ + unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */ + unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */ + unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */ + unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */ + unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/ + unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */ + unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */ +}; + +#define PSA_SIZE 64 + +/* Calculate offset of a field in the above structure. + * Warning: only even addresses are used. */ +#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) + +/******************** MODEM MANAGEMENT INTERFACE ********************/ + +/* + * Modem Management Controller (MMC) write structure. + */ +typedef struct mmw_t mmw_t; +struct mmw_t +{ + unsigned char mmw_encr_key[8]; /* encryption key */ + unsigned char mmw_encr_enable; /* Enable or disable encryption. */ +#define MMW_ENCR_ENABLE_MODE 0x02 /* mode of security option */ +#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option. */ + unsigned char mmw_unused0[1]; /* unused */ + unsigned char mmw_des_io_invert; /* encryption option */ +#define MMW_DES_IO_INVERT_RES 0x0F /* reserved */ +#define MMW_DES_IO_INVERT_CTRL 0xF0 /* control (?) (set to 0) */ + unsigned char mmw_unused1[5]; /* unused */ + unsigned char mmw_loopt_sel; /* looptest selection */ +#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* Disable NWID filtering. */ +#define MMW_LOOPT_SEL_INT 0x20 /* Activate Attention Request. */ +#define MMW_LOOPT_SEL_LS 0x10 /* looptest, no collision avoidance */ +#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */ +#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */ +#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */ +#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */ + unsigned char mmw_jabber_enable; /* jabber timer enable */ + /* Abort transmissions > 200 ms */ + unsigned char mmw_freeze; /* freeze or unfreeze signal level */ + /* 0 : signal level & qual updated for every new message, 1 : frozen */ + unsigned char mmw_anten_sel; /* antenna selection */ +#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */ +#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */ + unsigned char mmw_ifs; /* inter frame spacing */ + /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ + unsigned char mmw_mod_delay; /* modem delay (synchro) */ + unsigned char mmw_jam_time; /* jamming time (after collision) */ + unsigned char mmw_unused2[1]; /* unused */ + unsigned char mmw_thr_pre_set; /* level threshold preset */ + /* Discard all packet with signal < this value (4) */ + unsigned char mmw_decay_prm; /* decay parameters */ + unsigned char mmw_decay_updat_prm; /* decay update parameters */ + unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */ + /* Discard all packet with quality < this value (3) */ + unsigned char mmw_netw_id_l; /* NWID low order byte */ + unsigned char mmw_netw_id_h; /* NWID high order byte */ + /* Network ID or Domain : create virtual net on the air */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmw_mode_select; /* for analog tests (set to 0) */ + unsigned char mmw_unused3[1]; /* unused */ + unsigned char mmw_fee_ctrl; /* frequency EEPROM control */ +#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions. */ +#define MMW_FEE_CTRL_DWLD 0x08 /* Download EEPROM to mmc. */ +#define MMW_FEE_CTRL_CMD 0x07 /* EEPROM commands: */ +#define MMW_FEE_CTRL_READ 0x06 /* Read */ +#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */ +#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address. */ +#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses. */ +#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */ +#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */ +#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */ +#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers. */ +#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write address in protect register */ +#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ + /* Never issue the PRDS command: it's irreversible! */ + + unsigned char mmw_fee_addr; /* EEPROM address */ +#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel. */ +#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */ +#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */ +#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */ +#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */ +#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */ + + unsigned char mmw_fee_data_l; /* Write data to EEPROM. */ + unsigned char mmw_fee_data_h; /* high octet */ + unsigned char mmw_ext_ant; /* Setting for external antenna */ +#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */ +#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */ +#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */ +#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */ +#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */ +} __attribute__ ((packed)); + +#define MMW_SIZE 37 + +#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * Modem Management Controller (MMC) read structure. + */ +typedef struct mmr_t mmr_t; +struct mmr_t +{ + unsigned char mmr_unused0[8]; /* unused */ + unsigned char mmr_des_status; /* encryption status */ + unsigned char mmr_des_avail; /* encryption available (0x55 read) */ +#define MMR_DES_AVAIL_DES 0x55 /* DES available */ +#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */ + unsigned char mmr_des_io_invert; /* des I/O invert register */ + unsigned char mmr_unused1[5]; /* unused */ + unsigned char mmr_dce_status; /* DCE status */ +#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */ +#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ +#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ +#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ +#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ + unsigned char mmr_dsp_id; /* DSP ID (AA = Daedalus rev A) */ + unsigned char mmr_unused2[2]; /* unused */ + unsigned char mmr_correct_nwid_l; /* # of correct NWIDs rxd (low) */ + unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */ + /* Warning: read high-order octet first! */ + unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */ + unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs rxd (high) */ + unsigned char mmr_thr_pre_set; /* level threshold preset */ +#define MMR_THR_PRE_SET 0x3F /* level threshold preset */ +#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */ + unsigned char mmr_signal_lvl; /* signal level */ +#define MMR_SIGNAL_LVL 0x3F /* signal level */ +#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_silence_lvl; /* silence level (noise) */ +#define MMR_SILENCE_LVL 0x3F /* silence level */ +#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_sgnl_qual; /* signal quality */ +#define MMR_SGNL_QUAL 0x0F /* signal quality */ +#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */ + unsigned char mmr_netw_id_l; /* NWID low order byte (?) */ + unsigned char mmr_unused3[3]; /* unused */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmr_fee_status; /* Status of frequency EEPROM */ +#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision ID */ +#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */ +#define MMR_FEE_STATUS_BUSY 0x04 /* EEPROM busy */ + unsigned char mmr_unused4[1]; /* unused */ + unsigned char mmr_fee_data_l; /* Read data from EEPROM (low) */ + unsigned char mmr_fee_data_h; /* Read data from EEPROM (high) */ +} __attribute__ ((packed)); + +#define MMR_SIZE 36 + +#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) + +/* Make the two above structures one */ +typedef union mm_t +{ + struct mmw_t w; /* Write to the mmc */ + struct mmr_t r; /* Read from the mmc */ +} mm_t; + +#endif /* _WAVELAN_H */ + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * For more details, see wavelan.c. + */ diff --git a/drivers/staging/wavelan/wavelan.p.h b/drivers/staging/wavelan/wavelan.p.h new file mode 100644 index 000000000000..dbe8de6e5f52 --- /dev/null +++ b/drivers/staging/wavelan/wavelan.p.h @@ -0,0 +1,696 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * + * This file contains all definitions and declarations necessary for the + * WaveLAN ISA driver. This file is a private header, so it should + * be included only in wavelan.c! + */ + +#ifndef WAVELAN_P_H +#define WAVELAN_P_H + +/************************** DOCUMENTATION ***************************/ +/* + * This driver provides a Linux interface to the WaveLAN ISA hardware. + * The WaveLAN is a product of Lucent (http://www.wavelan.com/). + * This division was formerly part of NCR and then AT&T. + * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean. + * + * To learn how to use this driver, read the NET3 HOWTO. + * If you want to exploit the many other functionalities, read the comments + * in the code. + * + * This driver is the result of the effort of many people (see below). + */ + +/* ------------------------ SPECIFIC NOTES ------------------------ */ +/* + * Web page + * -------- + * I try to maintain a web page with the Wireless LAN Howto at : + * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html + * + * SMP + * --- + * We now are SMP compliant (I eventually fixed the remaining bugs). + * The driver has been tested on a dual P6-150 and survived my usual + * set of torture tests. + * Anyway, I spent enough time chasing interrupt re-entrancy during + * errors or reconfigure, and I designed the locked/unlocked sections + * of the driver with great care, and with the recent addition of + * the spinlock (thanks to the new API), we should be quite close to + * the truth. + * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), + * but better safe than sorry (especially at 2 Mb/s ;-). + * + * I have also looked into disabling only our interrupt on the card + * (via HACR) instead of all interrupts in the processor (via cli), + * so that other driver are not impacted, and it look like it's + * possible, but it's very tricky to do right (full of races). As + * the gain would be mostly for SMP systems, it can wait... + * + * Debugging and options + * --------------------- + * You will find below a set of '#define" allowing a very fine control + * on the driver behaviour and the debug messages printed. + * The main options are : + * o SET_PSA_CRC, to have your card correctly recognised by + * an access point and the Point-to-Point diagnostic tool. + * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) + * (otherwise we always start afresh with some defaults) + * + * wavelan.o is too darned big + * --------------------------- + * That's true! There is a very simple way to reduce the driver + * object by 33%! Comment out the following line: + * #include + * Other compile options can also reduce the size of it... + * + * MAC address and hardware detection: + * ----------------------------------- + * The detection code for the WaveLAN checks that the first three + * octets of the MAC address fit the company code. This type of + * detection works well for AT&T cards (because the AT&T code is + * hardcoded in wavelan.h), but of course will fail for other + * manufacturers. + * + * If you are sure that your card is derived from the WaveLAN, + * here is the way to configure it: + * 1) Get your MAC address + * a) With your card utilities (wfreqsel, instconf, etc.) + * b) With the driver: + * o compile the kernel with DEBUG_CONFIG_INFO enabled + * o Boot and look the card messages + * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h) + * 3) Compile and verify + * 4) Send me the MAC code. I will include it in the next version. + * + */ + +/* --------------------- WIRELESS EXTENSIONS --------------------- */ +/* + * This driver is the first to support "wireless extensions". + * This set of extensions provides a standard way to control the wireless + * characteristics of the hardware. Applications such as mobile IP may + * take advantage of it. + * + * It might be a good idea as well to fetch the wireless tools to + * configure the device and play a bit. + */ + +/* ---------------------------- FILES ---------------------------- */ +/* + * wavelan.c: actual code for the driver: C functions + * + * wavelan.p.h: private header: local types and variables for driver + * + * wavelan.h: description of the hardware interface and structs + * + * i82586.h: description of the Ethernet controller + */ + +/* --------------------------- HISTORY --------------------------- */ +/* + * This is based on information in the drivers' headers. It may not be + * accurate, and I guarantee only my best effort. + * + * The history of the WaveLAN drivers is as complicated as the history of + * the WaveLAN itself (NCR -> AT&T -> Lucent). + * + * It all started with Anders Klemets + * writing a WaveLAN ISA driver for the Mach microkernel. Girish + * Welling had also worked on it. + * Keith Moore modified this for the PCMCIA hardware. + * + * Robert Morris ported these two drivers to BSDI + * and added specific PCMCIA support (there is currently no equivalent + * of the PCMCIA package under BSD). + * + * Jim Binkley ported both BSDI drivers to FreeBSD. + * + * Bruce Janson ported the BSDI ISA driver to Linux. + * + * Anthony D. Joseph started to modify Bruce's driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li finished this work. + * Joe Finney patched the driver to start + * 2.00 cards correctly (2.4 GHz with frequency selection). + * David Hinds integrated the whole in his + * PCMCIA package (and bug corrections). + * + * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some + * patches to the PCMCIA driver. Later, I added code in the ISA driver + * for Wireless Extensions and full support of frequency selection + * cards. Then, I did the same to the PCMCIA driver, and did some + * reorganisation. Finally, I came back to the ISA driver to + * upgrade it at the same level as the PCMCIA one and reorganise + * the code. + * Loeke Brederveld from Lucent has given me + * much needed information on the WaveLAN hardware. + */ + +/* The original copyrights and literature mention others' names and + * credits. I don't know what their part in this development was. + */ + +/* By the way, for the copyright and legal stuff: + * almost everybody wrote code under the GNU or BSD license (or similar), + * and want their original copyright to remain somewhere in the + * code (for myself, I go with the GPL). + * Nobody wants to take responsibility for anything, except the fame. + */ + +/* --------------------------- CREDITS --------------------------- */ +/* + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + * Ajay Bakre , + * Donald Becker , + * Loeke Brederveld , + * Brent Elphick , + * Anders Klemets , + * Vladimir V. Kolpakov , + * Marc Meertens , + * Pauline Middelink , + * Robert Morris , + * Jean Tourrilhes , + * Girish Welling , + * Clark Woodworth + * Yongguang Zhang + * + * Thanks go also to: + * James Ashton , + * Alan Cox , + * Allan Creighton , + * Matthew Geier , + * Remo di Giovanni , + * Eckhard Grah , + * Vipul Gupta , + * Mark Hagan , + * Tim Nicholson , + * Ian Parkin , + * John Rosenberg , + * George Rossi , + * Arthur Scott , + * Stanislav Sinyagin + * and Peter Storey for their assistance and advice. + * + * Additional Credits: + * + * My development has been done initially under Debian 1.1 (Linux 2.0.x) + * and now under Debian 2.2, initially with an HP Vectra XP/60, and now + * an HP Vectra XP/90. + * + */ + +/* ------------------------- IMPROVEMENTS ------------------------- */ +/* + * I proudly present: + * + * Changes made in first pre-release: + * ---------------------------------- + * - reorganisation of the code, function name change + * - creation of private header (wavelan.p.h) + * - reorganised debug messages + * - more comments, history, etc. + * - mmc_init: configure the PSA if not done + * - mmc_init: correct default value of level threshold for PCMCIA + * - mmc_init: 2.00 detection better code for 2.00 initialization + * - better info at startup + * - IRQ setting (note: this setting is permanent) + * - watchdog: change strategy (and solve module removal problems) + * - add wireless extensions (ioctl and get_wireless_stats) + * get/set nwid/frequency on fly, info for /proc/net/wireless + * - more wireless extensions: SETSPY and GETSPY + * - make wireless extensions optional + * - private ioctl to set/get quality and level threshold, histogram + * - remove /proc/net/wavelan + * - suppress useless stuff from lp (net_local) + * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) + * - add message level (debug stuff in /var/adm/debug and errors not + * displayed at console and still in /var/adm/messages) + * - multi device support + * - start fixing the probe (init code) + * - more inlines + * - man page + * - many other minor details and cleanups + * + * Changes made in second pre-release: + * ----------------------------------- + * - clean up init code (probe and module init) + * - better multiple device support (module) + * - name assignment (module) + * + * Changes made in third pre-release: + * ---------------------------------- + * - be more conservative on timers + * - preliminary support for multicast (I still lack some details) + * + * Changes made in fourth pre-release: + * ----------------------------------- + * - multicast (revisited and finished) + * - avoid reset in set_multicast_list (a really big hack) + * if somebody could apply this code for other i82586 based drivers + * - share onboard memory 75% RU and 25% CU (instead of 50/50) + * + * Changes made for release in 2.1.15: + * ----------------------------------- + * - change the detection code for multi manufacturer code support + * + * Changes made for release in 2.1.17: + * ----------------------------------- + * - update to wireless extensions changes + * - silly bug in card initial configuration (psa_conf_status) + * + * Changes made for release in 2.1.27 & 2.0.30: + * -------------------------------------------- + * - small bug in debug code (probably not the last one...) + * - remove extern keyword for wavelan_probe() + * - level threshold is now a standard wireless extension (version 4 !) + * - modules parameters types (new module interface) + * + * Changes made for release in 2.1.36: + * ----------------------------------- + * - byte count stats (courtesy of David Hinds) + * - remove dev_tint stuff (courtesy of David Hinds) + * - encryption setting from Brent Elphick (thanks a lot!) + * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * + * Other changes (not by me) : + * ------------------------- + * - Spelling and gramar "rectification". + * + * Changes made for release in 2.0.37 & 2.2.2 : + * ------------------------------------------ + * - Correct status in /proc/net/wireless + * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray) + * - Module init code don't fail if we found at least one card in + * the address list (Karlis Peisenieks) + * - Missing parenthesis (Christopher Peterson) + * - Correct i82586 configuration parameters + * - Encryption initialisation bug (Robert McCormack) + * - New mac addresses detected in the probe + * - Increase watchdog for busy environments + * + * Changes made for release in 2.0.38 & 2.2.7 : + * ------------------------------------------ + * - Correct the reception logic to better report errors and avoid + * sending bogus packet up the stack + * - Delay RU config to avoid corrupting first received packet + * - Change config completion code (to actually check something) + * - Avoid reading out of bound in skbuf to transmit + * - Rectify a lot of (useless) debugging code + * - Change the way to `#ifdef SET_PSA_CRC' + * + * Changes made for release in 2.2.11 & 2.3.13 : + * ------------------------------------------- + * - Change e-mail and web page addresses + * - Watchdog timer is now correctly expressed in HZ, not in jiffies + * - Add channel number to the list of frequencies in range + * - Add the (short) list of bit-rates in range + * - Developp a new sensitivity... (sens.value & sens.fixed) + * + * Changes made for release in 2.2.14 & 2.3.23 : + * ------------------------------------------- + * - Fix check for root permission (break instead of exit) + * - New nwid & encoding setting (Wireless Extension 9) + * + * Changes made for release in 2.3.49 : + * ---------------------------------- + * - Indentation reformating (Alan) + * - Update to new network API (softnet - 2.3.43) : + * o replace dev->tbusy (Alan) + * o replace dev->tstart (Alan) + * o remove dev->interrupt (Alan) + * o add SMP locking via spinlock in splxx (me) + * o add spinlock in interrupt handler (me) + * o use kernel watchdog instead of ours (me) + * o increase watchdog timeout (kernel is more sensitive) (me) + * o verify that all the changes make sense and work (me) + * - Fixup a potential gotcha when reconfiguring and thighten a bit + * the interactions with Tx queue. + * + * Changes made for release in 2.4.0 : + * --------------------------------- + * - Fix spinlock stupid bugs that I left in. The driver is now SMP + * compliant and doesn't lockup at startup. + * + * Changes made for release in 2.5.2 : + * --------------------------------- + * - Use new driver API for Wireless Extensions : + * o got rid of wavelan_ioctl() + * o use a bunch of iw_handler instead + * + * Changes made for release in 2.5.35 : + * ---------------------------------- + * - Set dev->trans_start to avoid filling the logs + * - Handle better spurious/bogus interrupt + * - Avoid deadlocks in mmc_out()/mmc_in() + * + * Wishes & dreams: + * ---------------- + * - roaming (see Pcmcia driver) + */ + +/***************************** INCLUDES *****************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* Wireless extensions */ +#include /* Wireless handlers */ + +/* WaveLAN declarations */ +#include "i82586.h" +#include "wavelan.h" + +/************************** DRIVER OPTIONS **************************/ +/* + * `#define' or `#undef' the following constant to change the behaviour + * of the driver... + */ +#undef SET_PSA_CRC /* Calculate and set the CRC on PSA (slower) */ +#define USE_PSA_CONFIG /* Use info from the PSA. */ +#undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */ +#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical). */ +#undef SET_MAC_ADDRESS /* Experimental */ + +/* Warning: this stuff will slow down the driver. */ +#define WIRELESS_SPY /* Enable spying addresses. */ +#undef HISTOGRAM /* Enable histogram of signal level. */ + +/****************************** DEBUG ******************************/ + +#undef DEBUG_MODULE_TRACE /* module insertion/removal */ +#undef DEBUG_CALLBACK_TRACE /* calls made by Linux */ +#undef DEBUG_INTERRUPT_TRACE /* calls to handler */ +#undef DEBUG_INTERRUPT_INFO /* type of interrupt and so on */ +#define DEBUG_INTERRUPT_ERROR /* problems */ +#undef DEBUG_CONFIG_TRACE /* Trace the config functions. */ +#undef DEBUG_CONFIG_INFO /* what's going on */ +#define DEBUG_CONFIG_ERROR /* errors on configuration */ +#undef DEBUG_TX_TRACE /* transmission calls */ +#undef DEBUG_TX_INFO /* header of the transmitted packet */ +#undef DEBUG_TX_FAIL /* Normal failure conditions */ +#define DEBUG_TX_ERROR /* Unexpected conditions */ +#undef DEBUG_RX_TRACE /* transmission calls */ +#undef DEBUG_RX_INFO /* header of the received packet */ +#undef DEBUG_RX_FAIL /* Normal failure conditions */ +#define DEBUG_RX_ERROR /* Unexpected conditions */ + +#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */ +#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */ +#undef DEBUG_IOCTL_INFO /* various debugging info */ +#define DEBUG_IOCTL_ERROR /* what's going wrong */ +#define DEBUG_BASIC_SHOW /* Show basic startup info. */ +#undef DEBUG_VERSION_SHOW /* Print version info. */ +#undef DEBUG_PSA_SHOW /* Dump PSA to screen. */ +#undef DEBUG_MMC_SHOW /* Dump mmc to screen. */ +#undef DEBUG_SHOW_UNUSED /* Show unused fields too. */ +#undef DEBUG_I82586_SHOW /* Show i82586 status. */ +#undef DEBUG_DEVICE_SHOW /* Show device parameters. */ + +/************************ CONSTANTS & MACROS ************************/ + +#ifdef DEBUG_VERSION_SHOW +static const char *version = "wavelan.c : v24 (SMP + wireless extensions) 11/12/01\n"; +#endif + +/* Watchdog temporisation */ +#define WATCHDOG_JIFFIES (512*HZ/100) + +/* ------------------------ PRIVATE IOCTL ------------------------ */ + +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ + +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 2 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 3 /* Get histogram values */ + +/****************************** TYPES ******************************/ + +/* Shortcuts */ +typedef struct iw_statistics iw_stats; +typedef struct iw_quality iw_qual; +typedef struct iw_freq iw_freq;typedef struct net_local net_local; +typedef struct timer_list timer_list; + +/* Basic types */ +typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ + +/* + * Static specific data for the interface. + * + * For each network interface, Linux keeps data in two structures: "device" + * keeps the generic data (same format for everybody) and "net_local" keeps + * additional specific data. + */ +struct net_local +{ + net_local * next; /* linked list of the devices */ + struct net_device * dev; /* reverse link */ + spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ + int nresets; /* number of hardware resets */ + u_char reconfig_82586; /* We need to reconfigure the controller. */ + u_char promiscuous; /* promiscuous mode */ + int mc_count; /* number of multicast addresses */ + u_short hacr; /* current host interface state */ + + int tx_n_in_use; + u_short rx_head; + u_short rx_last; + u_short tx_first_free; + u_short tx_first_in_use; + + iw_stats wstats; /* Wireless-specific statistics */ + + struct iw_spy_data spy_data; + struct iw_public_data wireless_data; + +#ifdef HISTOGRAM + int his_number; /* number of intervals */ + u_char his_range[16]; /* boundaries of interval ]n-1; n] */ + u_long his_sum[16]; /* sum in interval */ +#endif /* HISTOGRAM */ +}; + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- MISC. SUBROUTINES ------------------------ */ +static u_char + wv_irq_to_psa(int); +static int + wv_psa_to_irq(u_char); +/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ +static inline u_short /* data */ + hasr_read(u_long); /* Read the host interface: base address */ +static inline void + hacr_write(u_long, /* Write to host interface: base address */ + u_short), /* data */ + hacr_write_slow(u_long, + u_short), + set_chan_attn(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_hacr_reset(u_long), /* ioaddr */ + wv_16_off(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_16_on(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_ints_off(struct net_device *), + wv_ints_on(struct net_device *); +/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ +static void + psa_read(u_long, /* Read the Parameter Storage Area. */ + u_short, /* hacr */ + int, /* offset in PSA */ + u_char *, /* buffer to fill */ + int), /* size to read */ + psa_write(u_long, /* Write to the PSA. */ + u_short, /* hacr */ + int, /* offset in PSA */ + u_char *, /* buffer in memory */ + int); /* length of buffer */ +static inline void + mmc_out(u_long, /* Write 1 byte to the Modem Manag Control. */ + u_short, + u_char), + mmc_write(u_long, /* Write n bytes to the MMC. */ + u_char, + u_char *, + int); +static inline u_char /* Read 1 byte from the MMC. */ + mmc_in(u_long, + u_short); +static inline void + mmc_read(u_long, /* Read n bytes from the MMC. */ + u_char, + u_char *, + int), + fee_wait(u_long, /* Wait for frequency EEPROM: base address */ + int, /* base delay to wait for */ + int); /* time to wait */ +static void + fee_read(u_long, /* Read the frequency EEPROM: base address */ + u_short, /* destination offset */ + u_short *, /* data buffer */ + int); /* number of registers */ +/* ---------------------- I82586 SUBROUTINES ----------------------- */ +static /*inline*/ void + obram_read(u_long, /* ioaddr */ + u_short, /* o */ + u_char *, /* b */ + int); /* n */ +static inline void + obram_write(u_long, /* ioaddr */ + u_short, /* o */ + u_char *, /* b */ + int); /* n */ +static void + wv_ack(struct net_device *); +static inline int + wv_synchronous_cmd(struct net_device *, + const char *), + wv_config_complete(struct net_device *, + u_long, + net_local *); +static int + wv_complete(struct net_device *, + u_long, + net_local *); +static inline void + wv_82586_reconfig(struct net_device *); +/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ +#ifdef DEBUG_I82586_SHOW +static void + wv_scb_show(unsigned short); +#endif +static inline void + wv_init_info(struct net_device *); /* display startup info */ +/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ +static iw_stats * + wavelan_get_wireless_stats(struct net_device *); +static void + wavelan_set_multicast_list(struct net_device *); +/* ----------------------- PACKET RECEPTION ----------------------- */ +static inline void + wv_packet_read(struct net_device *, /* Read a packet from a frame. */ + u_short, + int), + wv_receive(struct net_device *); /* Read all packets waiting. */ +/* --------------------- PACKET TRANSMISSION --------------------- */ +static inline int + wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer. */ + void *, + short); +static netdev_tx_t + wavelan_packet_xmit(struct sk_buff *, /* Send a packet. */ + struct net_device *); +/* -------------------- HARDWARE CONFIGURATION -------------------- */ +static inline int + wv_mmc_init(struct net_device *), /* Initialize the modem. */ + wv_ru_start(struct net_device *), /* Start the i82586 receiver unit. */ + wv_cu_start(struct net_device *), /* Start the i82586 command unit. */ + wv_82586_start(struct net_device *); /* Start the i82586. */ +static void + wv_82586_config(struct net_device *); /* Configure the i82586. */ +static inline void + wv_82586_stop(struct net_device *); +static int + wv_hw_reset(struct net_device *), /* Reset the WaveLAN hardware. */ + wv_check_ioaddr(u_long, /* ioaddr */ + u_char *); /* mac address (read) */ +/* ---------------------- INTERRUPT HANDLING ---------------------- */ +static irqreturn_t + wavelan_interrupt(int, /* interrupt handler */ + void *); +static void + wavelan_watchdog(struct net_device *); /* transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int + wavelan_open(struct net_device *), /* Open the device. */ + wavelan_close(struct net_device *), /* Close the device. */ + wavelan_config(struct net_device *, unsigned short);/* Configure one device. */ +extern struct net_device *wavelan_probe(int unit); /* See Space.c. */ + +/**************************** VARIABLES ****************************/ + +/* + * This is the root of the linked list of WaveLAN drivers + * It is use to verify that we don't reuse the same base address + * for two different drivers and to clean up when removing the module. + */ +static net_local * wavelan_list = (net_local *) NULL; + +/* + * This table is used to translate the PSA value to IRQ number + * and vice versa. + */ +static u_char irqvals[] = +{ + 0, 0, 0, 0x01, + 0x02, 0x04, 0, 0x08, + 0, 0, 0x10, 0x20, + 0x40, 0, 0, 0x80, +}; + +/* + * Table of the available I/O addresses (base addresses) for WaveLAN + */ +static unsigned short iobase[] = +{ +#if 0 + /* Leave out 0x3C0 for now -- seems to clash with some video + * controllers. + * Leave out the others too -- we will always use 0x390 and leave + * 0x300 for the Ethernet device. + * Jean II: 0x3E0 is fine as well. + */ + 0x300, 0x390, 0x3E0, 0x3C0 +#endif /* 0 */ + 0x390, 0x3E0 +}; + +#ifdef MODULE +/* Parameters set by insmod */ +static int io[4]; +static int irq[4]; +static char *name[4]; +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(name, charp, NULL, 0); + +MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required"); +MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)"); +MODULE_PARM_DESC(name, "WaveLAN interface neme(s)"); +#endif /* MODULE */ + +#endif /* WAVELAN_P_H */ diff --git a/drivers/staging/wavelan/wavelan_cs.c b/drivers/staging/wavelan/wavelan_cs.c new file mode 100644 index 000000000000..431a20ec6db6 --- /dev/null +++ b/drivers/staging/wavelan/wavelan_cs.c @@ -0,0 +1,4635 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follow. See wavelan_cs.p.h for details. + * + * This code is derived from Anthony D. Joseph's code and all the changes here + * are also under the original copyright below. + * + * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and + * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services + * + * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added + * critical code in the routine to initialize the Modem Management Controller. + * + * Thanks to Alan Cox and Bruce Janson for their advice. + * + * -- Yunzhou Li (scip4166@nus.sg) + * +#ifdef WAVELAN_ROAMING + * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu) + * based on patch by Joe Finney from Lancaster University. +#endif + * + * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An + * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor. + * + * A non-shared memory PCMCIA ethernet driver for linux + * + * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu) + * + * + * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu) + * + * Apr 2 '98 made changes to bring the i82593 control/int handling in line + * with offical specs... + * + **************************************************************************** + * Copyright 1995 + * Anthony D. Joseph + * Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this program + * for any purpose and without fee is hereby granted, provided + * that this copyright and permission notice appear on all copies + * and supporting documentation, the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * program without specific prior permission, and notice be given + * in supporting documentation that copying and distribution is + * by permission of M.I.T. M.I.T. makes no representations about + * the suitability of this software for any purpose. It is pro- + * vided "as is" without express or implied warranty. + **************************************************************************** + * + */ + +/* Do *NOT* add other headers here, you are guaranteed to be wrong - Jean II */ +#include "wavelan_cs.p.h" /* Private header */ + +#ifdef WAVELAN_ROAMING +static void wl_cell_expiry(unsigned long data); +static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp); +static void wv_nwid_filter(unsigned char mode, net_local *lp); +#endif /* WAVELAN_ROAMING */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (wavelan modem or i82593) + */ + +/******************* MODEM MANAGEMENT SUBROUTINES *******************/ +/* + * Useful subroutines to manage the modem of the wavelan + */ + +/*------------------------------------------------------------------*/ +/* + * Read from card's Host Adaptor Status Register. + */ +static inline u_char +hasr_read(u_long base) +{ + return(inb(HASR(base))); +} /* hasr_read */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. + */ +static inline void +hacr_write(u_long base, + u_char hacr) +{ + outb(hacr, HACR(base)); +} /* hacr_write */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. Include a delay for + * those times when it is needed. + */ +static void +hacr_write_slow(u_long base, + u_char hacr) +{ + hacr_write(base, hacr); + /* delay might only be needed sometimes */ + mdelay(1); +} /* hacr_write_slow */ + +/*------------------------------------------------------------------*/ +/* + * Read the Parameter Storage Area from the WaveLAN card's memory + */ +static void +psa_read(struct net_device * dev, + int o, /* offset in PSA */ + u_char * b, /* buffer to fill */ + int n) /* size to read */ +{ + net_local *lp = netdev_priv(dev); + u_char __iomem *ptr = lp->mem + PSA_ADDR + (o << 1); + + while(n-- > 0) + { + *b++ = readb(ptr); + /* Due to a lack of address decode pins, the WaveLAN PCMCIA card + * only supports reading even memory addresses. That means the + * increment here MUST be two. + * Because of that, we can't use memcpy_fromio()... + */ + ptr += 2; + } +} /* psa_read */ + +/*------------------------------------------------------------------*/ +/* + * Write the Parameter Storage Area to the WaveLAN card's memory + */ +static void +psa_write(struct net_device * dev, + int o, /* Offset in psa */ + u_char * b, /* Buffer in memory */ + int n) /* Length of buffer */ +{ + net_local *lp = netdev_priv(dev); + u_char __iomem *ptr = lp->mem + PSA_ADDR + (o << 1); + int count = 0; + unsigned int base = dev->base_addr; + /* As there seem to have no flag PSA_BUSY as in the ISA model, we are + * oblige to verify this address to know when the PSA is ready... */ + volatile u_char __iomem *verify = lp->mem + PSA_ADDR + + (psaoff(0, psa_comp_number) << 1); + + /* Authorize writing to PSA */ + hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN); + + while(n-- > 0) + { + /* write to PSA */ + writeb(*b++, ptr); + ptr += 2; + + /* I don't have the spec, so I don't know what the correct + * sequence to write is. This hack seem to work for me... */ + count = 0; + while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100)) + mdelay(1); + } + + /* Put the host interface back in standard state */ + hacr_write(base, HACR_DEFAULT); +} /* psa_write */ + +#ifdef SET_PSA_CRC +/*------------------------------------------------------------------*/ +/* + * Calculate the PSA CRC + * Thanks to Valster, Nico for the code + * NOTE: By specifying a length including the CRC position the + * returned value should be zero. (i.e. a correct checksum in the PSA) + * + * The Windows drivers don't use the CRC, but the AP and the PtP tool + * depend on it. + */ +static u_short +psa_crc(unsigned char * psa, /* The PSA */ + int size) /* Number of short for CRC */ +{ + int byte_cnt; /* Loop on the PSA */ + u_short crc_bytes = 0; /* Data in the PSA */ + int bit_cnt; /* Loop on the bits of the short */ + + for(byte_cnt = 0; byte_cnt < size; byte_cnt++ ) + { + crc_bytes ^= psa[byte_cnt]; /* Its an xor */ + + for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ ) + { + if(crc_bytes & 0x0001) + crc_bytes = (crc_bytes >> 1) ^ 0xA001; + else + crc_bytes >>= 1 ; + } + } + + return crc_bytes; +} /* psa_crc */ +#endif /* SET_PSA_CRC */ + +/*------------------------------------------------------------------*/ +/* + * update the checksum field in the Wavelan's PSA + */ +static void +update_psa_checksum(struct net_device * dev) +{ +#ifdef SET_PSA_CRC + psa_t psa; + u_short crc; + + /* read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + + /* update the checksum */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1]) + - sizeof(psa.psa_crc_status)); + + psa.psa_crc[0] = crc & 0xFF; + psa.psa_crc[1] = (crc & 0xFF00) >> 8; + + /* Write it ! */ + psa_write(dev, (char *)&psa.psa_crc - (char *)&psa, + (unsigned char *)&psa.psa_crc, 2); + +#ifdef DEBUG_IOCTL_INFO + printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", + dev->name, psa.psa_crc[0], psa.psa_crc[1]); + + /* Check again (luxury !) */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc_status)); + + if(crc != 0) + printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name); +#endif /* DEBUG_IOCTL_INFO */ +#endif /* SET_PSA_CRC */ +} /* update_psa_checksum */ + +/*------------------------------------------------------------------*/ +/* + * Write 1 byte to the MMC. + */ +static void +mmc_out(u_long base, + u_short o, + u_char d) +{ + int count = 0; + + /* Wait for MMC to go idle */ + while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) + udelay(10); + + outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base)); + outb(d, MMD(base)); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to write bytes to the Modem Management Controller. + * We start by the end because it is the way it should be ! + */ +static void +mmc_write(u_long base, + u_char o, + u_char * b, + int n) +{ + o += n; + b += n; + + while(n-- > 0 ) + mmc_out(base, --o, *(--b)); +} /* mmc_write */ + +/*------------------------------------------------------------------*/ +/* + * Read 1 byte from the MMC. + * Optimised version for 1 byte, avoid using memory... + */ +static u_char +mmc_in(u_long base, + u_short o) +{ + int count = 0; + + while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) + udelay(10); + outb(o << 1, MMR(base)); /* Set the read address */ + + outb(0, MMD(base)); /* Required dummy write */ + + while((count++ < 100) && (inb(HASR(base)) & HASR_MMI_BUSY)) + udelay(10); + return (u_char) (inb(MMD(base))); /* Now do the actual read */ +} + +/*------------------------------------------------------------------*/ +/* + * Routine to read bytes from the Modem Management Controller. + * The implementation is complicated by a lack of address lines, + * which prevents decoding of the low-order bit. + * (code has just been moved in the above function) + * We start by the end because it is the way it should be ! + */ +static void +mmc_read(u_long base, + u_char o, + u_char * b, + int n) +{ + o += n; + b += n; + + while(n-- > 0) + *(--b) = mmc_in(base, --o); +} /* mmc_read */ + +/*------------------------------------------------------------------*/ +/* + * Get the type of encryption available... + */ +static inline int +mmc_encr(u_long base) /* i/o port of the card */ +{ + int temp; + + temp = mmc_in(base, mmroff(0, mmr_des_avail)); + if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) + return 0; + else + return temp; +} + +/*------------------------------------------------------------------*/ +/* + * Wait for the frequency EEprom to complete a command... + */ +static void +fee_wait(u_long base, /* i/o port of the card */ + int delay, /* Base delay to wait for */ + int number) /* Number of time to wait */ +{ + int count = 0; /* Wait only a limited time */ + + while((count++ < number) && + (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY)) + udelay(delay); +} + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the Frequency EEprom (frequency select cards). + */ +static void +fee_read(u_long base, /* i/o port of the card */ + u_short o, /* destination offset */ + u_short * b, /* data buffer */ + int n) /* number of registers */ +{ + b += n; /* Position at the end of the area */ + + /* Write the address */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while(n-- > 0) + { + /* Write the read command */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ); + + /* Wait until EEprom is ready (should be quick !) */ + fee_wait(base, 10, 100); + + /* Read the value */ + *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) | + mmc_in(base, mmroff(0, mmr_fee_data_l))); + } +} + + +/*------------------------------------------------------------------*/ +/* + * Write bytes from the Frequency EEprom (frequency select cards). + * This is a bit complicated, because the frequency eeprom has to + * be unprotected and the write enabled. + * Jean II + */ +static void +fee_write(u_long base, /* i/o port of the card */ + u_short o, /* destination offset */ + u_short * b, /* data buffer */ + int n) /* number of registers */ +{ + b += n; /* Position at the end of the area */ + +#ifdef EEPROM_IS_PROTECTED /* disabled */ +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* Ask to read the protected register */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); + + fee_wait(base, 10, 100); + + /* Read the protected register */ + printk("Protected 2 : %02X-%02X\n", + mmc_in(base, mmroff(0, mmr_fee_data_h)), + mmc_in(base, mmroff(0, mmr_fee_data_l))); +#endif /* DOESNT_SEEM_TO_WORK */ + + /* Enable protected register */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); + + fee_wait(base, 10, 100); + + /* Unprotect area */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* Or use : */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); +#endif /* DOESNT_SEEM_TO_WORK */ + + fee_wait(base, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ + + /* Write enable */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); + + fee_wait(base, 10, 100); + + /* Write the EEprom address */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while(n-- > 0) + { + /* Write the value */ + mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); + mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF); + + /* Write the write command */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE); + + /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */ + mdelay(10); + fee_wait(base, 10, 100); + } + + /* Write disable */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); + + fee_wait(base, 10, 100); + +#ifdef EEPROM_IS_PROTECTED /* disabled */ + /* Reprotect EEprom */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); + + fee_wait(base, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ +} + +/******************* WaveLAN Roaming routines... ********************/ + +#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */ + +static unsigned char WAVELAN_BEACON_ADDRESS[] = {0x09,0x00,0x0e,0x20,0x03,0x00}; + +static void wv_roam_init(struct net_device *dev) +{ + net_local *lp= netdev_priv(dev); + + /* Do not remove this unless you have a good reason */ + printk(KERN_NOTICE "%s: Warning, you have enabled roaming on" + " device %s !\n", dev->name, dev->name); + printk(KERN_NOTICE "Roaming is currently an experimental unsupported feature" + " of the Wavelan driver.\n"); + printk(KERN_NOTICE "It may work, but may also make the driver behave in" + " erratic ways or crash.\n"); + + lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */ + lp->wavepoint_table.num_wavepoints=0; + lp->wavepoint_table.locked=0; + lp->curr_point=NULL; /* No default WavePoint */ + lp->cell_search=0; + + lp->cell_timer.data=(long)lp; /* Start cell expiry timer */ + lp->cell_timer.function=wl_cell_expiry; + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); + + wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */ + /* to build up a good WavePoint */ + /* table... */ + printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name); +} + +static void wv_roam_cleanup(struct net_device *dev) +{ + wavepoint_history *ptr,*old_ptr; + net_local *lp= netdev_priv(dev); + + printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name); + + /* Fixme : maybe we should check that the timer exist before deleting it */ + del_timer(&lp->cell_timer); /* Remove cell expiry timer */ + ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */ + while(ptr!=NULL) + { + old_ptr=ptr; + ptr=ptr->next; + wl_del_wavepoint(old_ptr,lp); + } +} + +/* Enable/Disable NWID promiscuous mode on a given device */ +static void wv_nwid_filter(unsigned char mode, net_local *lp) +{ + mm_t m; + unsigned long flags; + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&lp->spinlock, flags); + + m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00; + mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1); + + if(mode==NWID_PROMISC) + lp->cell_search=1; + else + lp->cell_search=0; + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&lp->spinlock, flags); +} + +/* Find a record in the WavePoint table matching a given NWID */ +static wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp) +{ + wavepoint_history *ptr=lp->wavepoint_table.head; + + while(ptr!=NULL){ + if(ptr->nwid==nwid) + return ptr; + ptr=ptr->next; + } + return NULL; +} + +/* Create a new wavepoint table entry */ +static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp) +{ + wavepoint_history *new_wavepoint; + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); +#endif + + if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS) + return NULL; + + new_wavepoint = kmalloc(sizeof(wavepoint_history),GFP_ATOMIC); + if(new_wavepoint==NULL) + return NULL; + + new_wavepoint->nwid=nwid; /* New WavePoints NWID */ + new_wavepoint->average_fast=0; /* Running Averages..*/ + new_wavepoint->average_slow=0; + new_wavepoint->qualptr=0; /* Start of ringbuffer */ + new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */ + memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */ + + new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */ + new_wavepoint->prev=NULL; + + if(lp->wavepoint_table.head!=NULL) + lp->wavepoint_table.head->prev=new_wavepoint; + + lp->wavepoint_table.head=new_wavepoint; + + lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */ + + return new_wavepoint; +} + +/* Remove a wavepoint entry from WavePoint table */ +static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp) +{ + if(wavepoint==NULL) + return; + + if(lp->curr_point==wavepoint) + lp->curr_point=NULL; + + if(wavepoint->prev!=NULL) + wavepoint->prev->next=wavepoint->next; + + if(wavepoint->next!=NULL) + wavepoint->next->prev=wavepoint->prev; + + if(lp->wavepoint_table.head==wavepoint) + lp->wavepoint_table.head=wavepoint->next; + + lp->wavepoint_table.num_wavepoints--; + kfree(wavepoint); +} + +/* Timer callback function - checks WavePoint table for stale entries */ +static void wl_cell_expiry(unsigned long data) +{ + net_local *lp=(net_local *)data; + wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point; + +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name); +#endif + + if(lp->wavepoint_table.locked) + { +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n"); +#endif + + lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */ + add_timer(&lp->cell_timer); + return; + } + + while(wavepoint!=NULL) + { + if(time_after(jiffies, wavepoint->last_seen + CELL_TIMEOUT)) + { +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid); +#endif + + old_point=wavepoint; + wavepoint=wavepoint->next; + wl_del_wavepoint(old_point,lp); + } + else + wavepoint=wavepoint->next; + } + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); +} + +/* Update SNR history of a wavepoint */ +static void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq) +{ + int i=0,num_missed=0,ptr=0; + int average_fast=0,average_slow=0; + + num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed + any beacons? */ + if(num_missed) + for(i=0;isigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */ + wavepoint->qualptr %=WAVEPOINT_HISTORY; /* in the ringbuffer. */ + } + wavepoint->last_seen=jiffies; /* Add beacon to history */ + wavepoint->last_seq=seq; + wavepoint->sigqual[wavepoint->qualptr++]=sigqual; + wavepoint->qualptr %=WAVEPOINT_HISTORY; + ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY; + + for(i=0;isigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + average_slow=average_fast; + for(i=WAVEPOINT_FAST_HISTORY;isigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY; + wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY; +} + +/* Perform a handover to a new WavePoint */ +static void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp) +{ + unsigned int base = lp->dev->base_addr; + mm_t m; + unsigned long flags; + + if(wavepoint==lp->curr_point) /* Sanity check... */ + { + wv_nwid_filter(!NWID_PROMISC,lp); + return; + } + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&lp->spinlock, flags); + + m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF; + m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8; + + mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + wv_nwid_filter(!NWID_PROMISC,lp); + lp->curr_point=wavepoint; +} + +/* Called when a WavePoint beacon is received */ +static void wl_roam_gather(struct net_device * dev, + u_char * hdr, /* Beacon header */ + u_char * stats) /* SNR, Signal quality + of packet */ +{ + wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */ + unsigned short nwid=ntohs(beacon->nwid); + unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */ + wavepoint_history *wavepoint=NULL; /* WavePoint table entry */ + net_local *lp = netdev_priv(dev); /* Device info */ + +#ifdef I_NEED_THIS_FEATURE + /* Some people don't need this, some other may need it */ + nwid=nwid^ntohs(beacon->domain_id); +#endif + +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name); + printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual); +#endif + + lp->wavepoint_table.locked=1; /* */ + + wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */ + if(wavepoint==NULL) /* If no entry, Create a new one... */ + { + wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp); + if(wavepoint==NULL) + goto out; + } + if(lp->curr_point==NULL) /* If this is the only WavePoint, */ + wv_roam_handover(wavepoint, lp); /* Jump on it! */ + + wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history + stats. */ + + if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */ + if(!lp->cell_search) /* WavePoint is getting faint, */ + wv_nwid_filter(NWID_PROMISC,lp); /* start looking for a new one */ + + if(wavepoint->average_slow > + lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA) + wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */ + + if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */ + if(lp->cell_search) /* getting better, drop out of cell search mode */ + wv_nwid_filter(!NWID_PROMISC,lp); + +out: + lp->wavepoint_table.locked=0; /* :-) */ +} + +/* Test this MAC frame a WavePoint beacon */ +static inline int WAVELAN_BEACON(unsigned char *data) +{ + wavepoint_beacon *beacon= (wavepoint_beacon *)data; + static const wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00}; + + if(memcmp(beacon,&beacon_template,9)==0) + return 1; + else + return 0; +} +#endif /* WAVELAN_ROAMING */ + +/************************ I82593 SUBROUTINES *************************/ +/* + * Useful subroutines to manage the Ethernet controller + */ + +/*------------------------------------------------------------------*/ +/* + * Routine to synchronously send a command to the i82593 chip. + * Should be called with interrupts disabled. + * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(), + * wv_82593_config() & wv_diag()) + */ +static int +wv_82593_cmd(struct net_device * dev, + char * str, + int cmd, + int result) +{ + unsigned int base = dev->base_addr; + int status; + int wait_completed; + long spin; + + /* Spin until the chip finishes executing its current command (if any) */ + spin = 1000; + do + { + /* Time calibration of the loop */ + udelay(10); + + /* Read the interrupt register */ + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); + + /* If the interrupt hasn't been posted */ + if (spin < 0) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n", + str, status); +#endif + return(FALSE); + } + + /* Issue the command to the controller */ + outb(cmd, LCCR(base)); + + /* If we don't have to check the result of the command + * Note : this mean that the irq handler will deal with that */ + if(result == SR0_NO_RESULT) + return(TRUE); + + /* We are waiting for command completion */ + wait_completed = TRUE; + + /* Busy wait while the LAN controller executes the command. */ + spin = 1000; + do + { + /* Time calibration of the loop */ + udelay(10); + + /* Read the interrupt register */ + outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); + status = inb(LCSR(base)); + + /* Check if there was an interrupt posted */ + if((status & SR0_INTERRUPT)) + { + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + + /* Check if interrupt is a command completion */ + if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) && + ((status & SR0_BOTH_RX_TX) != 0x0) && + !(status & SR0_RECEPTION)) + { + /* Signal command completion */ + wait_completed = FALSE; + } + else + { + /* Note : Rx interrupts will be handled later, because we can + * handle multiple Rx packets at once */ +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_INFO "wv_82593_cmd: not our interrupt\n"); +#endif + } + } + } + while(wait_completed && (spin-- > 0)); + + /* If the interrupt hasn't be posted */ + if(wait_completed) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n", + str, status); +#endif + return(FALSE); + } + + /* Check the return code returned by the card (see above) against + * the expected return code provided by the caller */ + if((status & SR0_EVENT_MASK) != result) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n", + str, status); +#endif + return(FALSE); + } + + return(TRUE); +} /* wv_82593_cmd */ + +/*------------------------------------------------------------------*/ +/* + * This routine does a 593 op-code number 7, and obtains the diagnose + * status for the WaveLAN. + */ +static inline int +wv_diag(struct net_device * dev) +{ + return(wv_82593_cmd(dev, "wv_diag(): diagnose", + OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)); +} /* wv_diag */ + +/*------------------------------------------------------------------*/ +/* + * Routine to read len bytes from the i82593's ring buffer, starting at + * chip address addr. The results read from the chip are stored in buf. + * The return value is the address to use for next the call. + */ +static int +read_ringbuf(struct net_device * dev, + int addr, + char * buf, + int len) +{ + unsigned int base = dev->base_addr; + int ring_ptr = addr; + int chunk_len; + char * buf_ptr = buf; + + /* Get all the buffer */ + while(len > 0) + { + /* Position the Program I/O Register at the ring buffer pointer */ + outb(ring_ptr & 0xff, PIORL(base)); + outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base)); + + /* First, determine how much we can read without wrapping around the + ring buffer */ + if((addr + len) < (RX_BASE + RX_SIZE)) + chunk_len = len; + else + chunk_len = RX_BASE + RX_SIZE - addr; + insb(PIOP(base), buf_ptr, chunk_len); + buf_ptr += chunk_len; + len -= chunk_len; + ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE; + } + return(ring_ptr); +} /* read_ringbuf */ + +/*------------------------------------------------------------------*/ +/* + * Reconfigure the i82593, or at least ask for it... + * Because wv_82593_config use the transmission buffer, we must do it + * when we are sure that there is no transmission, so we do it now + * or in wavelan_packet_xmit() (I can't find any better place, + * wavelan_interrupt is not an option...), so you may experience + * some delay sometime... + */ +static void +wv_82593_reconfig(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + struct pcmcia_device * link = lp->link; + unsigned long flags; + + /* Arm the flag, will be cleard in wv_82593_config() */ + lp->reconfig_82593 = TRUE; + + /* Check if we can do it now ! */ + if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev))) + { + spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ + wv_82593_config(dev); + spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ + } + else + { +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG + "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n", + dev->name, dev->state, link->open); +#endif + } +} + +/********************* DEBUG & INFO SUBROUTINES *********************/ +/* + * This routines are used in the code to show debug informations. + * Most of the time, it dump the content of hardware structures... + */ + +#ifdef DEBUG_PSA_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted contents of the Parameter Storage Area. + */ +static void +wv_psa_show(psa_t * p) +{ + printk(KERN_DEBUG "##### wavelan psa contents: #####\n"); + printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", + p->psa_io_base_addr_1, + p->psa_io_base_addr_2, + p->psa_io_base_addr_3, + p->psa_io_base_addr_4); + printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", + p->psa_rem_boot_addr_1, + p->psa_rem_boot_addr_2, + p->psa_rem_boot_addr_3); + printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); + printk("psa_int_req_no: %d\n", p->psa_int_req_no); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr); + printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr); + printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel); + printk("psa_comp_number: %d, ", p->psa_comp_number); + printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); + printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", + p->psa_feature_select); + printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); + printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); + printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); + printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]); + printk("psa_nwid_select: %d\n", p->psa_nwid_select); + printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select); + printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + p->psa_encryption_key[0], + p->psa_encryption_key[1], + p->psa_encryption_key[2], + p->psa_encryption_key[3], + p->psa_encryption_key[4], + p->psa_encryption_key[5], + p->psa_encryption_key[6], + p->psa_encryption_key[7]); + printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); + printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", + p->psa_call_code[0]); + printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + p->psa_call_code[0], + p->psa_call_code[1], + p->psa_call_code[2], + p->psa_call_code[3], + p->psa_call_code[4], + p->psa_call_code[5], + p->psa_call_code[6], + p->psa_call_code[7]); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_reserved[]: %02X:%02X\n", + p->psa_reserved[0], + p->psa_reserved[1]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); + printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); + printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); +} /* wv_psa_show */ +#endif /* DEBUG_PSA_SHOW */ + +#ifdef DEBUG_MMC_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the Modem Management Controller. + * This function need to be completed... + */ +static void +wv_mmc_show(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + mmr_t m; + + /* Basic check */ + if(hasr_read(base) & HASR_NO_CLK) + { + printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n", + dev->name); + return; + } + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the mmc */ + mmc_out(base, mmwoff(0, mmw_freeze), 1); + mmc_read(base, 0, (u_char *)&m, sizeof(m)); + mmc_out(base, mmwoff(0, mmw_freeze), 0); + + /* Don't forget to update statistics */ + lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; + + spin_unlock_irqrestore(&lp->spinlock, flags); + + printk(KERN_DEBUG "##### wavelan modem status registers: #####\n"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused0[0], + m.mmr_unused0[1], + m.mmr_unused0[2], + m.mmr_unused0[3], + m.mmr_unused0[4], + m.mmr_unused0[5], + m.mmr_unused0[6], + m.mmr_unused0[7]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", + m.mmr_des_avail, m.mmr_des_status); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused1[0], + m.mmr_unused1[1], + m.mmr_unused1[2], + m.mmr_unused1[3], + m.mmr_unused1[4]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", + m.mmr_dce_status, + (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"", + (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? + "loop test indicated," : "", + (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "", + (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? + "jabber timer expired," : ""); + printk(KERN_DEBUG "Dsp ID: %02X\n", + m.mmr_dsp_id); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", + m.mmr_unused2[0], + m.mmr_unused2[1]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", + (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, + (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); + printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", + m.mmr_thr_pre_set & MMR_THR_PRE_SET, + (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below"); + printk(KERN_DEBUG "signal_lvl: %d [%s], ", + m.mmr_signal_lvl & MMR_SIGNAL_LVL, + (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg"); + printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL, + (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update"); + printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL, + (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); +#endif /* DEBUG_SHOW_UNUSED */ +} /* wv_mmc_show */ +#endif /* DEBUG_MMC_SHOW */ + +#ifdef DEBUG_I82593_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the i82593's receive unit. + */ +static void +wv_ru_show(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + + printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n"); + printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop); + /* + * Not implemented yet... + */ + printk("\n"); +} /* wv_ru_show */ +#endif /* DEBUG_I82593_SHOW */ + +#ifdef DEBUG_DEVICE_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver. + */ +static void +wv_dev_show(struct net_device * dev) +{ + printk(KERN_DEBUG "dev:"); + printk(" state=%lX,", dev->state); + printk(" trans_start=%ld,", dev->trans_start); + printk(" flags=0x%x,", dev->flags); + printk("\n"); +} /* wv_dev_show */ + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver's + * private information. + */ +static void +wv_local_show(struct net_device * dev) +{ + net_local *lp = netdev_priv(dev); + + printk(KERN_DEBUG "local:"); + /* + * Not implemented yet... + */ + printk("\n"); +} /* wv_local_show */ +#endif /* DEBUG_DEVICE_SHOW */ + +#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) +/*------------------------------------------------------------------*/ +/* + * Dump packet header (and content if necessary) on the screen + */ +static void +wv_packet_info(u_char * p, /* Packet to dump */ + int length, /* Length of the packet */ + char * msg1, /* Name of the device */ + char * msg2) /* Name of the function */ +{ + int i; + int maxi; + + printk(KERN_DEBUG "%s: %s(): dest %pM, length %d\n", + msg1, msg2, p, length); + printk(KERN_DEBUG "%s: %s(): src %pM, type 0x%02X%02X\n", + msg1, msg2, &p[6], p[12], p[13]); + +#ifdef DEBUG_PACKET_DUMP + + printk(KERN_DEBUG "data=\""); + + if((maxi = length) > DEBUG_PACKET_DUMP) + maxi = DEBUG_PACKET_DUMP; + for(i = 14; i < maxi; i++) + if(p[i] >= ' ' && p[i] <= '~') + printk(" %c", p[i]); + else + printk("%02X", p[i]); + if(maxi < length) + printk(".."); + printk("\"\n"); + printk(KERN_DEBUG "\n"); +#endif /* DEBUG_PACKET_DUMP */ +} +#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ + +/*------------------------------------------------------------------*/ +/* + * This is the information which is displayed by the driver at startup + * There is a lot of flag to configure it at your will... + */ +static void +wv_init_info(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + psa_t psa; + + /* Read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef DEBUG_PSA_SHOW + wv_psa_show(&psa); +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82593_SHOW + wv_ru_show(dev); +#endif + +#ifdef DEBUG_BASIC_SHOW + /* Now, let's go for the basic stuff */ + printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr %pM", + dev->name, base, dev->irq, dev->dev_addr); + + /* Print current network id */ + if(psa.psa_nwid_select) + printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]); + else + printk(", nwid off"); + + /* If 2.00 card */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + unsigned short freq; + + /* Ask the EEprom to read the frequency from the first area */ + fee_read(base, 0x00 /* 1st area - frequency... */, + &freq, 1); + + /* Print frequency */ + printk(", 2.00, %ld", (freq >> 6) + 2400L); + + /* Hack !!! */ + if(freq & 0x20) + printk(".5"); + } + else + { + printk(", PCMCIA, "); + switch (psa.psa_subband) + { + case PSA_SUBBAND_915: + printk("915"); + break; + case PSA_SUBBAND_2425: + printk("2425"); + break; + case PSA_SUBBAND_2460: + printk("2460"); + break; + case PSA_SUBBAND_2484: + printk("2484"); + break; + case PSA_SUBBAND_2430_5: + printk("2430.5"); + break; + default: + printk("unknown"); + } + } + + printk(" MHz\n"); +#endif /* DEBUG_BASIC_SHOW */ + +#ifdef DEBUG_VERSION_SHOW + /* Print version information */ + printk(KERN_NOTICE "%s", version); +#endif +} /* wv_init_info */ + +/********************* IOCTL, STATS & RECONFIG *********************/ +/* + * We found here routines that are called by Linux on differents + * occasions after the configuration and not for transmitting data + * These may be called when the user use ifconfig, /proc/net/dev + * or wireless extensions + */ + + +/*------------------------------------------------------------------*/ +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ + +static void +wavelan_set_multicast_list(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name); +#endif + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", + dev->name, dev->flags, dev->mc_count); +#endif + + if(dev->flags & IFF_PROMISC) + { + /* + * Enable promiscuous mode: receive all packets. + */ + if(!lp->promiscuous) + { + lp->promiscuous = 1; + lp->allmulticast = 0; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + } + } + else + /* If all multicast addresses + * or too much multicast addresses for the hardware filter */ + if((dev->flags & IFF_ALLMULTI) || + (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES)) + { + /* + * Disable promiscuous mode, but active the all multicast mode + */ + if(!lp->allmulticast) + { + lp->promiscuous = 0; + lp->allmulticast = 1; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + } + } + else + /* If there is some multicast addresses to send */ + if(dev->mc_list != (struct dev_mc_list *) NULL) + { + /* + * Disable promiscuous mode, but receive all packets + * in multicast list + */ +#ifdef MULTICAST_AVOID + if(lp->promiscuous || lp->allmulticast || + (dev->mc_count != lp->mc_count)) +#endif + { + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = dev->mc_count; + + wv_82593_reconfig(dev); + } + } + else + { + /* + * Switch to normal mode: disable promiscuous mode and + * clear the multicast list. + */ + if(lp->promiscuous || lp->mc_count == 0) + { + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + } + } +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This function doesn't exist... + * (Note : it was a nice way to test the reconfigure stuff...) + */ +#ifdef SET_MAC_ADDRESS +static int +wavelan_set_mac_address(struct net_device * dev, + void * addr) +{ + struct sockaddr * mac = addr; + + /* Copy the address */ + memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); + + /* Reconfig the beast */ + wv_82593_reconfig(dev); + + return 0; +} +#endif /* SET_MAC_ADDRESS */ + + +/*------------------------------------------------------------------*/ +/* + * Frequency setting (for hardware able of it) + * It's a bit complicated and you don't really want to look into it... + */ +static int +wv_set_frequency(u_long base, /* i/o port of the card */ + iw_freq * frequency) +{ + const int BAND_NUM = 10; /* Number of bands */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz */ +#ifdef DEBUG_IOCTL_INFO + int i; +#endif + + /* Setting by frequency */ + /* Theoritically, you may set any frequency between + * the two limits with a 0.5 MHz precision. In practice, + * I don't want you to have trouble with local + * regulations... */ + if((frequency->e == 1) && + (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8)) + { + freq = ((frequency->m / 10000) - 24000L) / 5; + } + + /* Setting by channel (same as wfreqsel) */ + /* Warning : each channel is 22MHz wide, so some of the channels + * will interfere... */ + if((frequency->e == 0) && + (frequency->m >= 0) && (frequency->m < BAND_NUM)) + { + /* Get frequency offset. */ + freq = channel_bands[frequency->m] >> 1; + } + + /* Verify if the frequency is allowed */ + if(freq != 0L) + { + u_short table[10]; /* Authorized frequency table */ + + /* Read the frequency table */ + fee_read(base, 0x71 /* frequency table */, + table, 10); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Frequency table :"); + for(i = 0; i < 10; i++) + { + printk(" %04X", + table[i]); + } + printk("\n"); +#endif + + /* Look in the table if the frequency is allowed */ + if(!(table[9 - ((freq - 24) / 16)] & + (1 << ((freq - 24) % 16)))) + return -EINVAL; /* not allowed */ + } + else + return -EINVAL; + + /* If we get a usable frequency */ + if(freq != 0L) + { + unsigned short area[16]; + unsigned short dac[2]; + unsigned short area_verify[16]; + unsigned short dac_verify[2]; + /* Corresponding gain (in the power adjust value table) + * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8 + * & WCIN062D.DOC, page 6.2.9 */ + unsigned short power_limit[] = { 40, 80, 120, 160, 0 }; + int power_band = 0; /* Selected band */ + unsigned short power_adjust; /* Correct value */ + + /* Search for the gain */ + power_band = 0; + while((freq > power_limit[power_band]) && + (power_limit[++power_band] != 0)) + ; + + /* Read the first area */ + fee_read(base, 0x00, + area, 16); + + /* Read the DAC */ + fee_read(base, 0x60, + dac, 2); + + /* Read the new power adjust value */ + fee_read(base, 0x6B - (power_band >> 1), + &power_adjust, 1); + if(power_band & 0x1) + power_adjust >>= 8; + else + power_adjust &= 0xFF; + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); + for(i = 0; i < 16; i++) + { + printk(" %04X", + area[i]); + } + printk("\n"); + + printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", + dac[0], dac[1]); +#endif + + /* Frequency offset (for info only...) */ + area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); + + /* Receiver Principle main divider coefficient */ + area[3] = (freq >> 1) + 2400L - 352L; + area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Transmitter Main divider coefficient */ + area[13] = (freq >> 1) + 2400L; + area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Others part of the area are flags, bit streams or unused... */ + + /* Set the value in the DAC */ + dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); + dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); + + /* Write the first area */ + fee_write(base, 0x00, + area, 16); + + /* Write the DAC */ + fee_write(base, 0x60, + dac, 2); + + /* We now should verify here that the EEprom writing was ok */ + + /* ReRead the first area */ + fee_read(base, 0x00, + area_verify, 16); + + /* ReRead the DAC */ + fee_read(base, 0x60, + dac_verify, 2); + + /* Compare */ + if(memcmp(area, area_verify, 16 * 2) || + memcmp(dac, dac_verify, 2 * 2)) + { +#ifdef DEBUG_IOCTL_ERROR + printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (?)\n"); +#endif + return -EOPNOTSUPP; + } + + /* We must download the frequency parameters to the + * synthetisers (from the EEprom - area 1) + * Note : as the EEprom is auto decremented, we set the end + * if the area... */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + + /* We must now download the power adjust value (gain) to + * the synthetisers (from the EEprom - area 7 - DAC) */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + +#ifdef DEBUG_IOCTL_INFO + /* Verification of what we have done... */ + + printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); + for(i = 0; i < 16; i++) + { + printk(" %04X", + area_verify[i]); + } + printk("\n"); + + printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", + dac_verify[0], dac_verify[1]); +#endif + + return 0; + } + else + return -EINVAL; /* Bah, never get there... */ +} + +/*------------------------------------------------------------------*/ +/* + * Give the list of available frequencies + */ +static int +wv_frequency_list(u_long base, /* i/o port of the card */ + iw_freq * list, /* List of frequency to fill */ + int max) /* Maximum number of frequencies */ +{ + u_short table[10]; /* Authorized frequency table */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ + int i; /* index in the table */ + const int BAND_NUM = 10; /* Number of bands */ + int c = 0; /* Channel number */ + + /* Read the frequency table */ + fee_read(base, 0x71 /* frequency table */, + table, 10); + + /* Look all frequencies */ + i = 0; + for(freq = 0; freq < 150; freq++) + /* Look in the table if the frequency is allowed */ + if(table[9 - (freq / 16)] & (1 << (freq % 16))) + { + /* Compute approximate channel number */ + while((((channel_bands[c] >> 1) - 24) < freq) && + (c < BAND_NUM)) + c++; + list[i].i = c; /* Set the list index */ + + /* put in the list */ + list[i].m = (((freq + 24) * 5) + 24000L) * 10000; + list[i++].e = 1; + + /* Check number */ + if(i >= max) + return(i); + } + + return(i); +} + +#ifdef IW_WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Gather wireless spy statistics : for each packet, compare the source + * address with out list, and if match, get the stats... + * Sorry, but this function really need wireless extensions... + */ +static inline void +wl_spy_gather(struct net_device * dev, + u_char * mac, /* MAC address */ + u_char * stats) /* Statistics to gather */ +{ + struct iw_quality wstats; + + wstats.qual = stats[2] & MMR_SGNL_QUAL; + wstats.level = stats[0] & MMR_SIGNAL_LVL; + wstats.noise = stats[1] & MMR_SILENCE_LVL; + wstats.updated = 0x7; + + /* Update spy records */ + wireless_spy_update(dev, mac, &wstats); +} +#endif /* IW_WIRELESS_SPY */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * This function calculate an histogram on the signal level. + * As the noise is quite constant, it's like doing it on the SNR. + * We have defined a set of interval (lp->his_range), and each time + * the level goes in that interval, we increment the count (lp->his_sum). + * With this histogram you may detect if one wavelan is really weak, + * or you may also calculate the mean and standard deviation of the level... + */ +static inline void +wl_his_gather(struct net_device * dev, + u_char * stats) /* Statistics to gather */ +{ + net_local * lp = netdev_priv(dev); + u_char level = stats[0] & MMR_SIGNAL_LVL; + int i; + + /* Find the correct interval */ + i = 0; + while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++])) + ; + + /* Increment interval counter */ + (lp->his_sum[i])++; +} +#endif /* HISTOGRAM */ + +static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strncpy(info->driver, "wavelan_cs", sizeof(info->driver)-1); +} + +static const struct ethtool_ops ops = { + .get_drvinfo = wl_get_drvinfo +}; + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get protocol name + */ +static int wavelan_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + strcpy(wrqu->name, "WaveLAN"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set NWID + */ +static int wavelan_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + mm_t m; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set NWID in WaveLAN. */ + if (!wrqu->nwid.disabled) { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrqu->nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrqu->nwid.value & 0xFF; + psa.psa_nwid_select = 0x01; + psa_write(dev, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + + /* Set NWID in mmc. */ + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; + mmc_write(base, + (char *) &m.w.mmw_netw_id_l - + (char *) &m, + (unsigned char *) &m.w.mmw_netw_id_l, 2); + mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00); + } else { + /* Disable NWID in the psa. */ + psa.psa_nwid_select = 0x00; + psa_write(dev, + (char *) &psa.psa_nwid_select - + (char *) &psa, + (unsigned char *) &psa.psa_nwid_select, + 1); + + /* Disable NWID in the mmc (no filtering). */ + mmc_out(base, mmwoff(0, mmw_loopt_sel), + MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get NWID + */ +static int wavelan_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the NWID. */ + psa_read(dev, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + wrqu->nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrqu->nwid.disabled = !(psa.psa_nwid_select); + wrqu->nwid.fixed = 1; /* Superfluous */ + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int wavelan_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + unsigned long flags; + int ret; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(base, &(wrqu->freq)); + else + ret = -EOPNOTSUPP; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int wavelan_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). + * Does it work for everybody, especially old cards? */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + unsigned short freq; + + /* Ask the EEPROM to read the frequency from the first area. */ + fee_read(base, 0x00, &freq, 1); + wrqu->freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrqu->freq.e = 1; + } else { + psa_read(dev, + (char *) &psa.psa_subband - (char *) &psa, + (unsigned char *) &psa.psa_subband, 1); + + if (psa.psa_subband <= 4) { + wrqu->freq.m = fixed_bands[psa.psa_subband]; + wrqu->freq.e = (psa.psa_subband != 0); + } else + ret = -EOPNOTSUPP; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set level threshold + */ +static int wavelan_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Set the level threshold. */ + /* We should complain loudly if wrqu->sens.fixed = 0, because we + * can't set auto mode... */ + psa.psa_thr_pre_set = wrqu->sens.value & 0x3F; + psa_write(dev, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_thr_pre_set), + psa.psa_thr_pre_set); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get level threshold + */ +static int wavelan_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Read the level threshold. */ + psa_read(dev, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + wrqu->sens.value = psa.psa_thr_pre_set & 0x3F; + wrqu->sens.fixed = 1; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set encryption key + */ +static int wavelan_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + unsigned long flags; + psa_t psa; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if capable of encryption */ + if (!mmc_encr(base)) { + ret = -EOPNOTSUPP; + } + + /* Check the size of the key */ + if((wrqu->encoding.length != 8) && (wrqu->encoding.length != 0)) { + ret = -EINVAL; + } + + if(!ret) { + /* Basic checking... */ + if (wrqu->encoding.length == 8) { + /* Copy the key in the driver */ + memcpy(psa.psa_encryption_key, extra, + wrqu->encoding.length); + psa.psa_encryption_select = 1; + + psa_write(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 8 + 1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(base, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa. + psa_encryption_key, 8); + } + + /* disable encryption */ + if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { + psa.psa_encryption_select = 0; + psa_write(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get encryption key + */ +static int wavelan_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if encryption is available */ + if (!mmc_encr(base)) { + ret = -EOPNOTSUPP; + } else { + /* Read the encryption key */ + psa_read(dev, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1 + 8); + + /* encryption is enabled ? */ + if (psa.psa_encryption_select) + wrqu->encoding.flags = IW_ENCODE_ENABLED; + else + wrqu->encoding.flags = IW_ENCODE_DISABLED; + wrqu->encoding.flags |= mmc_encr(base); + + /* Copy the key to the user buffer */ + wrqu->encoding.length = 8; + memcpy(extra, psa.psa_encryption_key, wrqu->encoding.length); + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +#ifdef WAVELAN_ROAMING_EXT +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set ESSID (domain) + */ +static int wavelan_set_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check if disable */ + if(wrqu->data.flags == 0) + lp->filter_domains = 0; + else { + char essid[IW_ESSID_MAX_SIZE + 1]; + char * endp; + + /* Terminate the string */ + memcpy(essid, extra, wrqu->data.length); + essid[IW_ESSID_MAX_SIZE] = '\0'; + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "SetEssid : ``%s''\n", essid); +#endif /* DEBUG_IOCTL_INFO */ + + /* Convert to a number (note : Wavelan specific) */ + lp->domain_id = simple_strtoul(essid, &endp, 16); + /* Has it worked ? */ + if(endp > essid) + lp->filter_domains = 1; + else { + lp->filter_domains = 0; + ret = -EINVAL; + } + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get ESSID (domain) + */ +static int wavelan_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + + /* Is the domain ID active ? */ + wrqu->data.flags = lp->filter_domains; + + /* Copy Domain ID into a string (Wavelan specific) */ + /* Sound crazy, be we can't have a snprintf in the kernel !!! */ + sprintf(extra, "%lX", lp->domain_id); + extra[IW_ESSID_MAX_SIZE] = '\0'; + + /* Set the length */ + wrqu->data.length = strlen(extra); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set AP address + */ +static int wavelan_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Set AP to : %pM\n", wrqu->ap_addr.sa_data); +#endif /* DEBUG_IOCTL_INFO */ + + return -EOPNOTSUPP; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP address + */ +static int wavelan_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + /* Should get the real McCoy instead of own Ethernet address */ + memcpy(wrqu->ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + + return -EOPNOTSUPP; +} +#endif /* WAVELAN_ROAMING_EXT */ + +#ifdef WAVELAN_ROAMING +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set mode + */ +static int wavelan_set_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + int ret = 0; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Check mode */ + switch(wrqu->mode) { + case IW_MODE_ADHOC: + if(do_roaming) { + wv_roam_cleanup(dev); + do_roaming = 0; + } + break; + case IW_MODE_INFRA: + if(!do_roaming) { + wv_roam_init(dev); + do_roaming = 1; + } + break; + default: + ret = -EINVAL; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get mode + */ +static int wavelan_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + if(do_roaming) + wrqu->mode = IW_MODE_INFRA; + else + wrqu->mode = IW_MODE_ADHOC; + + return 0; +} +#endif /* WAVELAN_ROAMING */ + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int wavelan_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + struct iw_range *range = (struct iw_range *) extra; + unsigned long flags; + int ret = 0; + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; + + /* Set information in the range struct. */ + range->throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0xFFFF; + + range->sensitivity = 0x3F; + range->max_qual.qual = MMR_SGNL_QUAL; + range->max_qual.level = MMR_SIGNAL_LVL; + range->max_qual.noise = MMR_SILENCE_LVL; + range->avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range->avg_qual.level = 30; + range->avg_qual.noise = 8; + + range->num_bitrates = 1; + range->bitrate[0] = 2000000; /* 2 Mb/s */ + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | + IW_EVENT_CAPA_MASK(0x8B04) | + IW_EVENT_CAPA_MASK(0x8B06)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + range->num_channels = 10; + range->num_frequency = wv_frequency_list(base, range->freq, + IW_MAX_FREQUENCIES); + } else + range->num_channels = range->num_frequency = 0; + + /* Encryption supported ? */ + if (mmc_encr(base)) { + range->encoding_size[0] = 8; /* DES = 64 bits key */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set quality threshold + */ +static int wavelan_set_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned int base = dev->base_addr; + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa.psa_quality_thr = *(extra) & 0x0F; + psa_write(dev, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_quality_thr), + psa.psa_quality_thr); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_qthr(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + psa_t psa; + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + psa_read(dev, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + *(extra) = psa.psa_quality_thr & 0x0F; + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +#ifdef WAVELAN_ROAMING +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set roaming + */ +static int wavelan_set_roam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + unsigned long flags; + + /* Disable interrupts and save flags. */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Note : should check if user == root */ + if(do_roaming && (*extra)==0) + wv_roam_cleanup(dev); + else if(do_roaming==0 && (*extra)!=0) + wv_roam_init(dev); + + do_roaming = (*extra); + + /* Enable interrupts and restore flags. */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get quality threshold + */ +static int wavelan_get_roam(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + *(extra) = do_roaming; + + return 0; +} +#endif /* WAVELAN_ROAMING */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set histogram + */ +static int wavelan_set_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + + /* Check the number of intervals. */ + if (wrqu->data.length > 16) { + return(-E2BIG); + } + + /* Disable histo while we copy the addresses. + * As we don't disable interrupts, we need to do this */ + lp->his_number = 0; + + /* Are there ranges to copy? */ + if (wrqu->data.length > 0) { + /* Copy interval ranges to the driver */ + memcpy(lp->his_range, extra, wrqu->data.length); + + { + int i; + printk(KERN_DEBUG "Histo :"); + for(i = 0; i < wrqu->data.length; i++) + printk(" %d", lp->his_range[i]); + printk("\n"); + } + + /* Reset result structure. */ + memset(lp->his_sum, 0x00, sizeof(long) * 16); + } + + /* Now we can set the number of ranges */ + lp->his_number = wrqu->data.length; + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get histogram + */ +static int wavelan_get_histo(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + net_local *lp = netdev_priv(dev); + + /* Set the number of intervals. */ + wrqu->data.length = lp->his_number; + + /* Give back the distribution statistics */ + if(lp->his_number > 0) + memcpy(extra, lp->his_sum, sizeof(long) * lp->his_number); + + return(0); +} +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const struct iw_priv_args wavelan_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, + { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setroam" }, + { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" }, + { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, +}; + +static const iw_handler wavelan_handler[] = +{ + NULL, /* SIOCSIWNAME */ + wavelan_get_name, /* SIOCGIWNAME */ + wavelan_set_nwid, /* SIOCSIWNWID */ + wavelan_get_nwid, /* SIOCGIWNWID */ + wavelan_set_freq, /* SIOCSIWFREQ */ + wavelan_get_freq, /* SIOCGIWFREQ */ +#ifdef WAVELAN_ROAMING + wavelan_set_mode, /* SIOCSIWMODE */ + wavelan_get_mode, /* SIOCGIWMODE */ +#else /* WAVELAN_ROAMING */ + NULL, /* SIOCSIWMODE */ + NULL, /* SIOCGIWMODE */ +#endif /* WAVELAN_ROAMING */ + wavelan_set_sens, /* SIOCSIWSENS */ + wavelan_get_sens, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + wavelan_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#ifdef WAVELAN_ROAMING_EXT + wavelan_set_wap, /* SIOCSIWAP */ + wavelan_get_wap, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + wavelan_set_essid, /* SIOCSIWESSID */ + wavelan_get_essid, /* SIOCGIWESSID */ +#else /* WAVELAN_ROAMING_EXT */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ +#endif /* WAVELAN_ROAMING_EXT */ + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + wavelan_set_encode, /* SIOCSIWENCODE */ + wavelan_get_encode, /* SIOCGIWENCODE */ +}; + +static const iw_handler wavelan_private_handler[] = +{ + wavelan_set_qthr, /* SIOCIWFIRSTPRIV */ + wavelan_get_qthr, /* SIOCIWFIRSTPRIV + 1 */ +#ifdef WAVELAN_ROAMING + wavelan_set_roam, /* SIOCIWFIRSTPRIV + 2 */ + wavelan_get_roam, /* SIOCIWFIRSTPRIV + 3 */ +#else /* WAVELAN_ROAMING */ + NULL, /* SIOCIWFIRSTPRIV + 2 */ + NULL, /* SIOCIWFIRSTPRIV + 3 */ +#endif /* WAVELAN_ROAMING */ +#ifdef HISTOGRAM + wavelan_set_histo, /* SIOCIWFIRSTPRIV + 4 */ + wavelan_get_histo, /* SIOCIWFIRSTPRIV + 5 */ +#endif /* HISTOGRAM */ +}; + +static const struct iw_handler_def wavelan_handler_def = +{ + .num_standard = ARRAY_SIZE(wavelan_handler), + .num_private = ARRAY_SIZE(wavelan_private_handler), + .num_private_args = ARRAY_SIZE(wavelan_private_args), + .standard = wavelan_handler, + .private = wavelan_private_handler, + .private_args = wavelan_private_args, + .get_wireless_stats = wavelan_get_wireless_stats, +}; + +/*------------------------------------------------------------------*/ +/* + * Get wireless statistics + * Called by /proc/net/wireless... + */ +static iw_stats * +wavelan_get_wireless_stats(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + mmr_t m; + iw_stats * wstats; + unsigned long flags; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); +#endif + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&lp->spinlock, flags); + + wstats = &lp->wstats; + + /* Get data from the mmc */ + mmc_out(base, mmwoff(0, mmw_freeze), 1); + + mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); + mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2); + mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4); + + mmc_out(base, mmwoff(0, mmw_freeze), 0); + + /* Copy data to wireless stuff */ + wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; + wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; + wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; + wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; + wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) | + ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) | + ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); + wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; + wstats->discard.code = 0L; + wstats->discard.misc = 0L; + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name); +#endif + return &lp->wstats; +} + +/************************* PACKET RECEPTION *************************/ +/* + * This part deal with receiving the packets. + * The interrupt handler get an interrupt when a packet has been + * successfully received and called this part... + */ + +/*------------------------------------------------------------------*/ +/* + * Calculate the starting address of the frame pointed to by the receive + * frame pointer and verify that the frame seem correct + * (called by wv_packet_rcv()) + */ +static int +wv_start_of_frame(struct net_device * dev, + int rfp, /* end of frame */ + int wrap) /* start of buffer */ +{ + unsigned int base = dev->base_addr; + int rp; + int len; + + rp = (rfp - 5 + RX_SIZE) % RX_SIZE; + outb(rp & 0xff, PIORL(base)); + outb(((rp >> 8) & PIORH_MASK), PIORH(base)); + len = inb(PIOP(base)); + len |= inb(PIOP(base)) << 8; + + /* Sanity checks on size */ + /* Frame too big */ + if(len > MAXDATAZ + 100) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n", + dev->name, rfp, len); +#endif + return(-1); + } + + /* Frame too short */ + if(len < 7) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n", + dev->name, rfp, len); +#endif + return(-1); + } + + /* Wrap around buffer */ + if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */ + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n", + dev->name, wrap, rfp, len); +#endif + return(-1); + } + + return((rp - len + RX_SIZE) % RX_SIZE); +} /* wv_start_of_frame */ + +/*------------------------------------------------------------------*/ +/* + * This routine does the actual copy of data (including the ethernet + * header structure) from the WaveLAN card to an sk_buff chain that + * will be passed up to the network interface layer. NOTE: We + * currently don't handle trailer protocols (neither does the rest of + * the network interface), so if that is needed, it will (at least in + * part) be added here. The contents of the receive ring buffer are + * copied to a message chain that is then passed to the kernel. + * + * Note: if any errors occur, the packet is "dropped on the floor" + * (called by wv_packet_rcv()) + */ +static void +wv_packet_read(struct net_device * dev, + int fd_p, + int sksize) +{ + net_local * lp = netdev_priv(dev); + struct sk_buff * skb; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", + dev->name, fd_p, sksize); +#endif + + /* Allocate some buffer for the new packet */ + if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n", + dev->name, sksize); +#endif + dev->stats.rx_dropped++; + /* + * Not only do we want to return here, but we also need to drop the + * packet on the floor to clear the interrupt. + */ + return; + } + + skb_reserve(skb, 2); + fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize); + skb->protocol = eth_type_trans(skb, dev); + +#ifdef DEBUG_RX_INFO + wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read"); +#endif /* DEBUG_RX_INFO */ + + /* Statistics gathering & stuff associated. + * It seem a bit messy with all the define, but it's really simple... */ + if( +#ifdef IW_WIRELESS_SPY + (lp->spy_data.spy_number > 0) || +#endif /* IW_WIRELESS_SPY */ +#ifdef HISTOGRAM + (lp->his_number > 0) || +#endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + (do_roaming) || +#endif /* WAVELAN_ROAMING */ + 0) + { + u_char stats[3]; /* Signal level, Noise level, Signal quality */ + + /* read signal level, silence level and signal quality bytes */ + fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE, + stats, 3); +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", + dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F); +#endif + +#ifdef WAVELAN_ROAMING + if(do_roaming) + if(WAVELAN_BEACON(skb->data)) + wl_roam_gather(dev, skb->data, stats); +#endif /* WAVELAN_ROAMING */ + +#ifdef WIRELESS_SPY + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + wl_his_gather(dev, stats); +#endif /* HISTOGRAM */ + } + + /* + * Hand the packet to the Network Module + */ + netif_rx(skb); + + /* Keep stats up to date */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += sksize; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); +#endif + return; +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called by the interrupt handler to initiate a + * packet transfer from the card to the network interface layer above + * this driver. This routine checks if a buffer has been successfully + * received by the WaveLAN card. If so, the routine wv_packet_read is + * called to do the actual transfer of the card's data including the + * ethernet header into a packet consisting of an sk_buff chain. + * (called by wavelan_interrupt()) + * Note : the spinlock is already grabbed for us and irq are disabled. + */ +static void +wv_packet_rcv(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + int newrfp; + int rp; + int len; + int f_start; + int status; + int i593_rfp; + int stat_ptr; + u_char c[4]; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name); +#endif + + /* Get the new receive frame pointer from the i82593 chip */ + outb(CR0_STATUS_2 | OP0_NOP, LCCR(base)); + i593_rfp = inb(LCSR(base)); + i593_rfp |= inb(LCSR(base)) << 8; + i593_rfp %= RX_SIZE; + + /* Get the new receive frame pointer from the WaveLAN card. + * It is 3 bytes more than the increment of the i82593 receive + * frame pointer, for each packet. This is because it includes the + * 3 roaming bytes added by the mmc. + */ + newrfp = inb(RPLL(base)); + newrfp |= inb(RPLH(base)) << 8; + newrfp %= RX_SIZE; + +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + +#ifdef DEBUG_RX_ERROR + /* If no new frame pointer... */ + if(lp->overrunning || newrfp == lp->rfp) + printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + + /* Read all frames (packets) received */ + while(newrfp != lp->rfp) + { + /* A frame is composed of the packet, followed by a status word, + * the length of the frame (word) and the mmc info (SNR & qual). + * It's because the length is at the end that we can only scan + * frames backward. */ + + /* Find the first frame by skipping backwards over the frames */ + rp = newrfp; /* End of last frame */ + while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) && + (f_start != -1)) + rp = f_start; + + /* If we had a problem */ + if(f_start == -1) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "wavelan_cs: cannot find start of frame "); + printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + lp->rfp = rp; /* Get to the last usable frame */ + continue; + } + + /* f_start point to the beggining of the first frame received + * and rp to the beggining of the next one */ + + /* Read status & length of the frame */ + stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE; + stat_ptr = read_ringbuf(dev, stat_ptr, c, 4); + status = c[0] | (c[1] << 8); + len = c[2] | (c[3] << 8); + + /* Check status */ + if((status & RX_RCV_OK) != RX_RCV_OK) + { + dev->stats.rx_errors++; + if(status & RX_NO_SFD) + dev->stats.rx_frame_errors++; + if(status & RX_CRC_ERR) + dev->stats.rx_crc_errors++; + if(status & RX_OVRRUN) + dev->stats.rx_over_errors++; + +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n", + dev->name, status); +#endif + } + else + /* Read the packet and transmit to Linux */ + wv_packet_read(dev, f_start, len - 2); + + /* One frame has been processed, skip it */ + lp->rfp = rp; + } + + /* + * Update the frame stop register, but set it to less than + * the full 8K to allow space for 3 bytes of signal strength + * per packet. + */ + lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; + outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); + outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); + outb(OP1_SWIT_TO_PORT_0, LCCR(base)); + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name); +#endif +} + +/*********************** PACKET TRANSMISSION ***********************/ +/* + * This part deal with sending packet through the wavelan + * We copy the packet to the send buffer and then issue the send + * command to the i82593. The result of this operation will be + * checked in wavelan_interrupt() + */ + +/*------------------------------------------------------------------*/ +/* + * This routine fills in the appropriate registers and memory + * locations on the WaveLAN card and starts the card off on + * the transmit. + * (called in wavelan_packet_xmit()) + */ +static void +wv_packet_write(struct net_device * dev, + void * buf, + short length) +{ + net_local * lp = netdev_priv(dev); + unsigned int base = dev->base_addr; + unsigned long flags; + int clen = length; + register u_short xmtdata_base = TX_BASE; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length); +#endif + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Write the length of data buffer followed by the buffer */ + outb(xmtdata_base & 0xff, PIORL(base)); + outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(clen & 0xff, PIOP(base)); /* lsb */ + outb(clen >> 8, PIOP(base)); /* msb */ + + /* Send the data */ + outsb(PIOP(base), buf, clen); + + /* Indicate end of transmit chain */ + outb(OP0_NOP, PIOP(base)); + /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */ + outb(OP0_NOP, PIOP(base)); + + /* Reset the transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + /* Send the transmit command */ + wv_82593_cmd(dev, "wv_packet_write(): transmit", + OP0_TRANSMIT, SR0_NO_RESULT); + + /* Make sure the watchdog will keep quiet for a while */ + dev->trans_start = jiffies; + + /* Keep stats up to date */ + dev->stats.tx_bytes += length; + + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_TX_INFO + wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write"); +#endif /* DEBUG_TX_INFO */ + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called when we want to send a packet (NET3 callback) + * In this routine, we check if the harware is ready to accept + * the packet. We also prevent reentrance. Then, we call the function + * to send the packet... + */ +static netdev_tx_t +wavelan_packet_xmit(struct sk_buff * skb, + struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + unsigned long flags; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, + (unsigned) skb); +#endif + + /* + * Block a timer-based transmit from overlapping a previous transmit. + * In other words, prevent reentering this routine. + */ + netif_stop_queue(dev); + + /* If somebody has asked to reconfigure the controller, + * we can do it now */ + if(lp->reconfig_82593) + { + spin_lock_irqsave(&lp->spinlock, flags); /* Disable interrupts */ + wv_82593_config(dev); + spin_unlock_irqrestore(&lp->spinlock, flags); /* Re-enable interrupts */ + /* Note : the configure procedure was totally synchronous, + * so the Tx buffer is now free */ + } + + /* Check if we need some padding */ + /* Note : on wireless the propagation time is in the order of 1us, + * and we don't have the Ethernet specific requirement of beeing + * able to detect collisions, therefore in theory we don't really + * need to pad. Jean II */ + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + + wv_packet_write(dev, skb->data, skb->len); + + dev_kfree_skb(skb); + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); +#endif + return NETDEV_TX_OK; +} + +/********************** HARDWARE CONFIGURATION **********************/ +/* + * This part do the real job of starting and configuring the hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Routine to initialize the Modem Management Controller. + * (called by wv_hw_config()) + */ +static int +wv_mmc_init(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + psa_t psa; + mmw_t m; + int configured; + int i; /* Loop counter */ + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); +#endif + + /* Read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + + /* + * Check the first three octets of the MAC addr for the manufacturer's code. + * Note: If you get the error message below, you've got a + * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on + * how to configure your card... + */ + for (i = 0; i < ARRAY_SIZE(MAC_ADDRESSES); i++) + if ((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) && + (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) && + (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2])) + break; + + /* If we have not found it... */ + if (i == ARRAY_SIZE(MAC_ADDRESSES)) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n", + dev->name, psa.psa_univ_mac_addr[0], + psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]); +#endif + return FALSE; + } + + /* Get the MAC address */ + memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE); + +#ifdef USE_PSA_CONFIG + configured = psa.psa_conf_status & 1; +#else + configured = 0; +#endif + + /* Is the PSA is not configured */ + if(!configured) + { + /* User will be able to configure NWID after (with iwconfig) */ + psa.psa_nwid[0] = 0; + psa.psa_nwid[1] = 0; + + /* As NWID is not set : no NWID checking */ + psa.psa_nwid_select = 0; + + /* Disable encryption */ + psa.psa_encryption_select = 0; + + /* Set to standard values + * 0x04 for AT, + * 0x01 for MCA, + * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) + */ + if (psa.psa_comp_number & 1) + psa.psa_thr_pre_set = 0x01; + else + psa.psa_thr_pre_set = 0x04; + psa.psa_quality_thr = 0x03; + + /* It is configured */ + psa.psa_conf_status |= 1; + +#ifdef USE_PSA_CONFIG + /* Write the psa */ + psa_write(dev, (char *)psa.psa_nwid - (char *)&psa, + (unsigned char *)psa.psa_nwid, 4); + psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, + (unsigned char *)&psa.psa_thr_pre_set, 1); + psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa, + (unsigned char *)&psa.psa_quality_thr, 1); + psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa, + (unsigned char *)&psa.psa_conf_status, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); +#endif /* USE_PSA_CONFIG */ + } + + /* Zero the mmc structure */ + memset(&m, 0x00, sizeof(m)); + + /* Copy PSA info to the mmc */ + m.mmw_netw_id_l = psa.psa_nwid[1]; + m.mmw_netw_id_h = psa.psa_nwid[0]; + + if(psa.psa_nwid_select & 1) + m.mmw_loopt_sel = 0x00; + else + m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; + + memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, + sizeof(m.mmw_encr_key)); + + if(psa.psa_encryption_select) + m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; + else + m.mmw_encr_enable = 0; + + m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; + m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; + + /* + * Set default modem control parameters. + * See NCR document 407-0024326 Rev. A. + */ + m.mmw_jabber_enable = 0x01; + m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; + m.mmw_ifs = 0x20; + m.mmw_mod_delay = 0x04; + m.mmw_jam_time = 0x38; + + m.mmw_des_io_invert = 0; + m.mmw_freeze = 0; + m.mmw_decay_prm = 0; + m.mmw_decay_updat_prm = 0; + + /* Write all info to mmc */ + mmc_write(base, 0, (u_char *)&m, sizeof(m)); + + /* The following code start the modem of the 2.00 frequency + * selectable cards at power on. It's not strictly needed for the + * following boots... + * The original patch was by Joe Finney for the PCMCIA driver, but + * I've cleaned it a bit and add documentation. + * Thanks to Loeke Brederveld from Lucent for the info. + */ + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) + * (does it work for everybody ? - especially old cards...) */ + /* Note : WFREQSEL verify that it is able to read from EEprom + * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). + * My test is more crude but do work... */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + /* We must download the frequency parameters to the + * synthetisers (from the EEprom - area 1) + * Note : as the EEprom is auto decremented, we set the end + * if the area... */ + m.mmw_fee_addr = 0x0F; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, + (unsigned char *)&m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + +#ifdef DEBUG_CONFIG_INFO + /* The frequency was in the last word downloaded... */ + mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m, + (unsigned char *)&m.mmw_fee_data_l, 2); + + /* Print some info for the user */ + printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n", + dev->name, + ((m.mmw_fee_data_h << 4) | + (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L); +#endif + + /* We must now download the power adjust value (gain) to + * the synthetisers (from the EEprom - area 7 - DAC) */ + m.mmw_fee_addr = 0x61; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, + (unsigned char *)&m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished */ + } /* if 2.00 card */ + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * Routine to gracefully turn off reception, and wait for any commands + * to complete. + * (called in wv_ru_start() and wavelan_close() and wavelan_event()) + */ +static int +wv_ru_stop(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + unsigned long flags; + int status; + int spin; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); +#endif + + spin_lock_irqsave(&lp->spinlock, flags); + + /* First, send the LAN controller a stop receive command */ + wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv", + OP0_STOP_RCV, SR0_NO_RESULT); + + /* Then, spin until the receive unit goes idle */ + spin = 300; + do + { + udelay(10); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0)); + + /* Now, spin until the chip finishes executing its current command */ + do + { + udelay(10); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); + + spin_unlock_irqrestore(&lp->spinlock, flags); + + /* If there was a problem */ + if(spin <= 0) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n", + dev->name); +#endif + return FALSE; + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name); +#endif + return TRUE; +} /* wv_ru_stop */ + +/*------------------------------------------------------------------*/ +/* + * This routine starts the receive unit running. First, it checks if + * the card is actually ready. Then the card is instructed to receive + * packets again. + * (called in wv_hw_reset() & wavelan_open()) + */ +static int +wv_ru_start(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + unsigned long flags; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); +#endif + + /* + * We need to start from a quiescent state. To do so, we could check + * if the card is already running, but instead we just try to shut + * it down. First, we disable reception (in case it was already enabled). + */ + if(!wv_ru_stop(dev)) + return FALSE; + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Now we know that no command is being executed. */ + + /* Set the receive frame pointer and stop pointer */ + lp->rfp = 0; + outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); + + /* Reset ring management. This sets the receive frame pointer to 1 */ + outb(OP1_RESET_RING_MNGMT, LCCR(base)); + +#if 0 + /* XXX the i82593 manual page 6-4 seems to indicate that the stop register + should be set as below */ + /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/ +#elif 0 + /* but I set it 0 instead */ + lp->stop = 0; +#else + /* but I set it to 3 bytes per packet less than 8K */ + lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; +#endif + outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); + outb(OP1_INT_ENABLE, LCCR(base)); + outb(OP1_SWIT_TO_PORT_0, LCCR(base)); + + /* Reset receive DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write_slow(base, HACR_DEFAULT); + + /* Receive DMA on channel 1 */ + wv_82593_cmd(dev, "wv_ru_start(): rcv-enable", + CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT); + +#ifdef DEBUG_I82593_SHOW + { + int status; + int opri; + int spin = 10000; + + /* spin until the chip starts receiving */ + do + { + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + if(spin-- <= 0) + break; + } + while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) && + ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY)); + printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n", + (status & SR3_RCV_STATE_MASK), i); + } +#endif + + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard config of the WaveLAN controller (i82593). + * In the ISA driver, this is integrated in wavelan_hardware_reset() + * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit()) + */ +static int +wv_82593_config(struct net_device * dev) +{ + unsigned int base = dev->base_addr; + net_local * lp = netdev_priv(dev); + struct i82593_conf_block cfblk; + int ret = TRUE; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name); +#endif + + /* Create & fill i82593 config block + * + * Now conform to Wavelan document WCIN085B + */ + memset(&cfblk, 0x00, sizeof(struct i82593_conf_block)); + cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */ + cfblk.fifo_limit = 5; /* = 56 B rx and 40 B tx fifo thresholds */ + cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ + cfblk.fifo_32 = 1; + cfblk.throttle_enb = FALSE; + cfblk.contin = TRUE; /* enable continuous mode */ + cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */ + cfblk.addr_len = WAVELAN_ADDR_SIZE; + cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ + cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */ + cfblk.loopback = FALSE; + cfblk.lin_prio = 0; /* conform to 802.3 backoff algorithm */ + cfblk.exp_prio = 5; /* conform to 802.3 backoff algorithm */ + cfblk.bof_met = 1; /* conform to 802.3 backoff algorithm */ + cfblk.ifrm_spc = 0x20 >> 4; /* 32 bit times interframe spacing */ + cfblk.slottim_low = 0x20 >> 5; /* 32 bit times slot time */ + cfblk.slottim_hi = 0x0; + cfblk.max_retr = 15; + cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE); /* Promiscuous mode */ + cfblk.bc_dis = FALSE; /* Enable broadcast reception */ + cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ + cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */ + cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ + cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ + cfblk.cs_filter = 0; /* CS is recognized immediately */ + cfblk.crs_src = FALSE; /* External carrier sense */ + cfblk.cd_filter = 0; /* CD is recognized immediately */ + cfblk.min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length 64 bytes */ + cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ + cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ + cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ + cfblk.artx = TRUE; /* Disable automatic retransmission */ + cfblk.sarec = TRUE; /* Disable source addr trig of CD */ + cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ + cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ + cfblk.lbpkpol = TRUE; /* Loopback pin active high */ + cfblk.fdx = FALSE; /* Disable full duplex operation */ + cfblk.dummy_6 = 0x3f; /* all ones */ + cfblk.mult_ia = FALSE; /* No multiple individual addresses */ + cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ + cfblk.dummy_1 = TRUE; /* set to 1 */ + cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ +#ifdef MULTICAST_ALL + cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE); /* Allow all multicasts */ +#else + cfblk.mc_all = FALSE; /* No multicast all mode */ +#endif + cfblk.rcv_mon = 0; /* Monitor mode disabled */ + cfblk.frag_acpt = TRUE; /* Do not accept fragments */ + cfblk.tstrttrs = FALSE; /* No start transmission threshold */ + cfblk.fretx = TRUE; /* FIFO automatic retransmission */ + cfblk.syncrqs = FALSE; /* Synchronous DRQ deassertion... */ + cfblk.sttlen = TRUE; /* 6 byte status registers */ + cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ + cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ + cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ + cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ + +#ifdef DEBUG_I82593_SHOW + print_hex_dump(KERN_DEBUG, "wavelan_cs: config block: ", DUMP_PREFIX_NONE, + 16, 1, &cfblk, sizeof(struct i82593_conf_block), false); +#endif + + /* Copy the config block to the i82593 */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base)); /* lsb */ + outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base)); /* msb */ + outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block)); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): configure", + OP0_CONFIGURE, SR0_CONFIGURE_DONE)) + ret = FALSE; + + /* Initialize adapter's ethernet MAC address */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */ + outb(0, PIOP(base)); /* byte count msb */ + outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup", + OP0_IA_SETUP, SR0_IA_SETUP_DONE)) + ret = FALSE; + +#ifdef WAVELAN_ROAMING + /* If roaming is enabled, join the "Beacon Request" multicast group... */ + /* But only if it's not in there already! */ + if(do_roaming) + dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1); +#endif /* WAVELAN_ROAMING */ + + /* If any multicast address to set */ + if(lp->mc_count) + { + struct dev_mc_list * dmi; + int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n", + dev->name, lp->mc_count); + for(dmi=dev->mc_list; dmi; dmi=dmi->next) + printk(KERN_DEBUG " %pM\n", dmi->dmi_addr); +#endif + + /* Initialize adapter's ethernet multicast addresses */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */ + outb((addrs_len >> 8), PIOP(base)); /* byte count msb */ + for(dmi=dev->mc_list; dmi; dmi=dmi->next) + outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup", + OP0_MC_SETUP, SR0_MC_SETUP_DONE)) + ret = FALSE; + lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */ + } + + /* Job done, clear the flag */ + lp->reconfig_82593 = FALSE; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name); +#endif + return(ret); +} + +/*------------------------------------------------------------------*/ +/* + * Read the Access Configuration Register, perform a software reset, + * and then re-enable the card's software. + * + * If I understand correctly : reset the pcmcia interface of the + * wavelan. + * (called by wv_config()) + */ +static int +wv_pcmcia_reset(struct net_device * dev) +{ + int i; + conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 }; + struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name); +#endif + + i = pcmcia_access_configuration_register(link, ®); + if (i != 0) + { + cs_error(link, AccessConfigurationRegister, i); + return FALSE; + } + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n", + dev->name, (u_int) reg.Value); +#endif + + reg.Action = CS_WRITE; + reg.Value = reg.Value | COR_SW_RESET; + i = pcmcia_access_configuration_register(link, ®); + if (i != 0) + { + cs_error(link, AccessConfigurationRegister, i); + return FALSE; + } + + reg.Action = CS_WRITE; + reg.Value = COR_LEVEL_IRQ | COR_CONFIG; + i = pcmcia_access_configuration_register(link, ®); + if (i != 0) + { + cs_error(link, AccessConfigurationRegister, i); + return FALSE; + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * wavelan_hw_config() is called after a CARD_INSERTION event is + * received, to configure the wavelan hardware. + * Note that the reception will be enabled in wavelan->open(), so the + * device is configured but idle... + * Performs the following actions: + * 1. A pcmcia software reset (using wv_pcmcia_reset()) + * 2. A power reset (reset DMA) + * 3. Reset the LAN controller + * 4. Initialize the radio modem (using wv_mmc_init) + * 5. Configure LAN controller (using wv_82593_config) + * 6. Perform a diagnostic on the LAN controller + * (called by wavelan_event() & wv_hw_reset()) + */ +static int +wv_hw_config(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + unsigned int base = dev->base_addr; + unsigned long flags; + int ret = FALSE; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name); +#endif + + /* compile-time check the sizes of structures */ + BUILD_BUG_ON(sizeof(psa_t) != PSA_SIZE); + BUILD_BUG_ON(sizeof(mmw_t) != MMW_SIZE); + BUILD_BUG_ON(sizeof(mmr_t) != MMR_SIZE); + + /* Reset the pcmcia interface */ + if(wv_pcmcia_reset(dev) == FALSE) + return FALSE; + + /* Disable interrupts */ + spin_lock_irqsave(&lp->spinlock, flags); + + /* Disguised goto ;-) */ + do + { + /* Power UP the module + reset the modem + reset host adapter + * (in fact, reset DMA channels) */ + hacr_write_slow(base, HACR_RESET); + hacr_write(base, HACR_DEFAULT); + + /* Check if the module has been powered up... */ + if(hasr_read(base) & HASR_NO_CLK) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n", + dev->name); +#endif + break; + } + + /* initialize the modem */ + if(wv_mmc_init(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n", + dev->name); +#endif + break; + } + + /* reset the LAN controller (i82593) */ + outb(OP0_RESET, LCCR(base)); + mdelay(1); /* A bit crude ! */ + + /* Initialize the LAN controller */ + if(wv_82593_config(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", + dev->name); +#endif + break; + } + + /* Diagnostic */ + if(wv_diag(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n", + dev->name); +#endif + break; + } + + /* + * insert code for loopback test here + */ + + /* The device is now configured */ + lp->configured = 1; + ret = TRUE; + } + while(0); + + /* Re-enable interrupts */ + spin_unlock_irqrestore(&lp->spinlock, flags); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); +#endif + return(ret); +} + +/*------------------------------------------------------------------*/ +/* + * Totally reset the wavelan and restart it. + * Performs the following actions: + * 1. Call wv_hw_config() + * 2. Start the LAN controller's receive unit + * (called by wavelan_event(), wavelan_watchdog() and wavelan_open()) + */ +static void +wv_hw_reset(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name); +#endif + + lp->nresets++; + lp->configured = 0; + + /* Call wv_hw_config() for most of the reset & init stuff */ + if(wv_hw_config(dev) == FALSE) + return; + + /* start receive unit */ + wv_ru_start(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * wv_pcmcia_config() is called after a CARD_INSERTION event is + * received, to configure the PCMCIA socket, and to make the ethernet + * device available to the system. + * (called by wavelan_event()) + */ +static int +wv_pcmcia_config(struct pcmcia_device * link) +{ + struct net_device * dev = (struct net_device *) link->priv; + int i; + win_req_t req; + memreq_t mem; + net_local * lp = netdev_priv(dev); + + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link); +#endif + + do + { + i = pcmcia_request_io(link, &link->io); + if (i != 0) + { + cs_error(link, RequestIO, i); + break; + } + + /* + * Now allocate an interrupt line. Note that this does not + * actually assign a handler to the interrupt. + */ + i = pcmcia_request_irq(link, &link->irq); + if (i != 0) + { + cs_error(link, RequestIRQ, i); + break; + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + link->conf.ConfigIndex = 1; + i = pcmcia_request_configuration(link, &link->conf); + if (i != 0) + { + cs_error(link, RequestConfiguration, i); + break; + } + + /* + * Allocate a small memory window. Note that the struct pcmcia_device + * structure provides space for one window handle -- if your + * device needs several windows, you'll need to keep track of + * the handles in your private data structure, link->priv. + */ + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + req.Base = req.Size = 0; + req.AccessSpeed = mem_speed; + i = pcmcia_request_window(&link, &req, &link->win); + if (i != 0) + { + cs_error(link, RequestWindow, i); + break; + } + + lp->mem = ioremap(req.Base, req.Size); + dev->mem_start = (u_long)lp->mem; + dev->mem_end = dev->mem_start + req.Size; + + mem.CardOffset = 0; mem.Page = 0; + i = pcmcia_map_mem_page(link->win, &mem); + if (i != 0) + { + cs_error(link, MapMemPage, i); + break; + } + + /* Feed device with this info... */ + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + netif_start_queue(dev); + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART %p IRQ %d IOPORT 0x%x\n", + lp->mem, dev->irq, (u_int) dev->base_addr); +#endif + + SET_NETDEV_DEV(dev, &handle_to_dev(link)); + i = register_netdev(dev); + if(i != 0) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n"); +#endif + break; + } + } + while(0); /* Humm... Disguised goto !!! */ + + /* If any step failed, release any partially configured state */ + if(i != 0) + { + wv_pcmcia_release(link); + return FALSE; + } + + strcpy(((net_local *) netdev_priv(dev))->node.dev_name, dev->name); + link->dev_node = &((net_local *) netdev_priv(dev))->node; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "<-wv_pcmcia_config()\n"); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * After a card is removed, wv_pcmcia_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void +wv_pcmcia_release(struct pcmcia_device *link) +{ + struct net_device * dev = (struct net_device *) link->priv; + net_local * lp = netdev_priv(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link); +#endif + + iounmap(lp->mem); + pcmcia_disable_device(link); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); +#endif +} + +/************************ INTERRUPT HANDLING ************************/ + +/* + * This function is the interrupt handler for the WaveLAN card. This + * routine will be called whenever: + * 1. A packet is received. + * 2. A packet has successfully been transferred and the unit is + * ready to transmit another packet. + * 3. A command has completed execution. + */ +static irqreturn_t +wavelan_interrupt(int irq, + void * dev_id) +{ + struct net_device * dev = dev_id; + net_local * lp; + unsigned int base; + int status0; + u_int tx_status; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); +#endif + + lp = netdev_priv(dev); + base = dev->base_addr; + +#ifdef DEBUG_INTERRUPT_INFO + /* Check state of our spinlock (it should be cleared) */ + if(spin_is_locked(&lp->spinlock)) + printk(KERN_DEBUG + "%s: wavelan_interrupt(): spinlock is already locked !!!\n", + dev->name); +#endif + + /* Prevent reentrancy. We need to do that because we may have + * multiple interrupt handler running concurently. + * It is safe because interrupts are disabled before aquiring + * the spinlock. */ + spin_lock(&lp->spinlock); + + /* Treat all pending interrupts */ + while(1) + { + /* ---------------- INTERRUPT CHECKING ---------------- */ + /* + * Look for the interrupt and verify the validity + */ + outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); + status0 = inb(LCSR(base)); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, + (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT); + if(status0&SR0_INTERRUPT) + { + printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" : + ((status0 & SR0_EXECUTION) ? "cmd" : + ((status0 & SR0_RECEPTION) ? "recv" : "unknown")), + (status0 & SR0_EVENT_MASK)); + } + else + printk("\n"); +#endif + + /* Return if no actual interrupt from i82593 (normal exit) */ + if(!(status0 & SR0_INTERRUPT)) + break; + + /* If interrupt is both Rx and Tx or none... + * This code in fact is there to catch the spurious interrupt + * when you remove the wavelan pcmcia card from the socket */ + if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) || + ((status0 & SR0_BOTH_RX_TX) == 0x0)) + { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n", + dev->name, status0); +#endif + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + break; + } + + /* ----------------- RECEIVING PACKET ----------------- */ + /* + * When the wavelan signal the reception of a new packet, + * we call wv_packet_rcv() to copy if from the buffer and + * send it to NET3 + */ + if(status0 & SR0_RECEPTION) + { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name); +#endif + + if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n", + dev->name); +#endif + dev->stats.rx_over_errors++; + lp->overrunning = 1; + } + + /* Get the packet */ + wv_packet_rcv(dev); + lp->overrunning = 0; + + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + continue; + } + + /* ---------------- COMMAND COMPLETION ---------------- */ + /* + * Interrupts issued when the i82593 has completed a command. + * Most likely : transmission done + */ + + /* If a transmission has been done */ + if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || + (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE || + (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) + { +#ifdef DEBUG_TX_ERROR + if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) + printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n", + dev->name); +#endif + + /* Get transmission status */ + tx_status = inb(LCSR(base)); + tx_status |= (inb(LCSR(base)) << 8); +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n", + dev->name); + { + u_int rcv_bytes; + u_char status3; + rcv_bytes = inb(LCSR(base)); + rcv_bytes |= (inb(LCSR(base)) << 8); + status3 = inb(LCSR(base)); + printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n", + tx_status, rcv_bytes, (u_int) status3); + } +#endif + /* Check for possible errors */ + if((tx_status & TX_OK) != TX_OK) + { + dev->stats.tx_errors++; + + if(tx_status & TX_FRTL) + { +#ifdef DEBUG_TX_ERROR + printk(KERN_INFO "%s: wv_interrupt(): frame too long\n", + dev->name); +#endif + } + if(tx_status & TX_UND_RUN) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n", + dev->name); +#endif + dev->stats.tx_aborted_errors++; + } + if(tx_status & TX_LOST_CTS) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name); +#endif + dev->stats.tx_carrier_errors++; + } + if(tx_status & TX_LOST_CRS) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n", + dev->name); +#endif + dev->stats.tx_carrier_errors++; + } + if(tx_status & TX_HRT_BEAT) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name); +#endif + dev->stats.tx_heartbeat_errors++; + } + if(tx_status & TX_DEFER) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n", + dev->name); +#endif + } + /* Ignore late collisions since they're more likely to happen + * here (the WaveLAN design prevents the LAN controller from + * receiving while it is transmitting). We take action only when + * the maximum retransmit attempts is exceeded. + */ + if(tx_status & TX_COLL) + { + if(tx_status & TX_MAX_COL) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n", + dev->name); +#endif + if(!(tx_status & TX_NCOL_MASK)) + { + dev->stats.collisions += 0x10; + } + } + } + } /* if(!(tx_status & TX_OK)) */ + + dev->stats.collisions += (tx_status & TX_NCOL_MASK); + dev->stats.tx_packets++; + + netif_wake_queue(dev); + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ + } + else /* if interrupt = transmit done or retransmit done */ + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n", + status0); +#endif + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ + } + } /* while(1) */ + + spin_unlock(&lp->spinlock); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif + + /* We always return IRQ_HANDLED, because we will receive empty + * interrupts under normal operations. Anyway, it doesn't matter + * as we are dealing with an ISA interrupt that can't be shared. + * + * Explanation : under heavy receive, the following happens : + * ->wavelan_interrupt() + * (status0 & SR0_INTERRUPT) != 0 + * ->wv_packet_rcv() + * (status0 & SR0_INTERRUPT) != 0 + * ->wv_packet_rcv() + * (status0 & SR0_INTERRUPT) == 0 // i.e. no more event + * <-wavelan_interrupt() + * ->wavelan_interrupt() + * (status0 & SR0_INTERRUPT) == 0 // i.e. empty interrupt + * <-wavelan_interrupt() + * Jean II */ + return IRQ_HANDLED; +} /* wv_interrupt */ + +/*------------------------------------------------------------------*/ +/* + * Watchdog: when we start a transmission, a timer is set for us in the + * kernel. If the transmission completes, this timer is disabled. If + * the timer expires, we are called and we try to unlock the hardware. + * + * Note : This watchdog is move clever than the one in the ISA driver, + * because it try to abort the current command before reseting + * everything... + * On the other hand, it's a bit simpler, because we don't have to + * deal with the multiple Tx buffers... + */ +static void +wavelan_watchdog(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + unsigned int base = dev->base_addr; + unsigned long flags; + int aborted = FALSE; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); +#endif + +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", + dev->name); +#endif + + spin_lock_irqsave(&lp->spinlock, flags); + + /* Ask to abort the current command */ + outb(OP0_ABORT, LCCR(base)); + + /* Wait for the end of the command (a bit hackish) */ + if(wv_82593_cmd(dev, "wavelan_watchdog(): abort", + OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED)) + aborted = TRUE; + + /* Release spinlock here so that wv_hw_reset() can grab it */ + spin_unlock_irqrestore(&lp->spinlock, flags); + + /* Check if we were successful in aborting it */ + if(!aborted) + { + /* It seem that it wasn't enough */ +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n", + dev->name); +#endif + wv_hw_reset(dev); + } + +#ifdef DEBUG_PSA_SHOW + { + psa_t psa; + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + wv_psa_show(&psa); + } +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82593_SHOW + wv_ru_show(dev); +#endif + + /* We are no more waiting for something... */ + netif_wake_queue(dev); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); +#endif +} + +/********************* CONFIGURATION CALLBACKS *********************/ +/* + * Here are the functions called by the pcmcia package (cardmgr) and + * linux networking (NET3) for initialization, configuration and + * deinstallations of the Wavelan Pcmcia Hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Configure and start up the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "open" the device. + */ +static int +wavelan_open(struct net_device * dev) +{ + net_local * lp = netdev_priv(dev); + struct pcmcia_device * link = lp->link; + unsigned int base = dev->base_addr; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Check if the modem is powered up (wavelan_close() power it down */ + if(hasr_read(base) & HASR_NO_CLK) + { + /* Power up (power up time is 250us) */ + hacr_write(base, HACR_DEFAULT); + + /* Check if the module has been powered up... */ + if(hasr_read(base) & HASR_NO_CLK) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n", + dev->name); +#endif + return FALSE; + } + } + + /* Start reception and declare the driver ready */ + if(!lp->configured) + return FALSE; + if(!wv_ru_start(dev)) + wv_hw_reset(dev); /* If problem : reset */ + netif_start_queue(dev); + + /* Mark the device as used */ + link->open++; + +#ifdef WAVELAN_ROAMING + if(do_roaming) + wv_roam_init(dev); +#endif /* WAVELAN_ROAMING */ + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Shutdown the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "close" the device. + */ +static int +wavelan_close(struct net_device * dev) +{ + struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; + unsigned int base = dev->base_addr; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* If the device isn't open, then nothing to do */ + if(!link->open) + { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name); +#endif + return 0; + } + +#ifdef WAVELAN_ROAMING + /* Cleanup of roaming stuff... */ + if(do_roaming) + wv_roam_cleanup(dev); +#endif /* WAVELAN_ROAMING */ + + link->open--; + + /* If the card is still present */ + if(netif_running(dev)) + { + netif_stop_queue(dev); + + /* Stop receiving new messages and wait end of transmission */ + wv_ru_stop(dev); + + /* Power down the module */ + hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT)); + } + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); +#endif + return 0; +} + +static const struct net_device_ops wavelan_netdev_ops = { + .ndo_open = wavelan_open, + .ndo_stop = wavelan_close, + .ndo_start_xmit = wavelan_packet_xmit, + .ndo_set_multicast_list = wavelan_set_multicast_list, +#ifdef SET_MAC_ADDRESS + .ndo_set_mac_address = wavelan_set_mac_address, +#endif + .ndo_tx_timeout = wavelan_watchdog, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/*------------------------------------------------------------------*/ +/* + * wavelan_attach() creates an "instance" of the driver, allocating + * local data structures for one device (one interface). The device + * is registered with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static int +wavelan_probe(struct pcmcia_device *p_dev) +{ + struct net_device * dev; /* Interface generic data */ + net_local * lp; /* Interface specific data */ + int ret; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "-> wavelan_attach()\n"); +#endif + + /* The io structure describes IO port mapping */ + p_dev->io.NumPorts1 = 8; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = 3; + + /* Interrupt setup */ + p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = wavelan_interrupt; + + /* General socket configuration */ + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + + /* Allocate the generic data structure */ + dev = alloc_etherdev(sizeof(net_local)); + if (!dev) + return -ENOMEM; + + p_dev->priv = p_dev->irq.Instance = dev; + + lp = netdev_priv(dev); + + /* Init specific data */ + lp->configured = 0; + lp->reconfig_82593 = FALSE; + lp->nresets = 0; + /* Multicast stuff */ + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = 0; + + /* Init spinlock */ + spin_lock_init(&lp->spinlock); + + /* back links */ + lp->dev = dev; + + /* wavelan NET3 callbacks */ + dev->netdev_ops = &wavelan_netdev_ops; + dev->watchdog_timeo = WATCHDOG_JIFFIES; + SET_ETHTOOL_OPS(dev, &ops); + + dev->wireless_handlers = &wavelan_handler_def; + lp->wireless_data.spy_data = &lp->spy_data; + dev->wireless_data = &lp->wireless_data; + + /* Other specific data */ + dev->mtu = WAVELAN_MTU; + + ret = wv_pcmcia_config(p_dev); + if (ret) + return ret; + + ret = wv_hw_config(dev); + if (ret) { + dev->irq = 0; + pcmcia_disable_device(p_dev); + return ret; + } + + wv_init_info(dev); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<- wavelan_attach()\n"); +#endif + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This deletes a driver "instance". The device is de-registered with + * Card Services. If it has been released, all local data structures + * are freed. Otherwise, the structures will be freed when the device + * is released. + */ +static void +wavelan_detach(struct pcmcia_device *link) +{ +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link); +#endif + + /* Some others haven't done their job : give them another chance */ + wv_pcmcia_release(link); + + /* Free pieces */ + if(link->priv) + { + struct net_device * dev = (struct net_device *) link->priv; + + /* Remove ourselves from the kernel list of ethernet devices */ + /* Warning : can't be called from interrupt, timer or wavelan_close() */ + if (link->dev_node) + unregister_netdev(dev); + link->dev_node = NULL; + ((net_local *)netdev_priv(dev))->link = NULL; + ((net_local *)netdev_priv(dev))->dev = NULL; + free_netdev(dev); + } + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<- wavelan_detach()\n"); +#endif +} + +static int wavelan_suspend(struct pcmcia_device *link) +{ + struct net_device * dev = (struct net_device *) link->priv; + + /* NB: wavelan_close will be called, but too late, so we are + * obliged to close nicely the wavelan here. David, could you + * close the device before suspending them ? And, by the way, + * could you, on resume, add a "route add -net ..." after the + * ifconfig up ? Thanks... */ + + /* Stop receiving new messages and wait end of transmission */ + wv_ru_stop(dev); + + if (link->open) + netif_device_detach(dev); + + /* Power down the module */ + hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT)); + + return 0; +} + +static int wavelan_resume(struct pcmcia_device *link) +{ + struct net_device * dev = (struct net_device *) link->priv; + + if (link->open) { + wv_hw_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + + +static struct pcmcia_device_id wavelan_ids[] = { + PCMCIA_DEVICE_PROD_ID12("AT&T","WaveLAN/PCMCIA", 0xe7c5affd, 0x1bc50975), + PCMCIA_DEVICE_PROD_ID12("Digital", "RoamAbout/DS", 0x9999ab35, 0x00d05e06), + PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/PCMCIA", 0x23eb9949, 0x1bc50975), + PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/PCMCIA", 0x24358cd4, 0x1bc50975), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, wavelan_ids); + +static struct pcmcia_driver wavelan_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "wavelan_cs", + }, + .probe = wavelan_probe, + .remove = wavelan_detach, + .id_table = wavelan_ids, + .suspend = wavelan_suspend, + .resume = wavelan_resume, +}; + +static int __init +init_wavelan_cs(void) +{ + return pcmcia_register_driver(&wavelan_driver); +} + +static void __exit +exit_wavelan_cs(void) +{ + pcmcia_unregister_driver(&wavelan_driver); +} + +module_init(init_wavelan_cs); +module_exit(exit_wavelan_cs); diff --git a/drivers/staging/wavelan/wavelan_cs.h b/drivers/staging/wavelan/wavelan_cs.h new file mode 100644 index 000000000000..2e4bfe4147c6 --- /dev/null +++ b/drivers/staging/wavelan/wavelan_cs.h @@ -0,0 +1,386 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganization and extension of the driver. + * Original copyright follow. See wavelan_cs.h for details. + * + * This file contain the declarations of the Wavelan hardware. Note that + * the Pcmcia Wavelan include a i82593 controller (see definitions in + * file i82593.h). + * + * The main difference between the pcmcia hardware and the ISA one is + * the Ethernet Controller (i82593 instead of i82586). The i82593 allow + * only one send buffer. The PSA (Parameter Storage Area : EEprom for + * permanent storage of various info) is memory mapped, but not the + * MMI (Modem Management Interface). + */ + +/* + * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: + * An Ethernet-like radio transceiver controlled by an Intel 82593 + * coprocessor. + * + * + **************************************************************************** + * Copyright 1995 + * Anthony D. Joseph + * Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this program + * for any purpose and without fee is hereby granted, provided + * that this copyright and permission notice appear on all copies + * and supporting documentation, the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * program without specific prior permission, and notice be given + * in supporting documentation that copying and distribution is + * by permission of M.I.T. M.I.T. makes no representations about + * the suitability of this software for any purpose. It is pro- + * vided "as is" without express or implied warranty. + **************************************************************************** + * + * + * Credits: + * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for + * providing extremely useful information about WaveLAN PCMCIA hardware + * + * This driver is based upon several other drivers, in particular: + * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter + * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter + * Anders Klemets' PCMCIA WaveLAN adapter driver + * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter + */ + +#ifndef _WAVELAN_CS_H +#define _WAVELAN_CS_H + +/************************** MAGIC NUMBERS ***************************/ + +/* The detection of the wavelan card is made by reading the MAC address + * from the card and checking it. If you have a non AT&T product (OEM, + * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this + * part to accommodate your hardware... + */ +static const unsigned char MAC_ADDRESSES[][3] = +{ + { 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */ + { 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */ + { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ + { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ + /* Add your card here and send me the patch ! */ +}; + +/* + * Constants used to convert channels to frequencies + */ + +/* Frequency available in the 2.0 modem, in units of 250 kHz + * (as read in the offset register of the dac area). + * Used to map channel numbers used by `wfreqsel' to frequencies + */ +static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, + 0xD0, 0xF0, 0xF8, 0x150 }; + +/* Frequencies of the 1.0 modem (fixed frequencies). + * Use to map the PSA `subband' to a frequency + * Note : all frequencies apart from the first one need to be multiplied by 10 + */ +static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; + + +/*************************** PC INTERFACE ****************************/ + +/* WaveLAN host interface definitions */ + +#define LCCR(base) (base) /* LAN Controller Command Register */ +#define LCSR(base) (base) /* LAN Controller Status Register */ +#define HACR(base) (base+0x1) /* Host Adapter Command Register */ +#define HASR(base) (base+0x1) /* Host Adapter Status Register */ +#define PIORL(base) (base+0x2) /* Program I/O Register Low */ +#define RPLL(base) (base+0x2) /* Receive Pointer Latched Low */ +#define PIORH(base) (base+0x3) /* Program I/O Register High */ +#define RPLH(base) (base+0x3) /* Receive Pointer Latched High */ +#define PIOP(base) (base+0x4) /* Program I/O Port */ +#define MMR(base) (base+0x6) /* MMI Address Register */ +#define MMD(base) (base+0x7) /* MMI Data Register */ + +/* Host Adaptor Command Register bit definitions */ + +#define HACR_LOF (1 << 3) /* Lock Out Flag, toggle every 250ms */ +#define HACR_PWR_STAT (1 << 4) /* Power State, 1=active, 0=sleep */ +#define HACR_TX_DMA_RESET (1 << 5) /* Reset transmit DMA ptr on high */ +#define HACR_RX_DMA_RESET (1 << 6) /* Reset receive DMA ptr on high */ +#define HACR_ROM_WEN (1 << 7) /* EEPROM write enabled when true */ + +#define HACR_RESET (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET) +#define HACR_DEFAULT (HACR_PWR_STAT) + +/* Host Adapter Status Register bit definitions */ + +#define HASR_MMI_BUSY (1 << 2) /* MMI is busy when true */ +#define HASR_LOF (1 << 3) /* Lock out flag status */ +#define HASR_NO_CLK (1 << 4) /* active when modem not connected */ + +/* Miscellaneous bit definitions */ + +#define PIORH_SEL_TX (1 << 5) /* PIOR points to 0=rx/1=tx buffer */ +#define MMR_MMI_WR (1 << 0) /* Next MMI cycle is 0=read, 1=write */ +#define PIORH_MASK 0x1f /* only low 5 bits are significant */ +#define RPLH_MASK 0x1f /* only low 5 bits are significant */ +#define MMI_ADDR_MASK 0x7e /* Bits 1-6 of MMR are significant */ + +/* Attribute Memory map */ + +#define CIS_ADDR 0x0000 /* Card Information Status Register */ +#define PSA_ADDR 0x0e00 /* Parameter Storage Area address */ +#define EEPROM_ADDR 0x1000 /* EEPROM address (unused ?) */ +#define COR_ADDR 0x4000 /* Configuration Option Register */ + +/* Configuration Option Register bit definitions */ + +#define COR_CONFIG (1 << 0) /* Config Index, 0 when unconfigured */ +#define COR_SW_RESET (1 << 7) /* Software Reset on true */ +#define COR_LEVEL_IRQ (1 << 6) /* Level IRQ */ + +/* Local Memory map */ + +#define RX_BASE 0x0000 /* Receive memory, 8 kB */ +#define TX_BASE 0x2000 /* Transmit memory, 2 kB */ +#define UNUSED_BASE 0x2800 /* Unused, 22 kB */ +#define RX_SIZE (TX_BASE-RX_BASE) /* Size of receive area */ +#define RX_SIZE_SHIFT 6 /* Bits to shift in stop register */ + +#define TRUE 1 +#define FALSE 0 + +#define MOD_ENAL 1 +#define MOD_PROM 2 + +/* Size of a MAC address */ +#define WAVELAN_ADDR_SIZE 6 + +/* Maximum size of Wavelan packet */ +#define WAVELAN_MTU 1500 + +#define MAXDATAZ (6 + 6 + 2 + WAVELAN_MTU) + +/********************** PARAMETER STORAGE AREA **********************/ + +/* + * Parameter Storage Area (PSA). + */ +typedef struct psa_t psa_t; +struct psa_t +{ + /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */ + unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */ + unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */ + unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */ + unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */ + unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */ + unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */ + unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */ + unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */ + unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */ + unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */ + + unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */ + unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */ + unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */ +#define PSA_UNIVERSAL 0 /* Universal (factory) */ +#define PSA_LOCAL 1 /* Local */ + unsigned char psa_comp_number; /* [0x1D] Compatability Number: */ +#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */ +#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */ +#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */ +#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */ +#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */ + unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */ + unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */ +#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */ + unsigned char psa_subband; /* [0x20] Subband */ +#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */ +#define PSA_SUBBAND_2425 1 /* 2425 MHz */ +#define PSA_SUBBAND_2460 2 /* 2460 MHz */ +#define PSA_SUBBAND_2484 3 /* 2484 MHz */ +#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */ + unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */ + unsigned char psa_mod_delay; /* [0x22] Modem Delay ??? (reserved) */ + unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */ + unsigned char psa_nwid_select; /* [0x25] Network ID Select On Off */ + unsigned char psa_encryption_select; /* [0x26] Encryption On Off */ + unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */ + unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */ + unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */ + unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */ + unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */ + unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/ + unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */ + unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */ +}; + +/* Size for structure checking (if padding is correct) */ +#define PSA_SIZE 64 + +/* Calculate offset of a field in the above structure + * Warning : only even addresses are used */ +#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) + +/******************** MODEM MANAGEMENT INTERFACE ********************/ + +/* + * Modem Management Controller (MMC) write structure. + */ +typedef struct mmw_t mmw_t; +struct mmw_t +{ + unsigned char mmw_encr_key[8]; /* encryption key */ + unsigned char mmw_encr_enable; /* enable/disable encryption */ +#define MMW_ENCR_ENABLE_MODE 0x02 /* Mode of security option */ +#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option */ + unsigned char mmw_unused0[1]; /* unused */ + unsigned char mmw_des_io_invert; /* Encryption option */ +#define MMW_DES_IO_INVERT_RES 0x0F /* Reserved */ +#define MMW_DES_IO_INVERT_CTRL 0xF0 /* Control ??? (set to 0) */ + unsigned char mmw_unused1[5]; /* unused */ + unsigned char mmw_loopt_sel; /* looptest selection */ +#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* disable NWID filtering */ +#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */ +#define MMW_LOOPT_SEL_LS 0x10 /* looptest w/o collision avoidance */ +#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */ +#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */ +#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */ +#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */ + unsigned char mmw_jabber_enable; /* jabber timer enable */ + /* Abort transmissions > 200 ms */ + unsigned char mmw_freeze; /* freeze / unfreeeze signal level */ + /* 0 : signal level & qual updated for every new message, 1 : frozen */ + unsigned char mmw_anten_sel; /* antenna selection */ +#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */ +#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */ + unsigned char mmw_ifs; /* inter frame spacing */ + /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ + unsigned char mmw_mod_delay; /* modem delay (synchro) */ + unsigned char mmw_jam_time; /* jamming time (after collision) */ + unsigned char mmw_unused2[1]; /* unused */ + unsigned char mmw_thr_pre_set; /* level threshold preset */ + /* Discard all packet with signal < this value (4) */ + unsigned char mmw_decay_prm; /* decay parameters */ + unsigned char mmw_decay_updat_prm; /* decay update parameterz */ + unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */ + /* Discard all packet with quality < this value (3) */ + unsigned char mmw_netw_id_l; /* NWID low order byte */ + unsigned char mmw_netw_id_h; /* NWID high order byte */ + /* Network ID or Domain : create virtual net on the air */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmw_mode_select; /* for analog tests (set to 0) */ + unsigned char mmw_unused3[1]; /* unused */ + unsigned char mmw_fee_ctrl; /* frequency eeprom control */ +#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions */ +#define MMW_FEE_CTRL_DWLD 0x08 /* Download eeprom to mmc */ +#define MMW_FEE_CTRL_CMD 0x07 /* EEprom commands : */ +#define MMW_FEE_CTRL_READ 0x06 /* Read */ +#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */ +#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address */ +#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses */ +#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */ +#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */ +#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */ +#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers */ +#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write addr in protect register */ +#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ + /* Never issue this command (PRDS) : it's irreversible !!! */ + + unsigned char mmw_fee_addr; /* EEprom address */ +#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel */ +#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */ +#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */ +#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */ +#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */ +#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */ + + unsigned char mmw_fee_data_l; /* Write data to EEprom */ + unsigned char mmw_fee_data_h; /* high octet */ + unsigned char mmw_ext_ant; /* Setting for external antenna */ +#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */ +#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */ +#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */ +#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */ +#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */ +} __attribute__((packed)); + +/* Size for structure checking (if padding is correct) */ +#define MMW_SIZE 37 + +/* Calculate offset of a field in the above structure */ +#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) + + +/* + * Modem Management Controller (MMC) read structure. + */ +typedef struct mmr_t mmr_t; +struct mmr_t +{ + unsigned char mmr_unused0[8]; /* unused */ + unsigned char mmr_des_status; /* encryption status */ + unsigned char mmr_des_avail; /* encryption available (0x55 read) */ +#define MMR_DES_AVAIL_DES 0x55 /* DES available */ +#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */ + unsigned char mmr_des_io_invert; /* des I/O invert register */ + unsigned char mmr_unused1[5]; /* unused */ + unsigned char mmr_dce_status; /* DCE status */ +#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */ +#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ +#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ +#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ +#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ + unsigned char mmr_dsp_id; /* DSP id (AA = Daedalus rev A) */ + unsigned char mmr_unused2[2]; /* unused */ + unsigned char mmr_correct_nwid_l; /* # of correct NWID's rxd (low) */ + unsigned char mmr_correct_nwid_h; /* # of correct NWID's rxd (high) */ + /* Warning : Read high order octet first !!! */ + unsigned char mmr_wrong_nwid_l; /* # of wrong NWID's rxd (low) */ + unsigned char mmr_wrong_nwid_h; /* # of wrong NWID's rxd (high) */ + unsigned char mmr_thr_pre_set; /* level threshold preset */ +#define MMR_THR_PRE_SET 0x3F /* level threshold preset */ +#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */ + unsigned char mmr_signal_lvl; /* signal level */ +#define MMR_SIGNAL_LVL 0x3F /* signal level */ +#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_silence_lvl; /* silence level (noise) */ +#define MMR_SILENCE_LVL 0x3F /* silence level */ +#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_sgnl_qual; /* signal quality */ +#define MMR_SGNL_QUAL 0x0F /* signal quality */ +#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */ + unsigned char mmr_netw_id_l; /* NWID low order byte ??? */ + unsigned char mmr_unused3[3]; /* unused */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmr_fee_status; /* Status of frequency eeprom */ +#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision id */ +#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */ +#define MMR_FEE_STATUS_BUSY 0x04 /* EEprom busy */ + unsigned char mmr_unused4[1]; /* unused */ + unsigned char mmr_fee_data_l; /* Read data from eeprom (low) */ + unsigned char mmr_fee_data_h; /* Read data from eeprom (high) */ +}; + +/* Size for structure checking (if padding is correct) */ +#define MMR_SIZE 36 + +/* Calculate offset of a field in the above structure */ +#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) + + +/* Make the two above structures one */ +typedef union mm_t +{ + struct mmw_t w; /* Write to the mmc */ + struct mmr_t r; /* Read from the mmc */ +} mm_t; + +#endif /* _WAVELAN_CS_H */ diff --git a/drivers/staging/wavelan/wavelan_cs.p.h b/drivers/staging/wavelan/wavelan_cs.p.h new file mode 100644 index 000000000000..81d91531c4f9 --- /dev/null +++ b/drivers/staging/wavelan/wavelan_cs.p.h @@ -0,0 +1,766 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * + * This file contain all definition and declarations necessary for the + * wavelan pcmcia driver. This file is a private header, so it should + * be included only on wavelan_cs.c !!! + */ + +#ifndef WAVELAN_CS_P_H +#define WAVELAN_CS_P_H + +/************************** DOCUMENTATION **************************/ +/* + * This driver provide a Linux interface to the Wavelan Pcmcia hardware + * The Wavelan is a product of Lucent (http://www.wavelan.com/). + * This division was formerly part of NCR and then AT&T. + * Wavelan are also distributed by DEC (RoamAbout DS)... + * + * To know how to use this driver, read the PCMCIA HOWTO. + * If you want to exploit the many other fonctionalities, look comments + * in the code... + * + * This driver is the result of the effort of many peoples (see below). + */ + +/* ------------------------ SPECIFIC NOTES ------------------------ */ +/* + * Web page + * -------- + * I try to maintain a web page with the Wireless LAN Howto at : + * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html + * + * SMP + * --- + * We now are SMP compliant (I eventually fixed the remaining bugs). + * The driver has been tested on a dual P6-150 and survived my usual + * set of torture tests. + * Anyway, I spent enough time chasing interrupt re-entrancy during + * errors or reconfigure, and I designed the locked/unlocked sections + * of the driver with great care, and with the recent addition of + * the spinlock (thanks to the new API), we should be quite close to + * the truth. + * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), + * but better safe than sorry (especially at 2 Mb/s ;-). + * + * I have also looked into disabling only our interrupt on the card + * (via HACR) instead of all interrupts in the processor (via cli), + * so that other driver are not impacted, and it look like it's + * possible, but it's very tricky to do right (full of races). As + * the gain would be mostly for SMP systems, it can wait... + * + * Debugging and options + * --------------------- + * You will find below a set of '#define" allowing a very fine control + * on the driver behaviour and the debug messages printed. + * The main options are : + * o WAVELAN_ROAMING, for the experimental roaming support. + * o SET_PSA_CRC, to have your card correctly recognised by + * an access point and the Point-to-Point diagnostic tool. + * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) + * (otherwise we always start afresh with some defaults) + * + * wavelan_cs.o is darn too big + * ------------------------- + * That's true ! There is a very simple way to reduce the driver + * object by 33% (yes !). Comment out the following line : + * #include + * Other compile options can also reduce the size of it... + * + * MAC address and hardware detection : + * ---------------------------------- + * The detection code of the wavelan chech that the first 3 + * octets of the MAC address fit the company code. This type of + * detection work well for AT&T cards (because the AT&T code is + * hardcoded in wavelan_cs.h), but of course will fail for other + * manufacturer. + * + * If you are sure that your card is derived from the wavelan, + * here is the way to configure it : + * 1) Get your MAC address + * a) With your card utilities (wfreqsel, instconf, ...) + * b) With the driver : + * o compile the kernel with DEBUG_CONFIG_INFO enabled + * o Boot and look the card messages + * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan_cs.h) + * 3) Compile & verify + * 4) Send me the MAC code - I will include it in the next version... + * + */ + +/* --------------------- WIRELESS EXTENSIONS --------------------- */ +/* + * This driver is the first one to support "wireless extensions". + * This set of extensions provide you some way to control the wireless + * caracteristics of the hardware in a standard way and support for + * applications for taking advantage of it (like Mobile IP). + * + * It might be a good idea as well to fetch the wireless tools to + * configure the device and play a bit. + */ + +/* ---------------------------- FILES ---------------------------- */ +/* + * wavelan_cs.c : The actual code for the driver - C functions + * + * wavelan_cs.p.h : Private header : local types / vars for the driver + * + * wavelan_cs.h : Description of the hardware interface & structs + * + * i82593.h : Description if the Ethernet controller + */ + +/* --------------------------- HISTORY --------------------------- */ +/* + * The history of the Wavelan drivers is as complicated as history of + * the Wavelan itself (NCR -> AT&T -> Lucent). + * + * All started with Anders Klemets , + * writing a Wavelan ISA driver for the MACH microkernel. Girish + * Welling had also worked on it. + * Keith Moore modify this for the Pcmcia hardware. + * + * Robert Morris port these two drivers to BSDI + * and add specific Pcmcia support (there is currently no equivalent + * of the PCMCIA package under BSD...). + * + * Jim Binkley port both BSDI drivers to FreeBSD. + * + * Bruce Janson port the BSDI ISA driver to Linux. + * + * Anthony D. Joseph started modify Bruce driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li finished is work. + * Joe Finney patched the driver to start + * correctly 2.00 cards (2.4 GHz with frequency selection). + * David Hinds integrated the whole in his + * Pcmcia package (+ bug corrections). + * + * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some + * patchs to the Pcmcia driver. After, I added code in the ISA driver + * for Wireless Extensions and full support of frequency selection + * cards. Now, I'm doing the same to the Pcmcia driver + some + * reorganisation. + * Loeke Brederveld from Lucent has given me + * much needed informations on the Wavelan hardware. + */ + +/* By the way : for the copyright & legal stuff : + * Almost everybody wrote code under GNU or BSD license (or alike), + * and want that their original copyright remain somewhere in the + * code (for myself, I go with the GPL). + * Nobody want to take responsibility for anything, except the fame... + */ + +/* --------------------------- CREDITS --------------------------- */ +/* + * Credits: + * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and + * Loeke Brederveld of Lucent for providing extremely useful + * information about WaveLAN PCMCIA hardware + * + * This driver is based upon several other drivers, in particular: + * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter + * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter + * Anders Klemets' PCMCIA WaveLAN adapter driver + * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter + * + * Additional Credits: + * + * This software was originally developed under Linux 1.2.3 + * (Slackware 2.0 distribution). + * And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+) + * with an HP OmniBook 4000 and then a 5500. + * + * It is based on other device drivers and information either written + * or supplied by: + * James Ashton (jaa101@syseng.anu.edu.au), + * Ajay Bakre (bakre@paul.rutgers.edu), + * Donald Becker (becker@super.org), + * Jim Binkley , + * Loeke Brederveld , + * Allan Creighton (allanc@cs.su.oz.au), + * Brent Elphick , + * Joe Finney , + * Matthew Geier (matthew@cs.su.oz.au), + * Remo di Giovanni (remo@cs.su.oz.au), + * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + * David Hinds , + * Jan Hoogendoorn (c/o marteijn@lucent.com), + * Bruce Janson , + * Anthony D. Joseph , + * Anders Klemets (klemets@paul.rutgers.edu), + * Yunzhou Li , + * Marc Meertens (mmeertens@lucent.com), + * Keith Moore, + * Robert Morris (rtm@das.harvard.edu), + * Ian Parkin (ian@cs.su.oz.au), + * John Rosenberg (johnr@cs.su.oz.au), + * George Rossi (george@phm.gov.au), + * Arthur Scott (arthur@cs.su.oz.au), + * Stanislav Sinyagin + * Peter Storey, + * Jean Tourrilhes , + * Girish Welling (welling@paul.rutgers.edu) + * Clark Woodworth + * Yongguang Zhang ... + */ + +/* ------------------------- IMPROVEMENTS ------------------------- */ +/* + * I proudly present : + * + * Changes made in 2.8.22 : + * ---------------------- + * - improved wv_set_multicast_list + * - catch spurious interrupt + * - correct release of the device + * + * Changes mades in release : + * ------------------------ + * - Reorganisation of the code, function name change + * - Creation of private header (wavelan_cs.h) + * - Reorganised debug messages + * - More comments, history, ... + * - Configure earlier (in "insert" instead of "open") + * and do things only once + * - mmc_init : configure the PSA if not done + * - mmc_init : 2.00 detection better code for 2.00 init + * - better info at startup + * - Correct a HUGE bug (volatile & uncalibrated busy loop) + * in wv_82593_cmd => config speedup + * - Stop receiving & power down on close (and power up on open) + * use "ifconfig down" & "ifconfig up ; route add -net ..." + * - Send packets : add watchdog instead of pooling + * - Receive : check frame wrap around & try to recover some frames + * - wavelan_set_multicast_list : avoid reset + * - add wireless extensions (ioctl & get_wireless_stats) + * get/set nwid/frequency on fly, info for /proc/net/wireless + * - Suppress useless stuff from lp (net_local), but add link + * - More inlines + * - Lot of others minor details & cleanups + * + * Changes made in second release : + * ------------------------------ + * - Optimise wv_85893_reconfig stuff, fix potential problems + * - Change error values for ioctl + * - Non blocking wv_ru_stop() + call wv_reset() in case of problems + * - Remove development printk from wavelan_watchdog() + * - Remove of the watchdog to wavelan_close instead of wavelan_release + * fix potential problems... + * - Start debugging suspend stuff (but it's still a bit weird) + * - Debug & optimize dump header/packet in Rx & Tx (debug) + * - Use "readb" and "writeb" to be kernel 2.1 compliant + * - Better handling of bogus interrupts + * - Wireless extension : SETSPY and GETSPY + * - Remove old stuff (stats - for those needing it, just ask me...) + * - Make wireless extensions optional + * + * Changes made in third release : + * ----------------------------- + * - cleanups & typos + * - modif wireless ext (spy -> only one pointer) + * - new private ioctl to set/get quality & level threshold + * - Init : correct default value of level threshold for pcmcia + * - kill watchdog in hw_reset + * - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) + * - Add message level (debug stuff in /var/adm/debug & errors not + * displayed at console and still in /var/adm/messages) + * + * Changes made in fourth release : + * ------------------------------ + * - multicast support (yes !) thanks to Yongguang Zhang. + * + * Changes made in fifth release (2.9.0) : + * ------------------------------------- + * - Revisited multicast code (it was mostly wrong). + * - protect code in wv_82593_reconfig with dev->tbusy (oups !) + * + * Changes made in sixth release (2.9.1a) : + * -------------------------------------- + * - Change the detection code for multi manufacturer code support + * - Correct bug (hang kernel) in init when we were "rejecting" a card + * + * Changes made in seventh release (2.9.1b) : + * ---------------------------------------- + * - Update to wireless extensions changes + * - Silly bug in card initial configuration (psa_conf_status) + * + * Changes made in eigth release : + * ----------------------------- + * - Small bug in debug code (probably not the last one...) + * - 1.2.13 support (thanks to Clark Woodworth) + * + * Changes made for release in 2.9.2b : + * ---------------------------------- + * - Level threshold is now a standard wireless extension (version 4 !) + * - modules parameters types for kernel > 2.1.17 + * - updated man page + * - Others cleanup from David Hinds + * + * Changes made for release in 2.9.5 : + * --------------------------------- + * - byte count stats (courtesy of David Hinds) + * - Remove dev_tint stuff (courtesy of David Hinds) + * - Others cleanup from David Hinds + * - Encryption setting from Brent Elphick (thanks a lot !) + * - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * + * Changes made for release in 2.9.6 : + * --------------------------------- + * - fix bug : no longuer disable watchdog in case of bogus interrupt + * - increase timeout in config code for picky hardware + * - mask unused bits in status (Wireless Extensions) + * + * Changes integrated by Justin Seger & David Hinds : + * ----------------------------------------------------------------- + * - Roaming "hack" from Joe Finney + * - PSA CRC code from Bob Gray + * - Better initialisation of the i82593 controller + * from Joseph K. O'Sullivan + * + * Changes made for release in 3.0.10 : + * ---------------------------------- + * - Fix eject "hang" of the driver under 2.2.X : + * o create wv_flush_stale_links() + * o Rename wavelan_release to wv_pcmcia_release & move up + * o move unregister_netdev to wavelan_detach() + * o wavelan_release() no longer call wavelan_detach() + * o Suppress "release" timer + * o Other cleanups & fixes + * - New MAC address in the probe + * - Reorg PSA_CRC code (endian neutral & cleaner) + * - Correct initialisation of the i82593 from Lucent manual + * - Put back the watchdog, with larger timeout + * - TRANSMIT_NO_CRC is a "normal" error, so recover from it + * from Derrick J Brashear + * - Better handling of TX and RX normal failure conditions + * - #ifdef out all the roaming code + * - Add ESSID & "AP current address" ioctl stubs + * - General cleanup of the code + * + * Changes made for release in 3.0.13 : + * ---------------------------------- + * - Re-enable compilation of roaming code by default, but with + * do_roaming = 0 + * - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather + * at the demand of John Carol Langford + * - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff. + * + * Changes made for release in 3.0.15 : + * ---------------------------------- + * - Change e-mail and web page addresses + * - Watchdog timer is now correctly expressed in HZ, not in jiffies + * - Add channel number to the list of frequencies in range + * - Add the (short) list of bit-rates in range + * - Developp a new sensitivity... (sens.value & sens.fixed) + * + * Changes made for release in 3.1.2 : + * --------------------------------- + * - Fix check for root permission (break instead of exit) + * - New nwid & encoding setting (Wireless Extension 9) + * + * Changes made for release in 3.1.12 : + * ---------------------------------- + * - reworked wv_82593_cmd to avoid using the IRQ handler and doing + * ugly things with interrupts. + * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog + * - Update to new network API (softnet - 2.3.43) : + * o replace dev->tbusy (David + me) + * o replace dev->tstart (David + me) + * o remove dev->interrupt (David) + * o add SMP locking via spinlock in splxx (me) + * o add spinlock in interrupt handler (me) + * o use kernel watchdog instead of ours (me) + * o verify that all the changes make sense and work (me) + * - Re-sync kernel/pcmcia versions (not much actually) + * - A few other cleanups (David & me)... + * + * Changes made for release in 3.1.22 : + * ---------------------------------- + * - Check that SMP works, remove annoying log message + * + * Changes made for release in 3.1.24 : + * ---------------------------------- + * - Fix unfrequent card lockup when watchdog was reseting the hardware : + * o control first busy loop in wv_82593_cmd() + * o Extend spinlock protection in wv_hw_config() + * + * Changes made for release in 3.1.33 : + * ---------------------------------- + * - Optional use new driver API for Wireless Extensions : + * o got rid of wavelan_ioctl() + * o use a bunch of iw_handler instead + * + * Changes made for release in 3.2.1 : + * --------------------------------- + * - Set dev->trans_start to avoid filling the logs + * (and generating useless abort commands) + * - Avoid deadlocks in mmc_out()/mmc_in() + * + * Wishes & dreams: + * ---------------- + * - Cleanup and integrate the roaming code + * (std debug, set DomainID, decay avg and co...) + */ + +/***************************** INCLUDES *****************************/ + +/* Linux headers that we need */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* Wireless extensions */ +#include /* New driver API */ + +/* Pcmcia headers that we need */ +#include +#include +#include +#include +#include + +/* Wavelan declarations */ +#include "i82593.h" /* Definitions for the Intel chip */ + +#include "wavelan_cs.h" /* Others bits of the hardware */ + +/************************** DRIVER OPTIONS **************************/ +/* + * `#define' or `#undef' the following constant to change the behaviour + * of the driver... + */ +#define WAVELAN_ROAMING /* Include experimental roaming code */ +#undef WAVELAN_ROAMING_EXT /* Enable roaming wireless extensions */ +#undef SET_PSA_CRC /* Set the CRC in PSA (slower) */ +#define USE_PSA_CONFIG /* Use info from the PSA */ +#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */ +#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */ +#undef SET_MAC_ADDRESS /* Experimental */ + +/* Warning : these stuff will slow down the driver... */ +#define WIRELESS_SPY /* Enable spying addresses */ +#undef HISTOGRAM /* Enable histogram of sig level... */ + +/****************************** DEBUG ******************************/ + +#undef DEBUG_MODULE_TRACE /* Module insertion/removal */ +#undef DEBUG_CALLBACK_TRACE /* Calls made by Linux */ +#undef DEBUG_INTERRUPT_TRACE /* Calls to handler */ +#undef DEBUG_INTERRUPT_INFO /* type of interrupt & so on */ +#define DEBUG_INTERRUPT_ERROR /* problems */ +#undef DEBUG_CONFIG_TRACE /* Trace the config functions */ +#undef DEBUG_CONFIG_INFO /* What's going on... */ +#define DEBUG_CONFIG_ERRORS /* Errors on configuration */ +#undef DEBUG_TX_TRACE /* Transmission calls */ +#undef DEBUG_TX_INFO /* Header of the transmitted packet */ +#undef DEBUG_TX_FAIL /* Normal failure conditions */ +#define DEBUG_TX_ERROR /* Unexpected conditions */ +#undef DEBUG_RX_TRACE /* Transmission calls */ +#undef DEBUG_RX_INFO /* Header of the transmitted packet */ +#undef DEBUG_RX_FAIL /* Normal failure conditions */ +#define DEBUG_RX_ERROR /* Unexpected conditions */ +#undef DEBUG_PACKET_DUMP /* Dump packet on the screen */ +#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */ +#undef DEBUG_IOCTL_INFO /* Various debug info */ +#define DEBUG_IOCTL_ERROR /* What's going wrong */ +#define DEBUG_BASIC_SHOW /* Show basic startup info */ +#undef DEBUG_VERSION_SHOW /* Print version info */ +#undef DEBUG_PSA_SHOW /* Dump psa to screen */ +#undef DEBUG_MMC_SHOW /* Dump mmc to screen */ +#undef DEBUG_SHOW_UNUSED /* Show also unused fields */ +#undef DEBUG_I82593_SHOW /* Show i82593 status */ +#undef DEBUG_DEVICE_SHOW /* Show device parameters */ + +/************************ CONSTANTS & MACROS ************************/ + +#ifdef DEBUG_VERSION_SHOW +static const char *version = "wavelan_cs.c : v24 (SMP + wireless extensions) 11/1/02\n"; +#endif + +/* Watchdog temporisation */ +#define WATCHDOG_JIFFIES (256*HZ/100) + +/* Fix a bug in some old wireless extension definitions */ +#ifndef IW_ESSID_MAX_SIZE +#define IW_ESSID_MAX_SIZE 32 +#endif + +/* ------------------------ PRIVATE IOCTL ------------------------ */ + +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ +#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ +#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ + +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 4 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 5 /* Get histogram values */ + +/*************************** WaveLAN Roaming **************************/ +#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ + +#define WAVELAN_ROAMING_DEBUG 0 /* 1 = Trace of handover decisions */ + /* 2 = Info on each beacon rcvd... */ +#define MAX_WAVEPOINTS 7 /* Max visible at one time */ +#define WAVEPOINT_HISTORY 5 /* SNR sample history slow search */ +#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample history fast search */ +#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */ +#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */ +#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */ +#define CELL_TIMEOUT 2*HZ /* in jiffies */ + +#define FAST_CELL_SEARCH 1 /* Boolean values... */ +#define NWID_PROMISC 1 /* for code clarity. */ + +typedef struct wavepoint_beacon +{ + unsigned char dsap, /* Unused */ + ssap, /* Unused */ + ctrl, /* Unused */ + O,U,I, /* Unused */ + spec_id1, /* Unused */ + spec_id2, /* Unused */ + pdu_type, /* Unused */ + seq; /* WavePoint beacon sequence number */ + __be16 domain_id, /* WavePoint Domain ID */ + nwid; /* WavePoint NWID */ +} wavepoint_beacon; + +typedef struct wavepoint_history +{ + unsigned short nwid; /* WavePoint's NWID */ + int average_slow; /* SNR running average */ + int average_fast; /* SNR running average */ + unsigned char sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */ + unsigned char qualptr; /* Index into ringbuffer */ + unsigned char last_seq; /* Last seq. no seen for WavePoint */ + struct wavepoint_history *next; /* Next WavePoint in table */ + struct wavepoint_history *prev; /* Previous WavePoint in table */ + unsigned long last_seen; /* Time of last beacon recvd, jiffies */ +} wavepoint_history; + +struct wavepoint_table +{ + wavepoint_history *head; /* Start of ringbuffer */ + int num_wavepoints; /* No. of WavePoints visible */ + unsigned char locked; /* Table lock */ +}; + +#endif /* WAVELAN_ROAMING */ + +/****************************** TYPES ******************************/ + +/* Shortcuts */ +typedef struct iw_statistics iw_stats; +typedef struct iw_quality iw_qual; +typedef struct iw_freq iw_freq; +typedef struct net_local net_local; +typedef struct timer_list timer_list; + +/* Basic types */ +typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ + +/* + * Static specific data for the interface. + * + * For each network interface, Linux keep data in two structure. "device" + * keep the generic data (same format for everybody) and "net_local" keep + * the additional specific data. + */ +struct net_local +{ + dev_node_t node; /* ???? What is this stuff ???? */ + struct net_device * dev; /* Reverse link... */ + spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ + struct pcmcia_device * link; /* pcmcia structure */ + int nresets; /* Number of hw resets */ + u_char configured; /* If it is configured */ + u_char reconfig_82593; /* Need to reconfigure the controller */ + u_char promiscuous; /* Promiscuous mode */ + u_char allmulticast; /* All Multicast mode */ + int mc_count; /* Number of multicast addresses */ + + int stop; /* Current i82593 Stop Hit Register */ + int rfp; /* Last DMA machine receive pointer */ + int overrunning; /* Receiver overrun flag */ + + iw_stats wstats; /* Wireless specific stats */ + + struct iw_spy_data spy_data; + struct iw_public_data wireless_data; + +#ifdef HISTOGRAM + int his_number; /* Number of intervals */ + u_char his_range[16]; /* Boundaries of interval ]n-1; n] */ + u_long his_sum[16]; /* Sum in interval */ +#endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + u_long domain_id; /* Domain ID we lock on for roaming */ + int filter_domains; /* Check Domain ID of beacon found */ + struct wavepoint_table wavepoint_table; /* Table of visible WavePoints*/ + wavepoint_history * curr_point; /* Current wavepoint */ + int cell_search; /* Searching for new cell? */ + struct timer_list cell_timer; /* Garbage collection */ +#endif /* WAVELAN_ROAMING */ + void __iomem *mem; +}; + +/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ +static inline u_char /* data */ + hasr_read(u_long); /* Read the host interface : base address */ +static void + hacr_write(u_long, /* Write to host interface : base address */ + u_char), /* data */ + hacr_write_slow(u_long, + u_char); +static void + psa_read(struct net_device *, /* Read the Parameter Storage Area */ + int, /* offset in PSA */ + u_char *, /* buffer to fill */ + int), /* size to read */ + psa_write(struct net_device *, /* Write to the PSA */ + int, /* Offset in psa */ + u_char *, /* Buffer in memory */ + int); /* Length of buffer */ +static void + mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */ + u_short, + u_char), + mmc_write(u_long, /* Write n bytes to the MMC */ + u_char, + u_char *, + int); +static u_char /* Read 1 byte from the MMC */ + mmc_in(u_long, + u_short); +static void + mmc_read(u_long, /* Read n bytes from the MMC */ + u_char, + u_char *, + int), + fee_wait(u_long, /* Wait for frequency EEprom : base address */ + int, /* Base delay to wait for */ + int); /* Number of time to wait */ +static void + fee_read(u_long, /* Read the frequency EEprom : base address */ + u_short, /* destination offset */ + u_short *, /* data buffer */ + int); /* number of registers */ +/* ---------------------- I82593 SUBROUTINES ----------------------- */ +static int + wv_82593_cmd(struct net_device *, /* synchronously send a command to i82593 */ + char *, + int, + int); +static inline int + wv_diag(struct net_device *); /* Diagnostique the i82593 */ +static int + read_ringbuf(struct net_device *, /* Read a receive buffer */ + int, + char *, + int); +static void + wv_82593_reconfig(struct net_device *); /* Reconfigure the controller */ +/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ +static void + wv_init_info(struct net_device *); /* display startup info */ +/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ +static iw_stats * + wavelan_get_wireless_stats(struct net_device *); +/* ----------------------- PACKET RECEPTION ----------------------- */ +static int + wv_start_of_frame(struct net_device *, /* Seek beggining of current frame */ + int, /* end of frame */ + int); /* start of buffer */ +static void + wv_packet_read(struct net_device *, /* Read a packet from a frame */ + int, + int), + wv_packet_rcv(struct net_device *); /* Read all packets waiting */ +/* --------------------- PACKET TRANSMISSION --------------------- */ +static void + wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer */ + void *, + short); +static netdev_tx_t + wavelan_packet_xmit(struct sk_buff *, /* Send a packet */ + struct net_device *); +/* -------------------- HARDWARE CONFIGURATION -------------------- */ +static int + wv_mmc_init(struct net_device *); /* Initialize the modem */ +static int + wv_ru_stop(struct net_device *), /* Stop the i82593 receiver unit */ + wv_ru_start(struct net_device *); /* Start the i82593 receiver unit */ +static int + wv_82593_config(struct net_device *); /* Configure the i82593 */ +static int + wv_pcmcia_reset(struct net_device *); /* Reset the pcmcia interface */ +static int + wv_hw_config(struct net_device *); /* Reset & configure the whole hardware */ +static void + wv_hw_reset(struct net_device *); /* Same, + start receiver unit */ +static int + wv_pcmcia_config(struct pcmcia_device *); /* Configure the pcmcia interface */ +static void + wv_pcmcia_release(struct pcmcia_device *);/* Remove a device */ +/* ---------------------- INTERRUPT HANDLING ---------------------- */ +static irqreturn_t + wavelan_interrupt(int, /* Interrupt handler */ + void *); +static void + wavelan_watchdog(struct net_device *); /* Transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int + wavelan_open(struct net_device *), /* Open the device */ + wavelan_close(struct net_device *); /* Close the device */ +static void + wavelan_detach(struct pcmcia_device *p_dev); /* Destroy a removed device */ + +/**************************** VARIABLES ****************************/ + +/* + * Parameters that can be set with 'insmod' + * The exact syntax is 'insmod wavelan_cs.o =' + */ + +/* Shared memory speed, in ns */ +static int mem_speed = 0; + +/* New module interface */ +module_param(mem_speed, int, 0); + +#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ +/* Enable roaming mode ? No ! Please keep this to 0 */ +static int do_roaming = 0; +module_param(do_roaming, bool, 0); +#endif /* WAVELAN_ROAMING */ + +MODULE_LICENSE("GPL"); + +#endif /* WAVELAN_CS_P_H */ + -- cgit v1.2.3-59-g8ed1b From 4b5783549730da21d88b41dce9a2b738afa44abc Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 28 Oct 2009 16:16:38 -0400 Subject: netwave: move driver to staging Move the netwave driver to drivers/staging. This is another pre-802.11 driver that has seen virtually no non-API-fixup activity in years, and for which no active hardware is likely to still exist. This driver represents unnecessary ongoing maintenance for no clear benefit. This patch brought to you by the "hacking" session at the 2009 Kernel Summit in Tokyo, Japan... Acked-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 13 - drivers/net/wireless/Makefile | 3 - drivers/net/wireless/netwave_cs.c | 1389 ---------------------------------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/netwave/Kconfig | 11 + drivers/staging/netwave/Makefile | 1 + drivers/staging/netwave/netwave_cs.c | 1389 ++++++++++++++++++++++++++++++++++ 8 files changed, 1404 insertions(+), 1405 deletions(-) delete mode 100644 drivers/net/wireless/netwave_cs.c create mode 100644 drivers/staging/netwave/Kconfig create mode 100644 drivers/staging/netwave/Makefile create mode 100644 drivers/staging/netwave/netwave_cs.c (limited to 'drivers/staging') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f94188f05a02..bf0eb4e774b4 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -25,19 +25,6 @@ menuconfig WLAN_PRE80211 This option does not affect the kernel build, it only lets you choose drivers. -config PCMCIA_NETWAVE - tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on PCMCIA && WLAN_PRE80211 - select WIRELESS_EXT - select WEXT_PRIV - help - Say Y here if you intend to attach this type of PCMCIA (PC-card) - wireless Ethernet networking card to your computer. - - To compile this driver as a module, choose M here: the module will be - called netwave_cs. If unsure, say N. - - menuconfig WLAN_80211 bool "Wireless LAN (IEEE 802.11)" depends on NETDEVICES diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index f4a7c8ae27ea..5d4ce4d2b32b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -5,9 +5,6 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ -# Obsolete cards -obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o - obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_AIRO) += airo.o diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c deleted file mode 100644 index 9498b46c99a4..000000000000 --- a/drivers/net/wireless/netwave_cs.c +++ /dev/null @@ -1,1389 +0,0 @@ -/********************************************************************* - * - * Filename: netwave_cs.c - * Version: 0.4.1 - * Description: Netwave AirSurfer Wireless LAN PC Card driver - * Status: Experimental. - * Authors: John Markus Bjørndalen - * Dag Brattli - * David Hinds - * Created at: A long time ago! - * Modified at: Mon Nov 10 11:54:37 1997 - * Modified by: Dag Brattli - * - * Copyright (c) 1997 University of Tromsø, Norway - * - * Revision History: - * - * 08-Nov-97 15:14:47 John Markus Bjørndalen - * - Fixed some bugs in netwave_rx and cleaned it up a bit. - * (One of the bugs would have destroyed packets when receiving - * multiple packets per interrupt). - * - Cleaned up parts of newave_hw_xmit. - * - A few general cleanups. - * 24-Oct-97 13:17:36 Dag Brattli - * - Fixed netwave_rx receive function (got updated docs) - * Others: - * - Changed name from xircnw to netwave, take a look at - * http://www.netwave-wireless.com - * - Some reorganizing of the code - * - Removed possible race condition between interrupt handler and transmit - * function - * - Started to add wireless extensions, but still needs some coding - * - Added watchdog for better handling of transmission timeouts - * (hopefully this works better) - ********************************************************************/ - -/* To have statistics (just packets sent) define this */ -#undef NETWAVE_STATS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define NETWAVE_REGOFF 0x8000 -/* The Netwave IO registers, offsets to iobase */ -#define NETWAVE_REG_COR 0x0 -#define NETWAVE_REG_CCSR 0x2 -#define NETWAVE_REG_ASR 0x4 -#define NETWAVE_REG_IMR 0xa -#define NETWAVE_REG_PMR 0xc -#define NETWAVE_REG_IOLOW 0x6 -#define NETWAVE_REG_IOHI 0x7 -#define NETWAVE_REG_IOCONTROL 0x8 -#define NETWAVE_REG_DATA 0xf -/* The Netwave Extended IO registers, offsets to RamBase */ -#define NETWAVE_EREG_ASCC 0x114 -#define NETWAVE_EREG_RSER 0x120 -#define NETWAVE_EREG_RSERW 0x124 -#define NETWAVE_EREG_TSER 0x130 -#define NETWAVE_EREG_TSERW 0x134 -#define NETWAVE_EREG_CB 0x100 -#define NETWAVE_EREG_SPCQ 0x154 -#define NETWAVE_EREG_SPU 0x155 -#define NETWAVE_EREG_LIF 0x14e -#define NETWAVE_EREG_ISPLQ 0x156 -#define NETWAVE_EREG_HHC 0x158 -#define NETWAVE_EREG_NI 0x16e -#define NETWAVE_EREG_MHS 0x16b -#define NETWAVE_EREG_TDP 0x140 -#define NETWAVE_EREG_RDP 0x150 -#define NETWAVE_EREG_PA 0x160 -#define NETWAVE_EREG_EC 0x180 -#define NETWAVE_EREG_CRBP 0x17a -#define NETWAVE_EREG_ARW 0x166 - -/* - * Commands used in the extended command buffer - * NETWAVE_EREG_CB (0x100-0x10F) - */ -#define NETWAVE_CMD_NOP 0x00 -#define NETWAVE_CMD_SRC 0x01 -#define NETWAVE_CMD_STC 0x02 -#define NETWAVE_CMD_AMA 0x03 -#define NETWAVE_CMD_DMA 0x04 -#define NETWAVE_CMD_SAMA 0x05 -#define NETWAVE_CMD_ER 0x06 -#define NETWAVE_CMD_DR 0x07 -#define NETWAVE_CMD_TL 0x08 -#define NETWAVE_CMD_SRP 0x09 -#define NETWAVE_CMD_SSK 0x0a -#define NETWAVE_CMD_SMD 0x0b -#define NETWAVE_CMD_SAPD 0x0c -#define NETWAVE_CMD_SSS 0x11 -/* End of Command marker */ -#define NETWAVE_CMD_EOC 0x00 - -/* ASR register bits */ -#define NETWAVE_ASR_RXRDY 0x80 -#define NETWAVE_ASR_TXBA 0x01 - -#define TX_TIMEOUT ((32*HZ)/100) - -static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */ -static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */ - -static const unsigned int corConfIENA = 0x01; /* Interrupt enable */ -static const unsigned int corConfLVLREQ = 0x40; /* Keep high */ - -static const unsigned int rxConfRxEna = 0x80; /* Receive Enable */ -static const unsigned int rxConfMAC = 0x20; /* MAC host receive mode*/ -static const unsigned int rxConfPro = 0x10; /* Promiscuous */ -static const unsigned int rxConfAMP = 0x08; /* Accept Multicast Packets */ -static const unsigned int rxConfBcast = 0x04; /* Accept Broadcast Packets */ - -static const unsigned int txConfTxEna = 0x80; /* Transmit Enable */ -static const unsigned int txConfMAC = 0x20; /* Host sends MAC mode */ -static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */ -static const unsigned int txConfKey = 0x02; /* Scramble data packets */ -static const unsigned int txConfLoop = 0x01; /* Loopback mode */ - -/* - All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If - you do not define PCMCIA_DEBUG at all, all the debug code will be - left out. If you compile with PCMCIA_DEBUG=0, the debug code will - be present but disabled -- but it can then be enabled for specific - modules at load time with a 'pc_debug=#' option to insmod. -*/ - -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -module_param(pc_debug, int, 0); -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) -static char *version = -"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n"; -#else -#define DEBUG(n, args...) -#endif - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Choose the domain, default is 0x100 */ -static u_int domain = 0x100; - -/* Scramble key, range from 0x0 to 0xffff. - * 0x0 is no scrambling. - */ -static u_int scramble_key = 0x0; - -/* Shared memory speed, in ns. The documentation states that - * the card should not be read faster than every 400ns. - * This timing should be provided by the HBA. If it becomes a - * problem, try setting mem_speed to 400. - */ -static int mem_speed; - -module_param(domain, int, 0); -module_param(scramble_key, int, 0); -module_param(mem_speed, int, 0); - -/*====================================================================*/ - -/* PCMCIA (Card Services) related functions */ -static void netwave_release(struct pcmcia_device *link); /* Card removal */ -static int netwave_pcmcia_config(struct pcmcia_device *arg); /* Runs after card - insertion */ -static void netwave_detach(struct pcmcia_device *p_dev); /* Destroy instance */ - -/* Hardware configuration */ -static void netwave_doreset(unsigned int iobase, u_char __iomem *ramBase); -static void netwave_reset(struct net_device *dev); - -/* Misc device stuff */ -static int netwave_open(struct net_device *dev); /* Open the device */ -static int netwave_close(struct net_device *dev); /* Close the device */ - -/* Packet transmission and Packet reception */ -static netdev_tx_t netwave_start_xmit( struct sk_buff *skb, - struct net_device *dev); -static int netwave_rx( struct net_device *dev); - -/* Interrupt routines */ -static irqreturn_t netwave_interrupt(int irq, void *dev_id); -static void netwave_watchdog(struct net_device *); - -/* Wireless extensions */ -static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev); - -static void set_multicast_list(struct net_device *dev); - -/* - A struct pcmcia_device structure has fields for most things that are needed - to keep track of a socket, but there will usually be some device - specific information that also needs to be kept track of. The - 'priv' pointer in a struct pcmcia_device structure can be used to point to - a device-specific private data structure, like this. - - A driver needs to provide a dev_node_t structure for each device - on a card. In some cases, there is only one device per card (for - example, ethernet cards, modems). In other cases, there may be - many actual or logical devices (SCSI adapters, memory cards with - multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a struct pcmcia_device - structure. We allocate them in the card's private data structure, - because they generally can't be allocated dynamically. -*/ - -static const struct iw_handler_def netwave_handler_def; - -#define SIOCGIPSNAP SIOCIWFIRSTPRIV + 1 /* Site Survey Snapshot */ - -#define MAX_ESA 10 - -typedef struct net_addr { - u_char addr48[6]; -} net_addr; - -struct site_survey { - u_short length; - u_char struct_revision; - u_char roaming_state; - - u_char sp_existsFlag; - u_char sp_link_quality; - u_char sp_max_link_quality; - u_char linkQualityGoodFairBoundary; - u_char linkQualityFairPoorBoundary; - u_char sp_utilization; - u_char sp_goodness; - u_char sp_hotheadcount; - u_char roaming_condition; - - net_addr sp; - u_char numAPs; - net_addr nearByAccessPoints[MAX_ESA]; -}; - -typedef struct netwave_private { - struct pcmcia_device *p_dev; - spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - dev_node_t node; - u_char __iomem *ramBase; - int timeoutCounter; - int lastExec; - struct timer_list watchdog; /* To avoid blocking state */ - struct site_survey nss; - struct iw_statistics iw_stats; /* Wireless stats */ -} netwave_private; - -/* - * The Netwave card is little-endian, so won't work for big endian - * systems. - */ -static inline unsigned short get_uint16(u_char __iomem *staddr) -{ - return readw(staddr); /* Return only 16 bits */ -} - -static inline short get_int16(u_char __iomem * staddr) -{ - return readw(staddr); -} - -/* - * Wait until the WOC (Write Operation Complete) bit in the - * ASR (Adapter Status Register) is asserted. - * This should have aborted if it takes too long time. - */ -static inline void wait_WOC(unsigned int iobase) -{ - /* Spin lock */ - while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; -} - -static void netwave_snapshot(netwave_private *priv, u_char __iomem *ramBase, - unsigned int iobase) { - u_short resultBuffer; - - /* if time since last snapshot is > 1 sec. (100 jiffies?) then take - * new snapshot, else return cached data. This is the recommended rate. - */ - if ( jiffies - priv->lastExec > 100) { - /* Take site survey snapshot */ - /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies - - priv->lastExec); */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); - wait_WOC(iobase); - - /* Get result and copy to cach */ - resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); - copy_from_pc( &priv->nss, ramBase+resultBuffer, - sizeof(struct site_survey)); - } -} - -/* - * Function netwave_get_wireless_stats (dev) - * - * Wireless extensions statistics - * - */ -static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev) -{ - unsigned long flags; - unsigned int iobase = dev->base_addr; - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - struct iw_statistics* wstats; - - wstats = &priv->iw_stats; - - spin_lock_irqsave(&priv->spinlock, flags); - - netwave_snapshot( priv, ramBase, iobase); - - wstats->status = priv->nss.roaming_state; - wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); - wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ); - wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f; - wstats->discard.nwid = 0L; - wstats->discard.code = 0L; - wstats->discard.misc = 0L; - - spin_unlock_irqrestore(&priv->spinlock, flags); - - return &priv->iw_stats; -} - -static const struct net_device_ops netwave_netdev_ops = { - .ndo_open = netwave_open, - .ndo_stop = netwave_close, - .ndo_start_xmit = netwave_start_xmit, - .ndo_set_multicast_list = set_multicast_list, - .ndo_tx_timeout = netwave_watchdog, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -/* - * Function netwave_attach (void) - * - * Creates an "instance" of the driver, allocating local data - * structures for one device. The device is registered with Card - * Services. - * - * The dev_link structure is initialized, but we don't actually - * configure the card at this point -- we wait until we receive a - * card insertion event. - */ -static int netwave_probe(struct pcmcia_device *link) -{ - struct net_device *dev; - netwave_private *priv; - - DEBUG(0, "netwave_attach()\n"); - - /* Initialize the struct pcmcia_device structure */ - dev = alloc_etherdev(sizeof(netwave_private)); - if (!dev) - return -ENOMEM; - priv = netdev_priv(dev); - priv->p_dev = link; - link->priv = dev; - - /* The io structure describes IO port mapping */ - link->io.NumPorts1 = 16; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; - /* link->io.NumPorts2 = 16; - link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */ - link->io.IOAddrLines = 5; - - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = &netwave_interrupt; - - /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - - /* Netwave private struct init. link/dev/node already taken care of, - * other stuff zero'd - Jean II */ - spin_lock_init(&priv->spinlock); - - /* Netwave specific entries in the device structure */ - dev->netdev_ops = &netwave_netdev_ops; - /* wireless extensions */ - dev->wireless_handlers = &netwave_handler_def; - - dev->watchdog_timeo = TX_TIMEOUT; - - link->irq.Instance = dev; - - return netwave_pcmcia_config( link); -} /* netwave_attach */ - -/* - * Function netwave_detach (link) - * - * This deletes a driver "instance". The device is de-registered - * with Card Services. If it has been released, all local data - * structures are freed. Otherwise, the structures will be freed - * when the device is released. - */ -static void netwave_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - DEBUG(0, "netwave_detach(0x%p)\n", link); - - netwave_release(link); - - if (link->dev_node) - unregister_netdev(dev); - - free_netdev(dev); -} /* netwave_detach */ - -/* - * Wireless Handler : get protocol name - */ -static int netwave_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - strcpy(wrqu->name, "Netwave"); - return 0; -} - -/* - * Wireless Handler : set Network ID - */ -static int netwave_set_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long flags; - unsigned int iobase = dev->base_addr; - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&priv->spinlock, flags); - - if(!wrqu->nwid.disabled) { - domain = wrqu->nwid.value; - printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", - (domain >> 8) & 0x01, domain & 0xff); - wait_WOC(iobase); - writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); - writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - } - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&priv->spinlock, flags); - - return 0; -} - -/* - * Wireless Handler : get Network ID - */ -static int netwave_get_nwid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - wrqu->nwid.value = domain; - wrqu->nwid.disabled = 0; - wrqu->nwid.fixed = 1; - return 0; -} - -/* - * Wireless Handler : set scramble key - */ -static int netwave_set_scramble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *key) -{ - unsigned long flags; - unsigned int iobase = dev->base_addr; - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&priv->spinlock, flags); - - scramble_key = (key[0] << 8) | key[1]; - wait_WOC(iobase); - writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); - writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&priv->spinlock, flags); - - return 0; -} - -/* - * Wireless Handler : get scramble key - */ -static int netwave_get_scramble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *key) -{ - key[1] = scramble_key & 0xff; - key[0] = (scramble_key>>8) & 0xff; - wrqu->encoding.flags = IW_ENCODE_ENABLED; - wrqu->encoding.length = 2; - return 0; -} - -/* - * Wireless Handler : get mode - */ -static int netwave_get_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - if(domain & 0x100) - wrqu->mode = IW_MODE_INFRA; - else - wrqu->mode = IW_MODE_ADHOC; - - return 0; -} - -/* - * Wireless Handler : get range info - */ -static int netwave_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct iw_range *range = (struct iw_range *) extra; - int ret = 0; - - /* Set the length (very important for backward compatibility) */ - wrqu->data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(range, 0, sizeof(struct iw_range)); - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 9; /* Nothing for us in v10 and v11 */ - - /* Set information in the range struct */ - range->throughput = 450 * 1000; /* don't argue on this ! */ - range->min_nwid = 0x0000; - range->max_nwid = 0x01FF; - - range->num_channels = range->num_frequency = 0; - - range->sensitivity = 0x3F; - range->max_qual.qual = 255; - range->max_qual.level = 255; - range->max_qual.noise = 0; - - range->num_bitrates = 1; - range->bitrate[0] = 1000000; /* 1 Mb/s */ - - range->encoding_size[0] = 2; /* 16 bits scrambling */ - range->num_encoding_sizes = 1; - range->max_encoding_tokens = 1; /* Only one key possible */ - - return ret; -} - -/* - * Wireless Private Handler : get snapshot - */ -static int netwave_get_snap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - unsigned long flags; - unsigned int iobase = dev->base_addr; - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&priv->spinlock, flags); - - /* Take snapshot of environment */ - netwave_snapshot( priv, ramBase, iobase); - wrqu->data.length = priv->nss.length; - memcpy(extra, (u_char *) &priv->nss, sizeof( struct site_survey)); - - priv->lastExec = jiffies; - - /* ReEnable interrupts & restore flags */ - spin_unlock_irqrestore(&priv->spinlock, flags); - - return(0); -} - -/* - * Structures to export the Wireless Handlers - * This is the stuff that are treated the wireless extensions (iwconfig) - */ - -static const struct iw_priv_args netwave_private_args[] = { -/*{ cmd, set_args, get_args, name } */ - { SIOCGIPSNAP, 0, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof(struct site_survey), - "getsitesurvey" }, -}; - -static const iw_handler netwave_handler[] = -{ - NULL, /* SIOCSIWNAME */ - netwave_get_name, /* SIOCGIWNAME */ - netwave_set_nwid, /* SIOCSIWNWID */ - netwave_get_nwid, /* SIOCGIWNWID */ - NULL, /* SIOCSIWFREQ */ - NULL, /* SIOCGIWFREQ */ - NULL, /* SIOCSIWMODE */ - netwave_get_mode, /* SIOCGIWMODE */ - NULL, /* SIOCSIWSENS */ - NULL, /* SIOCGIWSENS */ - NULL, /* SIOCSIWRANGE */ - netwave_get_range, /* SIOCGIWRANGE */ - NULL, /* SIOCSIWPRIV */ - NULL, /* SIOCGIWPRIV */ - NULL, /* SIOCSIWSTATS */ - NULL, /* SIOCGIWSTATS */ - NULL, /* SIOCSIWSPY */ - NULL, /* SIOCGIWSPY */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWAP */ - NULL, /* SIOCGIWAP */ - NULL, /* -- hole -- */ - NULL, /* SIOCGIWAPLIST */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWESSID */ - NULL, /* SIOCGIWESSID */ - NULL, /* SIOCSIWNICKN */ - NULL, /* SIOCGIWNICKN */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - NULL, /* SIOCSIWRATE */ - NULL, /* SIOCGIWRATE */ - NULL, /* SIOCSIWRTS */ - NULL, /* SIOCGIWRTS */ - NULL, /* SIOCSIWFRAG */ - NULL, /* SIOCGIWFRAG */ - NULL, /* SIOCSIWTXPOW */ - NULL, /* SIOCGIWTXPOW */ - NULL, /* SIOCSIWRETRY */ - NULL, /* SIOCGIWRETRY */ - netwave_set_scramble, /* SIOCSIWENCODE */ - netwave_get_scramble, /* SIOCGIWENCODE */ -}; - -static const iw_handler netwave_private_handler[] = -{ - NULL, /* SIOCIWFIRSTPRIV */ - netwave_get_snap, /* SIOCIWFIRSTPRIV + 1 */ -}; - -static const struct iw_handler_def netwave_handler_def = -{ - .num_standard = ARRAY_SIZE(netwave_handler), - .num_private = ARRAY_SIZE(netwave_private_handler), - .num_private_args = ARRAY_SIZE(netwave_private_args), - .standard = (iw_handler *) netwave_handler, - .private = (iw_handler *) netwave_private_handler, - .private_args = (struct iw_priv_args *) netwave_private_args, - .get_wireless_stats = netwave_get_wireless_stats, -}; - -/* - * Function netwave_pcmcia_config (link) - * - * netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION - * event is received, to configure the PCMCIA socket, and to make the - * device available to the system. - * - */ - -#define CS_CHECK(fn, ret) \ -do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) - -static int netwave_pcmcia_config(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - netwave_private *priv = netdev_priv(dev); - int i, j, last_ret, last_fn; - win_req_t req; - memreq_t mem; - u_char __iomem *ramBase = NULL; - - DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link); - - /* - * Try allocating IO ports. This tries a few fixed addresses. - * If you want, you can also read the card's config table to - * pick addresses -- see the serial driver for an example. - */ - for (i = j = 0x0; j < 0x400; j += 0x20) { - link->io.BasePort1 = j ^ 0x300; - i = pcmcia_request_io(link, &link->io); - if (i == 0) - break; - } - if (i != 0) { - cs_error(link, RequestIO, i); - goto failed; - } - - /* - * Now allocate an interrupt line. Note that this does not - * actually assign a handler to the interrupt. - */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); - - /* - * This actually configures the PCMCIA socket -- setting up - * the I/O windows and the interrupt mapping. - */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); - - /* - * Allocate a 32K memory window. Note that the struct pcmcia_device - * structure provides space for one window handle -- if your - * device needs several windows, you'll need to keep track of - * the handles in your private data structure, dev->priv. - */ - DEBUG(1, "Setting mem speed of %d\n", mem_speed); - - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Base = 0; req.Size = 0x8000; - req.AccessSpeed = mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); - mem.CardOffset = 0x20000; mem.Page = 0; - CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem)); - - /* Store base address of the common window frame */ - ramBase = ioremap(req.Base, 0x8000); - priv->ramBase = ramBase; - - dev->irq = link->irq.AssignedIRQ; - dev->base_addr = link->io.BasePort1; - SET_NETDEV_DEV(dev, &handle_to_dev(link)); - - if (register_netdev(dev) != 0) { - printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); - goto failed; - } - - strcpy(priv->node.dev_name, dev->name); - link->dev_node = &priv->node; - - /* Reset card before reading physical address */ - netwave_doreset(dev->base_addr, ramBase); - - /* Read the ethernet address and fill in the Netwave registers. */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i); - - printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx, " - "id %c%c, hw_addr %pM\n", - dev->name, dev->base_addr, dev->irq, - (u_long) ramBase, - (int) readb(ramBase+NETWAVE_EREG_NI), - (int) readb(ramBase+NETWAVE_EREG_NI+1), - dev->dev_addr); - - /* get revision words */ - printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", - get_uint16(ramBase + NETWAVE_EREG_ARW), - get_uint16(ramBase + NETWAVE_EREG_ARW+2)); - return 0; - -cs_failed: - cs_error(link, last_fn, last_ret); -failed: - netwave_release(link); - return -ENODEV; -} /* netwave_pcmcia_config */ - -/* - * Function netwave_release (arg) - * - * After a card is removed, netwave_release() will unregister the net - * device, and release the PCMCIA configuration. If the device is - * still open, this will be postponed until it is closed. - */ -static void netwave_release(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - netwave_private *priv = netdev_priv(dev); - - DEBUG(0, "netwave_release(0x%p)\n", link); - - pcmcia_disable_device(link); - if (link->win) - iounmap(priv->ramBase); -} - -static int netwave_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int netwave_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - netwave_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -/* - * Function netwave_doreset (ioBase, ramBase) - * - * Proper hardware reset of the card. - */ -static void netwave_doreset(unsigned int ioBase, u_char __iomem *ramBase) -{ - /* Reset card */ - wait_WOC(ioBase); - outb(0x80, ioBase + NETWAVE_REG_PMR); - writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */ - outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */ -} - -/* - * Function netwave_reset (dev) - * - * Reset and restore all of the netwave registers - */ -static void netwave_reset(struct net_device *dev) { - /* u_char state; */ - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - unsigned int iobase = dev->base_addr; - - DEBUG(0, "netwave_reset: Done with hardware reset\n"); - - priv->timeoutCounter = 0; - - /* Reset card */ - netwave_doreset(iobase, ramBase); - printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n"); - - /* Write a NOP to check the card */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); - - /* Set receive conf */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); - writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); - - /* Set transmit conf */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0); - writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); - - /* Now set the MU Domain */ - printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff); - wait_WOC(iobase); - writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); - writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - - /* Set scramble key */ - printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key); - wait_WOC(iobase); - writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); - writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - - /* Enable interrupts, bit 4 high to keep unused - * source from interrupting us, bit 2 high to - * set interrupt enable, 567 to enable TxDN, - * RxErr and RxRdy - */ - wait_WOC(iobase); - outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR); - - /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36 - * waitWOC - * skriv 80 til d000:3688 - * sjekk om det ble 80 - */ - - /* Enable Receiver */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); - - /* Set the IENA bit in COR */ - wait_WOC(iobase); - outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR); -} - -/* - * Function netwave_hw_xmit (data, len, dev) - */ -static int netwave_hw_xmit(unsigned char* data, int len, - struct net_device* dev) { - unsigned long flags; - unsigned int TxFreeList, - curBuff, - MaxData, - DataOffset; - int tmpcount; - - netwave_private *priv = netdev_priv(dev); - u_char __iomem * ramBase = priv->ramBase; - unsigned int iobase = dev->base_addr; - - /* Disable interrupts & save flags */ - spin_lock_irqsave(&priv->spinlock, flags); - - /* Check if there are transmit buffers available */ - wait_WOC(iobase); - if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) { - /* No buffers available */ - printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n", - dev->name); - spin_unlock_irqrestore(&priv->spinlock, flags); - return 1; - } - - dev->stats.tx_bytes += len; - - DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n", - readb(ramBase + NETWAVE_EREG_SPCQ), - readb(ramBase + NETWAVE_EREG_SPU), - readb(ramBase + NETWAVE_EREG_LIF), - readb(ramBase + NETWAVE_EREG_ISPLQ)); - - /* Now try to insert it into the adapters free memory */ - wait_WOC(iobase); - TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP); - MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2); - DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4); - - DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n", - TxFreeList, MaxData, DataOffset); - - /* Copy packet to the adapter fragment buffers */ - curBuff = TxFreeList; - tmpcount = 0; - while (tmpcount < len) { - int tmplen = len - tmpcount; - copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, - (tmplen < MaxData) ? tmplen : MaxData); - tmpcount += MaxData; - - /* Advance to next buffer */ - curBuff = get_uint16(ramBase + curBuff); - } - - /* Now issue transmit list */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0); - writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1); - writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); - - spin_unlock_irqrestore(&priv->spinlock, flags); - return 0; -} - -static netdev_tx_t netwave_start_xmit(struct sk_buff *skb, - struct net_device *dev) { - /* This flag indicate that the hardware can't perform a transmission. - * Theoritically, NET3 check it before sending a packet to the driver, - * but in fact it never do that and pool continuously. - * As the watchdog will abort too long transmissions, we are quite safe... - */ - - netif_stop_queue(dev); - - { - short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned char* buf = skb->data; - - if (netwave_hw_xmit( buf, length, dev) == 1) { - /* Some error, let's make them call us another time? */ - netif_start_queue(dev); - } - dev->trans_start = jiffies; - } - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} /* netwave_start_xmit */ - -/* - * Function netwave_interrupt (irq, dev_id) - * - * This function is the interrupt handler for the Netwave card. This - * routine will be called whenever: - * 1. A packet is received. - * 2. A packet has successfully been transferred and the unit is - * ready to transmit another packet. - * 3. A command has completed execution. - */ -static irqreturn_t netwave_interrupt(int irq, void* dev_id) -{ - unsigned int iobase; - u_char __iomem *ramBase; - struct net_device *dev = (struct net_device *)dev_id; - struct netwave_private *priv = netdev_priv(dev); - struct pcmcia_device *link = priv->p_dev; - int i; - - if (!netif_device_present(dev)) - return IRQ_NONE; - - iobase = dev->base_addr; - ramBase = priv->ramBase; - - /* Now find what caused the interrupt, check while interrupts ready */ - for (i = 0; i < 10; i++) { - u_char status; - - wait_WOC(iobase); - if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02)) - break; /* None of the interrupt sources asserted (normal exit) */ - - status = inb(iobase + NETWAVE_REG_ASR); - - if (!pcmcia_dev_present(link)) { - DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x " - "from removed or suspended card!\n", status); - break; - } - - /* RxRdy */ - if (status & 0x80) { - netwave_rx(dev); - /* wait_WOC(iobase); */ - /* RxRdy cannot be reset directly by the host */ - } - /* RxErr */ - if (status & 0x40) { - u_char rser; - - rser = readb(ramBase + NETWAVE_EREG_RSER); - - if (rser & 0x04) { - ++dev->stats.rx_dropped; - ++dev->stats.rx_crc_errors; - } - if (rser & 0x02) - ++dev->stats.rx_frame_errors; - - /* Clear the RxErr bit in RSER. RSER+4 is the - * write part. Also clear the RxCRC (0x04) and - * RxBig (0x02) bits if present */ - wait_WOC(iobase); - writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4); - - /* Write bit 6 high to ASCC to clear RxErr in ASR, - * WOC must be set first! - */ - wait_WOC(iobase); - writeb(0x40, ramBase + NETWAVE_EREG_ASCC); - - /* Remember to count up dev->stats on error packets */ - ++dev->stats.rx_errors; - } - /* TxDN */ - if (status & 0x20) { - int txStatus; - - txStatus = readb(ramBase + NETWAVE_EREG_TSER); - DEBUG(3, "Transmit done. TSER = %x id %x\n", - txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1)); - - if (txStatus & 0x20) { - /* Transmitting was okay, clear bits */ - wait_WOC(iobase); - writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4); - ++dev->stats.tx_packets; - } - - if (txStatus & 0xd0) { - if (txStatus & 0x80) { - ++dev->stats.collisions; /* Because of /proc/net/dev*/ - /* ++dev->stats.tx_aborted_errors; */ - /* printk("Collision. %ld\n", jiffies - dev->trans_start); */ - } - if (txStatus & 0x40) - ++dev->stats.tx_carrier_errors; - /* 0x80 TxGU Transmit giveup - nine times and no luck - * 0x40 TxNOAP No access point. Discarded packet. - * 0x10 TxErr Transmit error. Always set when - * TxGU and TxNOAP is set. (Those are the only ones - * to set TxErr). - */ - DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", - txStatus); - - /* Clear out TxGU, TxNOAP, TxErr and TxTrys */ - wait_WOC(iobase); - writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4); - ++dev->stats.tx_errors; - } - DEBUG(3, "New status is TSER %x ASR %x\n", - readb(ramBase + NETWAVE_EREG_TSER), - inb(iobase + NETWAVE_REG_ASR)); - - netif_wake_queue(dev); - } - /* TxBA, this would trigger on all error packets received */ - /* if (status & 0x01) { - DEBUG(4, "Transmit buffers available, %x\n", status); - } - */ - } - /* Handled if we looped at least one time - Jean II */ - return IRQ_RETVAL(i); -} /* netwave_interrupt */ - -/* - * Function netwave_watchdog (a) - * - * Watchdog : when we start a transmission, we set a timer in the - * kernel. If the transmission complete, this timer is disabled. If - * it expire, we reset the card. - * - */ -static void netwave_watchdog(struct net_device *dev) { - - DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name); - netwave_reset(dev); - dev->trans_start = jiffies; - netif_wake_queue(dev); -} /* netwave_watchdog */ - -static int netwave_rx(struct net_device *dev) -{ - netwave_private *priv = netdev_priv(dev); - u_char __iomem *ramBase = priv->ramBase; - unsigned int iobase = dev->base_addr; - u_char rxStatus; - struct sk_buff *skb = NULL; - unsigned int curBuffer, - rcvList; - int rcvLen; - int tmpcount = 0; - int dataCount, dataOffset; - int i; - u_char *ptr; - - DEBUG(3, "xinw_rx: Receiving ... \n"); - - /* Receive max 10 packets for now. */ - for (i = 0; i < 10; i++) { - /* Any packets? */ - wait_WOC(iobase); - rxStatus = readb(ramBase + NETWAVE_EREG_RSER); - if ( !( rxStatus & 0x80)) /* No more packets */ - break; - - /* Check if multicast/broadcast or other */ - /* multicast = (rxStatus & 0x20); */ - - /* The receive list pointer and length of the packet */ - wait_WOC(iobase); - rcvLen = get_int16( ramBase + NETWAVE_EREG_RDP); - rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2); - - if (rcvLen < 0) { - printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", - rcvLen); - return 0; - } - - skb = dev_alloc_skb(rcvLen+5); - if (skb == NULL) { - DEBUG(1, "netwave_rx: Could not allocate an sk_buff of " - "length %d\n", rcvLen); - ++dev->stats.rx_dropped; - /* Tell the adapter to skip the packet */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); - return 0; - } - - skb_reserve( skb, 2); /* Align IP on 16 byte */ - skb_put( skb, rcvLen); - - /* Copy packet fragments to the skb data area */ - ptr = (u_char*) skb->data; - curBuffer = rcvList; - tmpcount = 0; - while ( tmpcount < rcvLen) { - /* Get length and offset of current buffer */ - dataCount = get_uint16( ramBase+curBuffer+2); - dataOffset = get_uint16( ramBase+curBuffer+4); - - copy_from_pc( ptr + tmpcount, - ramBase+curBuffer+dataOffset, dataCount); - - tmpcount += dataCount; - - /* Point to next buffer */ - curBuffer = get_uint16(ramBase + curBuffer); - } - - skb->protocol = eth_type_trans(skb,dev); - /* Queue packet for network layer */ - netif_rx(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += rcvLen; - - /* Got the packet, tell the adapter to skip it */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); - DEBUG(3, "Packet reception ok\n"); - } - return 0; -} - -static int netwave_open(struct net_device *dev) { - netwave_private *priv = netdev_priv(dev); - struct pcmcia_device *link = priv->p_dev; - - DEBUG(1, "netwave_open: starting.\n"); - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - - netif_start_queue(dev); - netwave_reset(dev); - - return 0; -} - -static int netwave_close(struct net_device *dev) { - netwave_private *priv = netdev_priv(dev); - struct pcmcia_device *link = priv->p_dev; - - DEBUG(1, "netwave_close: finishing.\n"); - - link->open--; - netif_stop_queue(dev); - - return 0; -} - -static struct pcmcia_device_id netwave_ids[] = { - PCMCIA_DEVICE_PROD_ID12("Xircom", "CreditCard Netwave", 0x2e3ee845, 0x54e28a28), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, netwave_ids); - -static struct pcmcia_driver netwave_driver = { - .owner = THIS_MODULE, - .drv = { - .name = "netwave_cs", - }, - .probe = netwave_probe, - .remove = netwave_detach, - .id_table = netwave_ids, - .suspend = netwave_suspend, - .resume = netwave_resume, -}; - -static int __init init_netwave_cs(void) -{ - return pcmcia_register_driver(&netwave_driver); -} - -static void __exit exit_netwave_cs(void) -{ - pcmcia_unregister_driver(&netwave_driver); -} - -module_init(init_netwave_cs); -module_exit(exit_netwave_cs); - -/* Set or clear the multicast filter for this adaptor. - num_addrs == -1 Promiscuous mode, receive all packets - num_addrs == 0 Normal mode, clear multicast list - num_addrs > 0 Multicast mode, receive normal and MC packets, and do - best-effort filtering. - */ -static void set_multicast_list(struct net_device *dev) -{ - unsigned int iobase = dev->base_addr; - netwave_private *priv = netdev_priv(dev); - u_char __iomem * ramBase = priv->ramBase; - u_char rcvMode = 0; - -#ifdef PCMCIA_DEBUG - if (pc_debug > 2) { - static int old; - if (old != dev->mc_count) { - old = dev->mc_count; - DEBUG(0, "%s: setting Rx mode to %d addresses.\n", - dev->name, dev->mc_count); - } - } -#endif - - if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) { - /* Multicast Mode */ - rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast; - } else if (dev->flags & IFF_PROMISC) { - /* Promiscous mode */ - rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast; - } else { - /* Normal mode */ - rcvMode = rxConfRxEna + rxConfBcast; - } - - /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/ - /* Now set receive mode */ - wait_WOC(iobase); - writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); - writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1); - writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); -} -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index c1082f20534c..4e3873bfd010 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -137,5 +137,7 @@ source "drivers/staging/arlan/Kconfig" source "drivers/staging/wavelan/Kconfig" +source "drivers/staging/netwave/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 7bccdd3e7fab..fb1d7851b563 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_STRIP) += strip/ obj-$(CONFIG_ARLAN) += arlan/ obj-$(CONFIG_WAVELAN) += wavelan/ obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan/ +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave/ diff --git a/drivers/staging/netwave/Kconfig b/drivers/staging/netwave/Kconfig new file mode 100644 index 000000000000..c0c996c0550a --- /dev/null +++ b/drivers/staging/netwave/Kconfig @@ -0,0 +1,11 @@ +config PCMCIA_NETWAVE + tristate "Xircom Netwave AirSurfer Pcmcia wireless support" + depends on PCMCIA + select WIRELESS_EXT + select WEXT_PRIV + help + Say Y here if you intend to attach this type of PCMCIA (PC-card) + wireless Ethernet networking card to your computer. + + To compile this driver as a module, choose M here: the module will be + called netwave_cs. If unsure, say N. diff --git a/drivers/staging/netwave/Makefile b/drivers/staging/netwave/Makefile new file mode 100644 index 000000000000..2ab89de59b9b --- /dev/null +++ b/drivers/staging/netwave/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o diff --git a/drivers/staging/netwave/netwave_cs.c b/drivers/staging/netwave/netwave_cs.c new file mode 100644 index 000000000000..9498b46c99a4 --- /dev/null +++ b/drivers/staging/netwave/netwave_cs.c @@ -0,0 +1,1389 @@ +/********************************************************************* + * + * Filename: netwave_cs.c + * Version: 0.4.1 + * Description: Netwave AirSurfer Wireless LAN PC Card driver + * Status: Experimental. + * Authors: John Markus Bjørndalen + * Dag Brattli + * David Hinds + * Created at: A long time ago! + * Modified at: Mon Nov 10 11:54:37 1997 + * Modified by: Dag Brattli + * + * Copyright (c) 1997 University of Tromsø, Norway + * + * Revision History: + * + * 08-Nov-97 15:14:47 John Markus Bjørndalen + * - Fixed some bugs in netwave_rx and cleaned it up a bit. + * (One of the bugs would have destroyed packets when receiving + * multiple packets per interrupt). + * - Cleaned up parts of newave_hw_xmit. + * - A few general cleanups. + * 24-Oct-97 13:17:36 Dag Brattli + * - Fixed netwave_rx receive function (got updated docs) + * Others: + * - Changed name from xircnw to netwave, take a look at + * http://www.netwave-wireless.com + * - Some reorganizing of the code + * - Removed possible race condition between interrupt handler and transmit + * function + * - Started to add wireless extensions, but still needs some coding + * - Added watchdog for better handling of transmission timeouts + * (hopefully this works better) + ********************************************************************/ + +/* To have statistics (just packets sent) define this */ +#undef NETWAVE_STATS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NETWAVE_REGOFF 0x8000 +/* The Netwave IO registers, offsets to iobase */ +#define NETWAVE_REG_COR 0x0 +#define NETWAVE_REG_CCSR 0x2 +#define NETWAVE_REG_ASR 0x4 +#define NETWAVE_REG_IMR 0xa +#define NETWAVE_REG_PMR 0xc +#define NETWAVE_REG_IOLOW 0x6 +#define NETWAVE_REG_IOHI 0x7 +#define NETWAVE_REG_IOCONTROL 0x8 +#define NETWAVE_REG_DATA 0xf +/* The Netwave Extended IO registers, offsets to RamBase */ +#define NETWAVE_EREG_ASCC 0x114 +#define NETWAVE_EREG_RSER 0x120 +#define NETWAVE_EREG_RSERW 0x124 +#define NETWAVE_EREG_TSER 0x130 +#define NETWAVE_EREG_TSERW 0x134 +#define NETWAVE_EREG_CB 0x100 +#define NETWAVE_EREG_SPCQ 0x154 +#define NETWAVE_EREG_SPU 0x155 +#define NETWAVE_EREG_LIF 0x14e +#define NETWAVE_EREG_ISPLQ 0x156 +#define NETWAVE_EREG_HHC 0x158 +#define NETWAVE_EREG_NI 0x16e +#define NETWAVE_EREG_MHS 0x16b +#define NETWAVE_EREG_TDP 0x140 +#define NETWAVE_EREG_RDP 0x150 +#define NETWAVE_EREG_PA 0x160 +#define NETWAVE_EREG_EC 0x180 +#define NETWAVE_EREG_CRBP 0x17a +#define NETWAVE_EREG_ARW 0x166 + +/* + * Commands used in the extended command buffer + * NETWAVE_EREG_CB (0x100-0x10F) + */ +#define NETWAVE_CMD_NOP 0x00 +#define NETWAVE_CMD_SRC 0x01 +#define NETWAVE_CMD_STC 0x02 +#define NETWAVE_CMD_AMA 0x03 +#define NETWAVE_CMD_DMA 0x04 +#define NETWAVE_CMD_SAMA 0x05 +#define NETWAVE_CMD_ER 0x06 +#define NETWAVE_CMD_DR 0x07 +#define NETWAVE_CMD_TL 0x08 +#define NETWAVE_CMD_SRP 0x09 +#define NETWAVE_CMD_SSK 0x0a +#define NETWAVE_CMD_SMD 0x0b +#define NETWAVE_CMD_SAPD 0x0c +#define NETWAVE_CMD_SSS 0x11 +/* End of Command marker */ +#define NETWAVE_CMD_EOC 0x00 + +/* ASR register bits */ +#define NETWAVE_ASR_RXRDY 0x80 +#define NETWAVE_ASR_TXBA 0x01 + +#define TX_TIMEOUT ((32*HZ)/100) + +static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */ +static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */ + +static const unsigned int corConfIENA = 0x01; /* Interrupt enable */ +static const unsigned int corConfLVLREQ = 0x40; /* Keep high */ + +static const unsigned int rxConfRxEna = 0x80; /* Receive Enable */ +static const unsigned int rxConfMAC = 0x20; /* MAC host receive mode*/ +static const unsigned int rxConfPro = 0x10; /* Promiscuous */ +static const unsigned int rxConfAMP = 0x08; /* Accept Multicast Packets */ +static const unsigned int rxConfBcast = 0x04; /* Accept Broadcast Packets */ + +static const unsigned int txConfTxEna = 0x80; /* Transmit Enable */ +static const unsigned int txConfMAC = 0x20; /* Host sends MAC mode */ +static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */ +static const unsigned int txConfKey = 0x02; /* Scramble data packets */ +static const unsigned int txConfLoop = 0x01; /* Loopback mode */ + +/* + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be + left out. If you compile with PCMCIA_DEBUG=0, the debug code will + be present but disabled -- but it can then be enabled for specific + modules at load time with a 'pc_debug=#' option to insmod. +*/ + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Choose the domain, default is 0x100 */ +static u_int domain = 0x100; + +/* Scramble key, range from 0x0 to 0xffff. + * 0x0 is no scrambling. + */ +static u_int scramble_key = 0x0; + +/* Shared memory speed, in ns. The documentation states that + * the card should not be read faster than every 400ns. + * This timing should be provided by the HBA. If it becomes a + * problem, try setting mem_speed to 400. + */ +static int mem_speed; + +module_param(domain, int, 0); +module_param(scramble_key, int, 0); +module_param(mem_speed, int, 0); + +/*====================================================================*/ + +/* PCMCIA (Card Services) related functions */ +static void netwave_release(struct pcmcia_device *link); /* Card removal */ +static int netwave_pcmcia_config(struct pcmcia_device *arg); /* Runs after card + insertion */ +static void netwave_detach(struct pcmcia_device *p_dev); /* Destroy instance */ + +/* Hardware configuration */ +static void netwave_doreset(unsigned int iobase, u_char __iomem *ramBase); +static void netwave_reset(struct net_device *dev); + +/* Misc device stuff */ +static int netwave_open(struct net_device *dev); /* Open the device */ +static int netwave_close(struct net_device *dev); /* Close the device */ + +/* Packet transmission and Packet reception */ +static netdev_tx_t netwave_start_xmit( struct sk_buff *skb, + struct net_device *dev); +static int netwave_rx( struct net_device *dev); + +/* Interrupt routines */ +static irqreturn_t netwave_interrupt(int irq, void *dev_id); +static void netwave_watchdog(struct net_device *); + +/* Wireless extensions */ +static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev); + +static void set_multicast_list(struct net_device *dev); + +/* + A struct pcmcia_device structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a struct pcmcia_device structure can be used to point to + a device-specific private data structure, like this. + + A driver needs to provide a dev_node_t structure for each device + on a card. In some cases, there is only one device per card (for + example, ethernet cards, modems). In other cases, there may be + many actual or logical devices (SCSI adapters, memory cards with + multiple partitions). The dev_node_t structures need to be kept + in a linked list starting at the 'dev' field of a struct pcmcia_device + structure. We allocate them in the card's private data structure, + because they generally can't be allocated dynamically. +*/ + +static const struct iw_handler_def netwave_handler_def; + +#define SIOCGIPSNAP SIOCIWFIRSTPRIV + 1 /* Site Survey Snapshot */ + +#define MAX_ESA 10 + +typedef struct net_addr { + u_char addr48[6]; +} net_addr; + +struct site_survey { + u_short length; + u_char struct_revision; + u_char roaming_state; + + u_char sp_existsFlag; + u_char sp_link_quality; + u_char sp_max_link_quality; + u_char linkQualityGoodFairBoundary; + u_char linkQualityFairPoorBoundary; + u_char sp_utilization; + u_char sp_goodness; + u_char sp_hotheadcount; + u_char roaming_condition; + + net_addr sp; + u_char numAPs; + net_addr nearByAccessPoints[MAX_ESA]; +}; + +typedef struct netwave_private { + struct pcmcia_device *p_dev; + spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ + dev_node_t node; + u_char __iomem *ramBase; + int timeoutCounter; + int lastExec; + struct timer_list watchdog; /* To avoid blocking state */ + struct site_survey nss; + struct iw_statistics iw_stats; /* Wireless stats */ +} netwave_private; + +/* + * The Netwave card is little-endian, so won't work for big endian + * systems. + */ +static inline unsigned short get_uint16(u_char __iomem *staddr) +{ + return readw(staddr); /* Return only 16 bits */ +} + +static inline short get_int16(u_char __iomem * staddr) +{ + return readw(staddr); +} + +/* + * Wait until the WOC (Write Operation Complete) bit in the + * ASR (Adapter Status Register) is asserted. + * This should have aborted if it takes too long time. + */ +static inline void wait_WOC(unsigned int iobase) +{ + /* Spin lock */ + while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; +} + +static void netwave_snapshot(netwave_private *priv, u_char __iomem *ramBase, + unsigned int iobase) { + u_short resultBuffer; + + /* if time since last snapshot is > 1 sec. (100 jiffies?) then take + * new snapshot, else return cached data. This is the recommended rate. + */ + if ( jiffies - priv->lastExec > 100) { + /* Take site survey snapshot */ + /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies - + priv->lastExec); */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + wait_WOC(iobase); + + /* Get result and copy to cach */ + resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); + copy_from_pc( &priv->nss, ramBase+resultBuffer, + sizeof(struct site_survey)); + } +} + +/* + * Function netwave_get_wireless_stats (dev) + * + * Wireless extensions statistics + * + */ +static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev) +{ + unsigned long flags; + unsigned int iobase = dev->base_addr; + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + struct iw_statistics* wstats; + + wstats = &priv->iw_stats; + + spin_lock_irqsave(&priv->spinlock, flags); + + netwave_snapshot( priv, ramBase, iobase); + + wstats->status = priv->nss.roaming_state; + wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); + wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ); + wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f; + wstats->discard.nwid = 0L; + wstats->discard.code = 0L; + wstats->discard.misc = 0L; + + spin_unlock_irqrestore(&priv->spinlock, flags); + + return &priv->iw_stats; +} + +static const struct net_device_ops netwave_netdev_ops = { + .ndo_open = netwave_open, + .ndo_stop = netwave_close, + .ndo_start_xmit = netwave_start_xmit, + .ndo_set_multicast_list = set_multicast_list, + .ndo_tx_timeout = netwave_watchdog, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/* + * Function netwave_attach (void) + * + * Creates an "instance" of the driver, allocating local data + * structures for one device. The device is registered with Card + * Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static int netwave_probe(struct pcmcia_device *link) +{ + struct net_device *dev; + netwave_private *priv; + + DEBUG(0, "netwave_attach()\n"); + + /* Initialize the struct pcmcia_device structure */ + dev = alloc_etherdev(sizeof(netwave_private)); + if (!dev) + return -ENOMEM; + priv = netdev_priv(dev); + priv->p_dev = link; + link->priv = dev; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + /* link->io.NumPorts2 = 16; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */ + link->io.IOAddrLines = 5; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = &netwave_interrupt; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + + /* Netwave private struct init. link/dev/node already taken care of, + * other stuff zero'd - Jean II */ + spin_lock_init(&priv->spinlock); + + /* Netwave specific entries in the device structure */ + dev->netdev_ops = &netwave_netdev_ops; + /* wireless extensions */ + dev->wireless_handlers = &netwave_handler_def; + + dev->watchdog_timeo = TX_TIMEOUT; + + link->irq.Instance = dev; + + return netwave_pcmcia_config( link); +} /* netwave_attach */ + +/* + * Function netwave_detach (link) + * + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void netwave_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + DEBUG(0, "netwave_detach(0x%p)\n", link); + + netwave_release(link); + + if (link->dev_node) + unregister_netdev(dev); + + free_netdev(dev); +} /* netwave_detach */ + +/* + * Wireless Handler : get protocol name + */ +static int netwave_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + strcpy(wrqu->name, "Netwave"); + return 0; +} + +/* + * Wireless Handler : set Network ID + */ +static int netwave_set_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long flags; + unsigned int iobase = dev->base_addr; + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&priv->spinlock, flags); + + if(!wrqu->nwid.disabled) { + domain = wrqu->nwid.value; + printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", + (domain >> 8) & 0x01, domain & 0xff); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); + writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + } + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&priv->spinlock, flags); + + return 0; +} + +/* + * Wireless Handler : get Network ID + */ +static int netwave_get_nwid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + wrqu->nwid.value = domain; + wrqu->nwid.disabled = 0; + wrqu->nwid.fixed = 1; + return 0; +} + +/* + * Wireless Handler : set scramble key + */ +static int netwave_set_scramble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *key) +{ + unsigned long flags; + unsigned int iobase = dev->base_addr; + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&priv->spinlock, flags); + + scramble_key = (key[0] << 8) | key[1]; + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&priv->spinlock, flags); + + return 0; +} + +/* + * Wireless Handler : get scramble key + */ +static int netwave_get_scramble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *key) +{ + key[1] = scramble_key & 0xff; + key[0] = (scramble_key>>8) & 0xff; + wrqu->encoding.flags = IW_ENCODE_ENABLED; + wrqu->encoding.length = 2; + return 0; +} + +/* + * Wireless Handler : get mode + */ +static int netwave_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + if(domain & 0x100) + wrqu->mode = IW_MODE_INFRA; + else + wrqu->mode = IW_MODE_ADHOC; + + return 0; +} + +/* + * Wireless Handler : get range info + */ +static int netwave_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct iw_range *range = (struct iw_range *) extra; + int ret = 0; + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(struct iw_range)); + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; /* Nothing for us in v10 and v11 */ + + /* Set information in the range struct */ + range->throughput = 450 * 1000; /* don't argue on this ! */ + range->min_nwid = 0x0000; + range->max_nwid = 0x01FF; + + range->num_channels = range->num_frequency = 0; + + range->sensitivity = 0x3F; + range->max_qual.qual = 255; + range->max_qual.level = 255; + range->max_qual.noise = 0; + + range->num_bitrates = 1; + range->bitrate[0] = 1000000; /* 1 Mb/s */ + + range->encoding_size[0] = 2; /* 16 bits scrambling */ + range->num_encoding_sizes = 1; + range->max_encoding_tokens = 1; /* Only one key possible */ + + return ret; +} + +/* + * Wireless Private Handler : get snapshot + */ +static int netwave_get_snap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + unsigned long flags; + unsigned int iobase = dev->base_addr; + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&priv->spinlock, flags); + + /* Take snapshot of environment */ + netwave_snapshot( priv, ramBase, iobase); + wrqu->data.length = priv->nss.length; + memcpy(extra, (u_char *) &priv->nss, sizeof( struct site_survey)); + + priv->lastExec = jiffies; + + /* ReEnable interrupts & restore flags */ + spin_unlock_irqrestore(&priv->spinlock, flags); + + return(0); +} + +/* + * Structures to export the Wireless Handlers + * This is the stuff that are treated the wireless extensions (iwconfig) + */ + +static const struct iw_priv_args netwave_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { SIOCGIPSNAP, 0, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof(struct site_survey), + "getsitesurvey" }, +}; + +static const iw_handler netwave_handler[] = +{ + NULL, /* SIOCSIWNAME */ + netwave_get_name, /* SIOCGIWNAME */ + netwave_set_nwid, /* SIOCSIWNWID */ + netwave_get_nwid, /* SIOCGIWNWID */ + NULL, /* SIOCSIWFREQ */ + NULL, /* SIOCGIWFREQ */ + NULL, /* SIOCSIWMODE */ + netwave_get_mode, /* SIOCGIWMODE */ + NULL, /* SIOCSIWSENS */ + NULL, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + netwave_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + NULL, /* SIOCSIWSPY */ + NULL, /* SIOCGIWSPY */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWAP */ + NULL, /* SIOCGIWAP */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWESSID */ + NULL, /* SIOCGIWESSID */ + NULL, /* SIOCSIWNICKN */ + NULL, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + NULL, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + netwave_set_scramble, /* SIOCSIWENCODE */ + netwave_get_scramble, /* SIOCGIWENCODE */ +}; + +static const iw_handler netwave_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ + netwave_get_snap, /* SIOCIWFIRSTPRIV + 1 */ +}; + +static const struct iw_handler_def netwave_handler_def = +{ + .num_standard = ARRAY_SIZE(netwave_handler), + .num_private = ARRAY_SIZE(netwave_private_handler), + .num_private_args = ARRAY_SIZE(netwave_private_args), + .standard = (iw_handler *) netwave_handler, + .private = (iw_handler *) netwave_private_handler, + .private_args = (struct iw_priv_args *) netwave_private_args, + .get_wireless_stats = netwave_get_wireless_stats, +}; + +/* + * Function netwave_pcmcia_config (link) + * + * netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION + * event is received, to configure the PCMCIA socket, and to make the + * device available to the system. + * + */ + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static int netwave_pcmcia_config(struct pcmcia_device *link) { + struct net_device *dev = link->priv; + netwave_private *priv = netdev_priv(dev); + int i, j, last_ret, last_fn; + win_req_t req; + memreq_t mem; + u_char __iomem *ramBase = NULL; + + DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link); + + /* + * Try allocating IO ports. This tries a few fixed addresses. + * If you want, you can also read the card's config table to + * pick addresses -- see the serial driver for an example. + */ + for (i = j = 0x0; j < 0x400; j += 0x20) { + link->io.BasePort1 = j ^ 0x300; + i = pcmcia_request_io(link, &link->io); + if (i == 0) + break; + } + if (i != 0) { + cs_error(link, RequestIO, i); + goto failed; + } + + /* + * Now allocate an interrupt line. Note that this does not + * actually assign a handler to the interrupt. + */ + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + + /* + * Allocate a 32K memory window. Note that the struct pcmcia_device + * structure provides space for one window handle -- if your + * device needs several windows, you'll need to keep track of + * the handles in your private data structure, dev->priv. + */ + DEBUG(1, "Setting mem speed of %d\n", mem_speed); + + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + req.Base = 0; req.Size = 0x8000; + req.AccessSpeed = mem_speed; + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); + mem.CardOffset = 0x20000; mem.Page = 0; + CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem)); + + /* Store base address of the common window frame */ + ramBase = ioremap(req.Base, 0x8000); + priv->ramBase = ramBase; + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); + + if (register_netdev(dev) != 0) { + printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); + goto failed; + } + + strcpy(priv->node.dev_name, dev->name); + link->dev_node = &priv->node; + + /* Reset card before reading physical address */ + netwave_doreset(dev->base_addr, ramBase); + + /* Read the ethernet address and fill in the Netwave registers. */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i); + + printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx, " + "id %c%c, hw_addr %pM\n", + dev->name, dev->base_addr, dev->irq, + (u_long) ramBase, + (int) readb(ramBase+NETWAVE_EREG_NI), + (int) readb(ramBase+NETWAVE_EREG_NI+1), + dev->dev_addr); + + /* get revision words */ + printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", + get_uint16(ramBase + NETWAVE_EREG_ARW), + get_uint16(ramBase + NETWAVE_EREG_ARW+2)); + return 0; + +cs_failed: + cs_error(link, last_fn, last_ret); +failed: + netwave_release(link); + return -ENODEV; +} /* netwave_pcmcia_config */ + +/* + * Function netwave_release (arg) + * + * After a card is removed, netwave_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void netwave_release(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + netwave_private *priv = netdev_priv(dev); + + DEBUG(0, "netwave_release(0x%p)\n", link); + + pcmcia_disable_device(link); + if (link->win) + iounmap(priv->ramBase); +} + +static int netwave_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int netwave_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + netwave_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + + +/* + * Function netwave_doreset (ioBase, ramBase) + * + * Proper hardware reset of the card. + */ +static void netwave_doreset(unsigned int ioBase, u_char __iomem *ramBase) +{ + /* Reset card */ + wait_WOC(ioBase); + outb(0x80, ioBase + NETWAVE_REG_PMR); + writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */ + outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */ +} + +/* + * Function netwave_reset (dev) + * + * Reset and restore all of the netwave registers + */ +static void netwave_reset(struct net_device *dev) { + /* u_char state; */ + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + unsigned int iobase = dev->base_addr; + + DEBUG(0, "netwave_reset: Done with hardware reset\n"); + + priv->timeoutCounter = 0; + + /* Reset card */ + netwave_doreset(iobase, ramBase); + printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n"); + + /* Write a NOP to check the card */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + + /* Set receive conf */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); + writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); + + /* Set transmit conf */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0); + writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); + + /* Now set the MU Domain */ + printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); + writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* Set scramble key */ + printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* Enable interrupts, bit 4 high to keep unused + * source from interrupting us, bit 2 high to + * set interrupt enable, 567 to enable TxDN, + * RxErr and RxRdy + */ + wait_WOC(iobase); + outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR); + + /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36 + * waitWOC + * skriv 80 til d000:3688 + * sjekk om det ble 80 + */ + + /* Enable Receiver */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + + /* Set the IENA bit in COR */ + wait_WOC(iobase); + outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR); +} + +/* + * Function netwave_hw_xmit (data, len, dev) + */ +static int netwave_hw_xmit(unsigned char* data, int len, + struct net_device* dev) { + unsigned long flags; + unsigned int TxFreeList, + curBuff, + MaxData, + DataOffset; + int tmpcount; + + netwave_private *priv = netdev_priv(dev); + u_char __iomem * ramBase = priv->ramBase; + unsigned int iobase = dev->base_addr; + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&priv->spinlock, flags); + + /* Check if there are transmit buffers available */ + wait_WOC(iobase); + if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) { + /* No buffers available */ + printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n", + dev->name); + spin_unlock_irqrestore(&priv->spinlock, flags); + return 1; + } + + dev->stats.tx_bytes += len; + + DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n", + readb(ramBase + NETWAVE_EREG_SPCQ), + readb(ramBase + NETWAVE_EREG_SPU), + readb(ramBase + NETWAVE_EREG_LIF), + readb(ramBase + NETWAVE_EREG_ISPLQ)); + + /* Now try to insert it into the adapters free memory */ + wait_WOC(iobase); + TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP); + MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2); + DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4); + + DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n", + TxFreeList, MaxData, DataOffset); + + /* Copy packet to the adapter fragment buffers */ + curBuff = TxFreeList; + tmpcount = 0; + while (tmpcount < len) { + int tmplen = len - tmpcount; + copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, + (tmplen < MaxData) ? tmplen : MaxData); + tmpcount += MaxData; + + /* Advance to next buffer */ + curBuff = get_uint16(ramBase + curBuff); + } + + /* Now issue transmit list */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0); + writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + spin_unlock_irqrestore(&priv->spinlock, flags); + return 0; +} + +static netdev_tx_t netwave_start_xmit(struct sk_buff *skb, + struct net_device *dev) { + /* This flag indicate that the hardware can't perform a transmission. + * Theoritically, NET3 check it before sending a packet to the driver, + * but in fact it never do that and pool continuously. + * As the watchdog will abort too long transmissions, we are quite safe... + */ + + netif_stop_queue(dev); + + { + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char* buf = skb->data; + + if (netwave_hw_xmit( buf, length, dev) == 1) { + /* Some error, let's make them call us another time? */ + netif_start_queue(dev); + } + dev->trans_start = jiffies; + } + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} /* netwave_start_xmit */ + +/* + * Function netwave_interrupt (irq, dev_id) + * + * This function is the interrupt handler for the Netwave card. This + * routine will be called whenever: + * 1. A packet is received. + * 2. A packet has successfully been transferred and the unit is + * ready to transmit another packet. + * 3. A command has completed execution. + */ +static irqreturn_t netwave_interrupt(int irq, void* dev_id) +{ + unsigned int iobase; + u_char __iomem *ramBase; + struct net_device *dev = (struct net_device *)dev_id; + struct netwave_private *priv = netdev_priv(dev); + struct pcmcia_device *link = priv->p_dev; + int i; + + if (!netif_device_present(dev)) + return IRQ_NONE; + + iobase = dev->base_addr; + ramBase = priv->ramBase; + + /* Now find what caused the interrupt, check while interrupts ready */ + for (i = 0; i < 10; i++) { + u_char status; + + wait_WOC(iobase); + if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02)) + break; /* None of the interrupt sources asserted (normal exit) */ + + status = inb(iobase + NETWAVE_REG_ASR); + + if (!pcmcia_dev_present(link)) { + DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x " + "from removed or suspended card!\n", status); + break; + } + + /* RxRdy */ + if (status & 0x80) { + netwave_rx(dev); + /* wait_WOC(iobase); */ + /* RxRdy cannot be reset directly by the host */ + } + /* RxErr */ + if (status & 0x40) { + u_char rser; + + rser = readb(ramBase + NETWAVE_EREG_RSER); + + if (rser & 0x04) { + ++dev->stats.rx_dropped; + ++dev->stats.rx_crc_errors; + } + if (rser & 0x02) + ++dev->stats.rx_frame_errors; + + /* Clear the RxErr bit in RSER. RSER+4 is the + * write part. Also clear the RxCRC (0x04) and + * RxBig (0x02) bits if present */ + wait_WOC(iobase); + writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4); + + /* Write bit 6 high to ASCC to clear RxErr in ASR, + * WOC must be set first! + */ + wait_WOC(iobase); + writeb(0x40, ramBase + NETWAVE_EREG_ASCC); + + /* Remember to count up dev->stats on error packets */ + ++dev->stats.rx_errors; + } + /* TxDN */ + if (status & 0x20) { + int txStatus; + + txStatus = readb(ramBase + NETWAVE_EREG_TSER); + DEBUG(3, "Transmit done. TSER = %x id %x\n", + txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1)); + + if (txStatus & 0x20) { + /* Transmitting was okay, clear bits */ + wait_WOC(iobase); + writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4); + ++dev->stats.tx_packets; + } + + if (txStatus & 0xd0) { + if (txStatus & 0x80) { + ++dev->stats.collisions; /* Because of /proc/net/dev*/ + /* ++dev->stats.tx_aborted_errors; */ + /* printk("Collision. %ld\n", jiffies - dev->trans_start); */ + } + if (txStatus & 0x40) + ++dev->stats.tx_carrier_errors; + /* 0x80 TxGU Transmit giveup - nine times and no luck + * 0x40 TxNOAP No access point. Discarded packet. + * 0x10 TxErr Transmit error. Always set when + * TxGU and TxNOAP is set. (Those are the only ones + * to set TxErr). + */ + DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", + txStatus); + + /* Clear out TxGU, TxNOAP, TxErr and TxTrys */ + wait_WOC(iobase); + writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4); + ++dev->stats.tx_errors; + } + DEBUG(3, "New status is TSER %x ASR %x\n", + readb(ramBase + NETWAVE_EREG_TSER), + inb(iobase + NETWAVE_REG_ASR)); + + netif_wake_queue(dev); + } + /* TxBA, this would trigger on all error packets received */ + /* if (status & 0x01) { + DEBUG(4, "Transmit buffers available, %x\n", status); + } + */ + } + /* Handled if we looped at least one time - Jean II */ + return IRQ_RETVAL(i); +} /* netwave_interrupt */ + +/* + * Function netwave_watchdog (a) + * + * Watchdog : when we start a transmission, we set a timer in the + * kernel. If the transmission complete, this timer is disabled. If + * it expire, we reset the card. + * + */ +static void netwave_watchdog(struct net_device *dev) { + + DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name); + netwave_reset(dev); + dev->trans_start = jiffies; + netif_wake_queue(dev); +} /* netwave_watchdog */ + +static int netwave_rx(struct net_device *dev) +{ + netwave_private *priv = netdev_priv(dev); + u_char __iomem *ramBase = priv->ramBase; + unsigned int iobase = dev->base_addr; + u_char rxStatus; + struct sk_buff *skb = NULL; + unsigned int curBuffer, + rcvList; + int rcvLen; + int tmpcount = 0; + int dataCount, dataOffset; + int i; + u_char *ptr; + + DEBUG(3, "xinw_rx: Receiving ... \n"); + + /* Receive max 10 packets for now. */ + for (i = 0; i < 10; i++) { + /* Any packets? */ + wait_WOC(iobase); + rxStatus = readb(ramBase + NETWAVE_EREG_RSER); + if ( !( rxStatus & 0x80)) /* No more packets */ + break; + + /* Check if multicast/broadcast or other */ + /* multicast = (rxStatus & 0x20); */ + + /* The receive list pointer and length of the packet */ + wait_WOC(iobase); + rcvLen = get_int16( ramBase + NETWAVE_EREG_RDP); + rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2); + + if (rcvLen < 0) { + printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", + rcvLen); + return 0; + } + + skb = dev_alloc_skb(rcvLen+5); + if (skb == NULL) { + DEBUG(1, "netwave_rx: Could not allocate an sk_buff of " + "length %d\n", rcvLen); + ++dev->stats.rx_dropped; + /* Tell the adapter to skip the packet */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + return 0; + } + + skb_reserve( skb, 2); /* Align IP on 16 byte */ + skb_put( skb, rcvLen); + + /* Copy packet fragments to the skb data area */ + ptr = (u_char*) skb->data; + curBuffer = rcvList; + tmpcount = 0; + while ( tmpcount < rcvLen) { + /* Get length and offset of current buffer */ + dataCount = get_uint16( ramBase+curBuffer+2); + dataOffset = get_uint16( ramBase+curBuffer+4); + + copy_from_pc( ptr + tmpcount, + ramBase+curBuffer+dataOffset, dataCount); + + tmpcount += dataCount; + + /* Point to next buffer */ + curBuffer = get_uint16(ramBase + curBuffer); + } + + skb->protocol = eth_type_trans(skb,dev); + /* Queue packet for network layer */ + netif_rx(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += rcvLen; + + /* Got the packet, tell the adapter to skip it */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + DEBUG(3, "Packet reception ok\n"); + } + return 0; +} + +static int netwave_open(struct net_device *dev) { + netwave_private *priv = netdev_priv(dev); + struct pcmcia_device *link = priv->p_dev; + + DEBUG(1, "netwave_open: starting.\n"); + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + link->open++; + + netif_start_queue(dev); + netwave_reset(dev); + + return 0; +} + +static int netwave_close(struct net_device *dev) { + netwave_private *priv = netdev_priv(dev); + struct pcmcia_device *link = priv->p_dev; + + DEBUG(1, "netwave_close: finishing.\n"); + + link->open--; + netif_stop_queue(dev); + + return 0; +} + +static struct pcmcia_device_id netwave_ids[] = { + PCMCIA_DEVICE_PROD_ID12("Xircom", "CreditCard Netwave", 0x2e3ee845, 0x54e28a28), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, netwave_ids); + +static struct pcmcia_driver netwave_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "netwave_cs", + }, + .probe = netwave_probe, + .remove = netwave_detach, + .id_table = netwave_ids, + .suspend = netwave_suspend, + .resume = netwave_resume, +}; + +static int __init init_netwave_cs(void) +{ + return pcmcia_register_driver(&netwave_driver); +} + +static void __exit exit_netwave_cs(void) +{ + pcmcia_unregister_driver(&netwave_driver); +} + +module_init(init_netwave_cs); +module_exit(exit_netwave_cs); + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ +static void set_multicast_list(struct net_device *dev) +{ + unsigned int iobase = dev->base_addr; + netwave_private *priv = netdev_priv(dev); + u_char __iomem * ramBase = priv->ramBase; + u_char rcvMode = 0; + +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + static int old; + if (old != dev->mc_count) { + old = dev->mc_count; + DEBUG(0, "%s: setting Rx mode to %d addresses.\n", + dev->name, dev->mc_count); + } + } +#endif + + if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) { + /* Multicast Mode */ + rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast; + } else if (dev->flags & IFF_PROMISC) { + /* Promiscous mode */ + rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast; + } else { + /* Normal mode */ + rcvMode = rxConfRxEna + rxConfBcast; + } + + /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/ + /* Now set receive mode */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); + writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); +} +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From bc7601c38b5051fcc3c88f68437b2daace4aff53 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Oct 2009 09:18:23 -0700 Subject: Staging: strip: add TODO file Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/staging/strip/TODO | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 drivers/staging/strip/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/strip/TODO b/drivers/staging/strip/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/strip/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman . -- cgit v1.2.3-59-g8ed1b From bcecf0ff98b4b55d65c8b0442803bd6b66f3d796 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Oct 2009 09:18:24 -0700 Subject: Staging: arlan: add TODO file Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/staging/arlan/TODO | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 drivers/staging/arlan/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/arlan/TODO b/drivers/staging/arlan/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/arlan/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman . -- cgit v1.2.3-59-g8ed1b From 5cc49ab71a879f71f45b8c1bb6b1f883674a0dff Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Oct 2009 09:18:25 -0700 Subject: Staging: wavelan: add TODO file Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/staging/wavelan/TODO | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 drivers/staging/wavelan/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/wavelan/TODO b/drivers/staging/wavelan/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/wavelan/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman . -- cgit v1.2.3-59-g8ed1b From f1f93cddfe713d20e9004c29a02e9c183e460a53 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Oct 2009 09:18:26 -0700 Subject: Staging: netwave: add TODO file Signed-off-by: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/staging/netwave/TODO | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 drivers/staging/netwave/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/netwave/TODO b/drivers/staging/netwave/TODO new file mode 100644 index 000000000000..9bd15a2f6d9e --- /dev/null +++ b/drivers/staging/netwave/TODO @@ -0,0 +1,7 @@ +TODO: + - step up and maintain this driver to ensure that it continues + to work. Having the hardware for this is pretty much a + requirement. If this does not happen, the will be removed in + the 2.6.35 kernel release. + +Please send patches to Greg Kroah-Hartman . -- cgit v1.2.3-59-g8ed1b From 6a6a5c7af130e703c23d23ebe3cde23e079e554e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 6 Nov 2009 15:35:15 -0800 Subject: staging/wireless: don't build when NET etc. are not enabled These wireless drivers in staging could be built when CONFIG_NET=n, CONFIG_NETDEVICES=n, causing this build error: net/wireless/wext-priv.c: In function 'ioctl_private_call': net/wireless/wext-priv.c:206: error: implicit declaration of function 'call_commit_handler' due to faulty selects. Signed-off-by: Randy Dunlap Cc: Greg Kroah-Hartman Signed-off-by: John W. Linville --- drivers/staging/arlan/Kconfig | 2 +- drivers/staging/netwave/Kconfig | 2 +- drivers/staging/wavelan/Kconfig | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/arlan/Kconfig b/drivers/staging/arlan/Kconfig index 0585ed8b4d3e..5e42b81f97b0 100644 --- a/drivers/staging/arlan/Kconfig +++ b/drivers/staging/arlan/Kconfig @@ -1,6 +1,6 @@ config ARLAN tristate "Aironet Arlan 655 & IC2200 DS support" - depends on ISA && !64BIT + depends on ISA && !64BIT && WLAN select WIRELESS_EXT ---help--- Aironet makes Arlan, a class of wireless LAN adapters. These use the diff --git a/drivers/staging/netwave/Kconfig b/drivers/staging/netwave/Kconfig index c0c996c0550a..8033e8171f9e 100644 --- a/drivers/staging/netwave/Kconfig +++ b/drivers/staging/netwave/Kconfig @@ -1,6 +1,6 @@ config PCMCIA_NETWAVE tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on PCMCIA + depends on PCMCIA && WLAN select WIRELESS_EXT select WEXT_PRIV help diff --git a/drivers/staging/wavelan/Kconfig b/drivers/staging/wavelan/Kconfig index 786060e025c0..af655668c2a7 100644 --- a/drivers/staging/wavelan/Kconfig +++ b/drivers/staging/wavelan/Kconfig @@ -1,6 +1,6 @@ config WAVELAN tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on ISA + depends on ISA && WLAN select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV @@ -25,7 +25,7 @@ config WAVELAN config PCMCIA_WAVELAN tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on PCMCIA + depends on PCMCIA && WLAN select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV -- cgit v1.2.3-59-g8ed1b From c85e9d7739fc8d879c4293ea020760926d6f87cd Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 17 Nov 2009 10:16:32 -0500 Subject: znet: fix build failure from i82593.h relocation znet was including "wireless/i82593.h" (which is a bit wierd), and I missed that when I relocated i82593.h to drivers/staging/wavelan. Since I don't have ISA turned-on in my normal .config, I didn't see the build failures -- mea culpa! Signed-off-by: John W. Linville --- drivers/net/znet.c | 3 +- drivers/staging/wavelan/i82593.h | 229 --------------------------------- drivers/staging/wavelan/wavelan_cs.p.h | 2 +- include/linux/i82593.h | 229 +++++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+), 232 deletions(-) delete mode 100644 drivers/staging/wavelan/i82593.h create mode 100644 include/linux/i82593.h (limited to 'drivers/staging') diff --git a/drivers/net/znet.c b/drivers/net/znet.c index b42347333750..443c4eee28c1 100644 --- a/drivers/net/znet.c +++ b/drivers/net/znet.c @@ -103,8 +103,7 @@ #include #include -/* This include could be elsewhere, since it is not wireless specific */ -#include "wireless/i82593.h" +#include static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n"; diff --git a/drivers/staging/wavelan/i82593.h b/drivers/staging/wavelan/i82593.h deleted file mode 100644 index afac5c7a323d..000000000000 --- a/drivers/staging/wavelan/i82593.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Definitions for Intel 82593 CSMA/CD Core LAN Controller - * The definitions are taken from the 1992 users manual with Intel - * order number 297125-001. - * - * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp - * - * Copyright 1994, Anders Klemets - * - * HISTORY - * i82593.h,v - * Revision 1.4 2005/11/4 09:15:00 baroniunas - * Modified copyright with permission of author as follows: - * - * "If I82539.H is the only file with my copyright statement - * that is included in the Source Forge project, then you have - * my approval to change the copyright statement to be a GPL - * license, in the way you proposed on October 10." - * - * Revision 1.1 1996/07/17 15:23:12 root - * Initial revision - * - * Revision 1.3 1995/04/05 15:13:58 adj - * Initial alpha release - * - * Revision 1.2 1994/06/16 23:57:31 klemets - * Mirrored all the fields in the configuration block. - * - * Revision 1.1 1994/06/02 20:25:34 klemets - * Initial revision - * - * - */ -#ifndef _I82593_H -#define _I82593_H - -/* Intel 82593 CSMA/CD Core LAN Controller */ - -/* Port 0 Command Register definitions */ - -/* Execution operations */ -#define OP0_NOP 0 /* CHNL = 0 */ -#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ -#define OP0_IA_SETUP 1 -#define OP0_CONFIGURE 2 -#define OP0_MC_SETUP 3 -#define OP0_TRANSMIT 4 -#define OP0_TDR 5 -#define OP0_DUMP 6 -#define OP0_DIAGNOSE 7 -#define OP0_TRANSMIT_NO_CRC 9 -#define OP0_RETRANSMIT 12 -#define OP0_ABORT 13 -/* Reception operations */ -#define OP0_RCV_ENABLE 8 -#define OP0_RCV_DISABLE 10 -#define OP0_STOP_RCV 11 -/* Status pointer control operations */ -#define OP0_FIX_PTR 15 /* CHNL = 1 */ -#define OP0_RLS_PTR 15 /* CHNL = 0 */ -#define OP0_RESET 14 - -#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ -#define CR0_STATUS_0 0x00 -#define CR0_STATUS_1 0x20 -#define CR0_STATUS_2 0x40 -#define CR0_STATUS_3 0x60 -#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ - -/* Port 0 Status Register definitions */ - -#define SR0_NO_RESULT 0 /* dummy */ -#define SR0_EVENT_MASK 0x0f -#define SR0_IA_SETUP_DONE 1 -#define SR0_CONFIGURE_DONE 2 -#define SR0_MC_SETUP_DONE 3 -#define SR0_TRANSMIT_DONE 4 -#define SR0_TDR_DONE 5 -#define SR0_DUMP_DONE 6 -#define SR0_DIAGNOSE_PASSED 7 -#define SR0_TRANSMIT_NO_CRC_DONE 9 -#define SR0_RETRANSMIT_DONE 12 -#define SR0_EXECUTION_ABORTED 13 -#define SR0_END_OF_FRAME 8 -#define SR0_RECEPTION_ABORTED 10 -#define SR0_DIAGNOSE_FAILED 15 -#define SR0_STOP_REG_HIT 11 - -#define SR0_CHNL (1 << 4) -#define SR0_EXECUTION (1 << 5) -#define SR0_RECEPTION (1 << 6) -#define SR0_INTERRUPT (1 << 7) -#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) - -#define SR3_EXEC_STATE_MASK 0x03 -#define SR3_EXEC_IDLE 0 -#define SR3_TX_ABORT_IN_PROGRESS 1 -#define SR3_EXEC_ACTIVE 2 -#define SR3_ABORT_IN_PROGRESS 3 -#define SR3_EXEC_CHNL (1 << 2) -#define SR3_STP_ON_NO_RSRC (1 << 3) -#define SR3_RCVING_NO_RSRC (1 << 4) -#define SR3_RCV_STATE_MASK 0x60 -#define SR3_RCV_IDLE 0x00 -#define SR3_RCV_READY 0x20 -#define SR3_RCV_ACTIVE 0x40 -#define SR3_RCV_STOP_IN_PROG 0x60 -#define SR3_RCV_CHNL (1 << 7) - -/* Port 1 Command Register definitions */ - -#define OP1_NOP 0 -#define OP1_SWIT_TO_PORT_0 1 -#define OP1_INT_DISABLE 2 -#define OP1_INT_ENABLE 3 -#define OP1_SET_TS 5 -#define OP1_RST_TS 7 -#define OP1_POWER_DOWN 8 -#define OP1_RESET_RING_MNGMT 11 -#define OP1_RESET 14 -#define OP1_SEL_RST 15 - -#define CR1_STATUS_4 0x00 -#define CR1_STATUS_5 0x20 -#define CR1_STATUS_6 0x40 -#define CR1_STOP_REG_UPDATE (1 << 7) - -/* Receive frame status bits */ - -#define RX_RCLD (1 << 0) -#define RX_IA_MATCH (1 << 1) -#define RX_NO_AD_MATCH (1 << 2) -#define RX_NO_SFD (1 << 3) -#define RX_SRT_FRM (1 << 7) -#define RX_OVRRUN (1 << 8) -#define RX_ALG_ERR (1 << 10) -#define RX_CRC_ERR (1 << 11) -#define RX_LEN_ERR (1 << 12) -#define RX_RCV_OK (1 << 13) -#define RX_TYP_LEN (1 << 15) - -/* Transmit status bits */ - -#define TX_NCOL_MASK 0x0f -#define TX_FRTL (1 << 4) -#define TX_MAX_COL (1 << 5) -#define TX_HRT_BEAT (1 << 6) -#define TX_DEFER (1 << 7) -#define TX_UND_RUN (1 << 8) -#define TX_LOST_CTS (1 << 9) -#define TX_LOST_CRS (1 << 10) -#define TX_LTCOL (1 << 11) -#define TX_OK (1 << 13) -#define TX_COLL (1 << 15) - -struct i82593_conf_block { - u_char fifo_limit : 4, - forgnesi : 1, - fifo_32 : 1, - d6mod : 1, - throttle_enb : 1; - u_char throttle : 6, - cntrxint : 1, - contin : 1; - u_char addr_len : 3, - acloc : 1, - preamb_len : 2, - loopback : 2; - u_char lin_prio : 3, - tbofstop : 1, - exp_prio : 3, - bof_met : 1; - u_char : 4, - ifrm_spc : 4; - u_char : 5, - slottim_low : 3; - u_char slottim_hi : 3, - : 1, - max_retr : 4; - u_char prmisc : 1, - bc_dis : 1, - : 1, - crs_1 : 1, - nocrc_ins : 1, - crc_1632 : 1, - : 1, - crs_cdt : 1; - u_char cs_filter : 3, - crs_src : 1, - cd_filter : 3, - : 1; - u_char : 2, - min_fr_len : 6; - u_char lng_typ : 1, - lng_fld : 1, - rxcrc_xf : 1, - artx : 1, - sarec : 1, - tx_jabber : 1, /* why is this called max_len in the manual? */ - hash_1 : 1, - lbpkpol : 1; - u_char : 6, - fdx : 1, - : 1; - u_char dummy_6 : 6, /* supposed to be ones */ - mult_ia : 1, - dis_bof : 1; - u_char dummy_1 : 1, /* supposed to be one */ - tx_ifs_retrig : 2, - mc_all : 1, - rcv_mon : 2, - frag_acpt : 1, - tstrttrs : 1; - u_char fretx : 1, - runt_eop : 1, - hw_sw_pin : 1, - big_endn : 1, - syncrqs : 1, - sttlen : 1, - tx_eop : 1, - rx_eop : 1; - u_char rbuf_size : 5, - rcvstop : 1, - : 2; -}; - -#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ - -#endif /* _I82593_H */ diff --git a/drivers/staging/wavelan/wavelan_cs.p.h b/drivers/staging/wavelan/wavelan_cs.p.h index 81d91531c4f9..8fbfaa8a5a67 100644 --- a/drivers/staging/wavelan/wavelan_cs.p.h +++ b/drivers/staging/wavelan/wavelan_cs.p.h @@ -446,7 +446,7 @@ #include /* Wavelan declarations */ -#include "i82593.h" /* Definitions for the Intel chip */ +#include /* Definitions for the Intel chip */ #include "wavelan_cs.h" /* Others bits of the hardware */ diff --git a/include/linux/i82593.h b/include/linux/i82593.h new file mode 100644 index 000000000000..afac5c7a323d --- /dev/null +++ b/include/linux/i82593.h @@ -0,0 +1,229 @@ +/* + * Definitions for Intel 82593 CSMA/CD Core LAN Controller + * The definitions are taken from the 1992 users manual with Intel + * order number 297125-001. + * + * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp + * + * Copyright 1994, Anders Klemets + * + * HISTORY + * i82593.h,v + * Revision 1.4 2005/11/4 09:15:00 baroniunas + * Modified copyright with permission of author as follows: + * + * "If I82539.H is the only file with my copyright statement + * that is included in the Source Forge project, then you have + * my approval to change the copyright statement to be a GPL + * license, in the way you proposed on October 10." + * + * Revision 1.1 1996/07/17 15:23:12 root + * Initial revision + * + * Revision 1.3 1995/04/05 15:13:58 adj + * Initial alpha release + * + * Revision 1.2 1994/06/16 23:57:31 klemets + * Mirrored all the fields in the configuration block. + * + * Revision 1.1 1994/06/02 20:25:34 klemets + * Initial revision + * + * + */ +#ifndef _I82593_H +#define _I82593_H + +/* Intel 82593 CSMA/CD Core LAN Controller */ + +/* Port 0 Command Register definitions */ + +/* Execution operations */ +#define OP0_NOP 0 /* CHNL = 0 */ +#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ +#define OP0_IA_SETUP 1 +#define OP0_CONFIGURE 2 +#define OP0_MC_SETUP 3 +#define OP0_TRANSMIT 4 +#define OP0_TDR 5 +#define OP0_DUMP 6 +#define OP0_DIAGNOSE 7 +#define OP0_TRANSMIT_NO_CRC 9 +#define OP0_RETRANSMIT 12 +#define OP0_ABORT 13 +/* Reception operations */ +#define OP0_RCV_ENABLE 8 +#define OP0_RCV_DISABLE 10 +#define OP0_STOP_RCV 11 +/* Status pointer control operations */ +#define OP0_FIX_PTR 15 /* CHNL = 1 */ +#define OP0_RLS_PTR 15 /* CHNL = 0 */ +#define OP0_RESET 14 + +#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ +#define CR0_STATUS_0 0x00 +#define CR0_STATUS_1 0x20 +#define CR0_STATUS_2 0x40 +#define CR0_STATUS_3 0x60 +#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ + +/* Port 0 Status Register definitions */ + +#define SR0_NO_RESULT 0 /* dummy */ +#define SR0_EVENT_MASK 0x0f +#define SR0_IA_SETUP_DONE 1 +#define SR0_CONFIGURE_DONE 2 +#define SR0_MC_SETUP_DONE 3 +#define SR0_TRANSMIT_DONE 4 +#define SR0_TDR_DONE 5 +#define SR0_DUMP_DONE 6 +#define SR0_DIAGNOSE_PASSED 7 +#define SR0_TRANSMIT_NO_CRC_DONE 9 +#define SR0_RETRANSMIT_DONE 12 +#define SR0_EXECUTION_ABORTED 13 +#define SR0_END_OF_FRAME 8 +#define SR0_RECEPTION_ABORTED 10 +#define SR0_DIAGNOSE_FAILED 15 +#define SR0_STOP_REG_HIT 11 + +#define SR0_CHNL (1 << 4) +#define SR0_EXECUTION (1 << 5) +#define SR0_RECEPTION (1 << 6) +#define SR0_INTERRUPT (1 << 7) +#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) + +#define SR3_EXEC_STATE_MASK 0x03 +#define SR3_EXEC_IDLE 0 +#define SR3_TX_ABORT_IN_PROGRESS 1 +#define SR3_EXEC_ACTIVE 2 +#define SR3_ABORT_IN_PROGRESS 3 +#define SR3_EXEC_CHNL (1 << 2) +#define SR3_STP_ON_NO_RSRC (1 << 3) +#define SR3_RCVING_NO_RSRC (1 << 4) +#define SR3_RCV_STATE_MASK 0x60 +#define SR3_RCV_IDLE 0x00 +#define SR3_RCV_READY 0x20 +#define SR3_RCV_ACTIVE 0x40 +#define SR3_RCV_STOP_IN_PROG 0x60 +#define SR3_RCV_CHNL (1 << 7) + +/* Port 1 Command Register definitions */ + +#define OP1_NOP 0 +#define OP1_SWIT_TO_PORT_0 1 +#define OP1_INT_DISABLE 2 +#define OP1_INT_ENABLE 3 +#define OP1_SET_TS 5 +#define OP1_RST_TS 7 +#define OP1_POWER_DOWN 8 +#define OP1_RESET_RING_MNGMT 11 +#define OP1_RESET 14 +#define OP1_SEL_RST 15 + +#define CR1_STATUS_4 0x00 +#define CR1_STATUS_5 0x20 +#define CR1_STATUS_6 0x40 +#define CR1_STOP_REG_UPDATE (1 << 7) + +/* Receive frame status bits */ + +#define RX_RCLD (1 << 0) +#define RX_IA_MATCH (1 << 1) +#define RX_NO_AD_MATCH (1 << 2) +#define RX_NO_SFD (1 << 3) +#define RX_SRT_FRM (1 << 7) +#define RX_OVRRUN (1 << 8) +#define RX_ALG_ERR (1 << 10) +#define RX_CRC_ERR (1 << 11) +#define RX_LEN_ERR (1 << 12) +#define RX_RCV_OK (1 << 13) +#define RX_TYP_LEN (1 << 15) + +/* Transmit status bits */ + +#define TX_NCOL_MASK 0x0f +#define TX_FRTL (1 << 4) +#define TX_MAX_COL (1 << 5) +#define TX_HRT_BEAT (1 << 6) +#define TX_DEFER (1 << 7) +#define TX_UND_RUN (1 << 8) +#define TX_LOST_CTS (1 << 9) +#define TX_LOST_CRS (1 << 10) +#define TX_LTCOL (1 << 11) +#define TX_OK (1 << 13) +#define TX_COLL (1 << 15) + +struct i82593_conf_block { + u_char fifo_limit : 4, + forgnesi : 1, + fifo_32 : 1, + d6mod : 1, + throttle_enb : 1; + u_char throttle : 6, + cntrxint : 1, + contin : 1; + u_char addr_len : 3, + acloc : 1, + preamb_len : 2, + loopback : 2; + u_char lin_prio : 3, + tbofstop : 1, + exp_prio : 3, + bof_met : 1; + u_char : 4, + ifrm_spc : 4; + u_char : 5, + slottim_low : 3; + u_char slottim_hi : 3, + : 1, + max_retr : 4; + u_char prmisc : 1, + bc_dis : 1, + : 1, + crs_1 : 1, + nocrc_ins : 1, + crc_1632 : 1, + : 1, + crs_cdt : 1; + u_char cs_filter : 3, + crs_src : 1, + cd_filter : 3, + : 1; + u_char : 2, + min_fr_len : 6; + u_char lng_typ : 1, + lng_fld : 1, + rxcrc_xf : 1, + artx : 1, + sarec : 1, + tx_jabber : 1, /* why is this called max_len in the manual? */ + hash_1 : 1, + lbpkpol : 1; + u_char : 6, + fdx : 1, + : 1; + u_char dummy_6 : 6, /* supposed to be ones */ + mult_ia : 1, + dis_bof : 1; + u_char dummy_1 : 1, /* supposed to be one */ + tx_ifs_retrig : 2, + mc_all : 1, + rcv_mon : 2, + frag_acpt : 1, + tstrttrs : 1; + u_char fretx : 1, + runt_eop : 1, + hw_sw_pin : 1, + big_endn : 1, + syncrqs : 1, + sttlen : 1, + tx_eop : 1, + rx_eop : 1; + u_char rbuf_size : 5, + rcvstop : 1, + : 2; +}; + +#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ + +#endif /* _I82593_H */ -- cgit v1.2.3-59-g8ed1b