diff options
author | David S. Miller <davem@davemloft.net> | 2008-07-18 03:58:52 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-18 03:58:52 -0700 |
commit | a60f28fa934ccadbf526f4dab8d73079480002a4 (patch) | |
tree | f3f577dcaf45b85e6308510a3e26239863324527 /drivers/net | |
parent | 49997d75152b3d23c53b0fa730599f2f74c92c65 (diff) |
Revert "remove the strip driver"
This reverts commit 94d9842403f770239a656586442454b7a8f2df29.
Alan says it's not appropriate to remove this driver,
Adrian Bunk also agrees with this revert.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/Kconfig | 24 | ||||
-rw-r--r-- | drivers/net/wireless/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/strip.c | 2804 |
3 files changed, 2829 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d5b006f5b86..91fc2c765d9 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -14,6 +14,30 @@ config 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 + (on the WWW at <http://mosquitonet.stanford.edu/>) 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 2668934abbf..54a4f6f1db6 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_IPW2100) += ipw2100.o obj-$(CONFIG_IPW2200) += ipw2200.o +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 new file mode 100644 index 00000000000..883af891ebf --- /dev/null +++ b/drivers/net/wireless/strip.c @@ -0,0 +1,2804 @@ +/* + * 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 <cheshire@cs.stanford.edu> + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +# include <linux/ctype.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/if_strip.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/serial.h> +#include <linux/serialP.h> +#include <linux/rcupdate.h> +#include <net/arp.h> +#include <net/net_namespace.h> + +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/time.h> +#include <linux/jiffies.h> + +/************************************************************************/ +/* 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 "<CR>*<address>*<key><encaps payload><CR>" + * 1 1 1-18 1 4 ? 1 + * eg. <CR>*0000-1234*SIP0<encaps payload><CR> + * We allow 31 bytes for the stars, the key, the address and the <CR>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 then 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) +{ + 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) +{ + 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 ch |