summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS4
-rw-r--r--drivers/net/wireless/Kconfig24
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/strip.c2804
4 files changed, 2833 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f92707fee59e..ec0c9c914f17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3908,6 +3908,10 @@ P: Ion Badulescu
M: ionut@cs.columbia.edu
S: Maintained
+STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
+W: http://mosquitonet.Stanford.EDU/strip.html
+S: Unsupported ?
+
STRADIS MPEG-2 DECODER DRIVER
P: Nathan Laredo
M: laredo@gnu.org
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index d5b006f5b86f..91fc2c765d90 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 2668934abbff..54a4f6f1db67 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 000000000000..883af891ebfb
--- /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 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);
+ }