summaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/8390.c1
-rw-r--r--drivers/net/Config.in22
-rw-r--r--drivers/net/Makefile18
-rw-r--r--drivers/net/README.wanpipe12
-rw-r--r--drivers/net/Space.c15
-rw-r--r--drivers/net/cops.c1018
-rw-r--r--drivers/net/cops.h60
-rw-r--r--drivers/net/cops_ffdrv.h533
-rw-r--r--drivers/net/cops_ltdrv.h242
-rw-r--r--drivers/net/ibmtr.c11
-rw-r--r--drivers/net/lapbether.c2
-rw-r--r--drivers/net/sdla_fr.c339
-rw-r--r--drivers/net/sdla_ppp.c142
-rw-r--r--drivers/net/sdla_x25.c174
-rw-r--r--drivers/net/sdlamain.c45
-rw-r--r--drivers/net/shaper.c22
-rw-r--r--drivers/net/shaper.h61
-rw-r--r--drivers/net/strip.c1940
-rw-r--r--drivers/net/sunhme.c17
-rw-r--r--drivers/net/sunqe.c14
-rw-r--r--drivers/net/tlan.c2309
-rw-r--r--drivers/net/tlan.h485
-rw-r--r--drivers/net/tulip.c1
23 files changed, 6277 insertions, 1206 deletions
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index fbf67cb42..1150ddcf4 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -186,6 +186,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
/* Mask interrupts from the ethercard. */
outb_p(0x00, e8390_base + EN0_IMR);
+ synchronize_irq();
if (dev->interrupt) {
printk("%s: Tx request while isr active.\n",dev->name);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index 23befe03c..bba3e43b8 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -84,6 +84,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
@@ -113,7 +114,12 @@ fi
#
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
if [ "$CONFIG_ATALK" != "n" ]; then
- tristate 'LocalTalk PC support' CONFIG_LTPC
+ tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC
+ tristate 'COPS LocalTalk PC support' CONFIG_COPS
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool 'Dayna firmware support' CONFIG_COPS_DAYNA
+ bool 'Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
fi
fi
@@ -146,12 +152,14 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then
bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
- if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then
- bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
- fi
- if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then
- bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
- fi
+# if [ -f doesn't work with xconfig. Enable it again when the drivers are really
+# included.
+# if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then
+# bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
+# fi
+# if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then
+# bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
+# fi
fi
fi
tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3a8721933..b149eac58 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -391,6 +391,14 @@ else
endif
endif
+ifeq ($(CONFIG_TLAN),y)
+L_OBJS += tlan.o
+else
+ ifeq ($(CONFIG_TLAN),m)
+ M_OBJS += tlan.o
+ endif
+endif
+
ifeq ($(CONFIG_ZNET),y)
L_OBJS += znet.o
endif
@@ -663,6 +671,14 @@ else
endif
endif
+ifeq ($(CONFIG_COPS),y)
+L_OBJS += cops.o
+else
+ ifeq ($(CONFIG_COPS),m)
+ M_OBJS += cops.o
+ endif
+endif
+
ifeq ($(CONFIG_BAYCOM),y)
L_OBJS += baycom.o
CONFIG_HDLCDRV_BUILTIN = y
@@ -785,7 +801,7 @@ dlci.o: dlci.c CONFIG
sdladrv.o: sdladrv.c CONFIG
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $^
+ ld -r -o $@ $(WANPIPE_OBJS)
sdlamain.o: sdlamain.c CONFIG
diff --git a/drivers/net/README.wanpipe b/drivers/net/README.wanpipe
index bcfed26fe..9650edb73 100644
--- a/drivers/net/README.wanpipe
+++ b/drivers/net/README.wanpipe
@@ -1,10 +1,10 @@
------------------------------------------------------------------------------
WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router
------------------------------------------------------------------------------
-Release 3.0.0
-December 31, 1996
+Release 3.1.0
+January 30, 1997
Author: Gene Kozin <genek@compuserve.com>
-Copyright (c) 1995-1996 Sangoma Technologies Inc.
+Copyright (c) 1995-1997 Sangoma Technologies Inc.
------------------------------------------------------------------------------
INTRODUCTION
@@ -81,6 +81,12 @@ include/linux:
REVISION HISTORY
+3.1.0 January 30, 1997
+
+ o Implemented IOCTL for executing adapter commands.
+ o Fixed a bug in frame relay code causing driver configured as a FR
+ switch to be stuck in WAN_DISCONNECTED mode.
+
3.0.0 December 31, 1996
o Uses Linux WAN Router interface
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index ce820df60..64bc62c41 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -89,6 +89,7 @@ extern int atarilance_probe(struct device *);
extern int a2065_probe(struct device *);
extern int ariadne_probe(struct device *);
extern int hydra_probe(struct device *);
+extern int tlan_probe(struct device *);
extern int cs89x0_probe(struct device *dev);
/* Detachable devices ("pocket adaptors") */
@@ -242,6 +243,9 @@ __initfunc(static int ethif_probe(struct device *dev))
#ifdef CONFIG_SUNLANCE
&& sparc_lance_probe(dev)
#endif
+#ifdef CONFIG_TLAN
+ && tlan_probe(dev)
+#endif
#ifdef CONFIG_HAPPYMEAL
&& happy_meal_probe(dev)
#endif
@@ -301,6 +305,17 @@ static struct device atp_dev = {
# define NEXT_DEV (&dev_ltpc)
#endif /* LTPC */
+#if defined(CONFIG_COPS)
+ extern int cops_probe(struct device *);
+ static struct device dev_cops = {
+ "lt0",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NEXT_DEV, cops_probe };
+# undef NEXT_DEV
+# define NEXT_DEV (&dev_cops)
+#endif /* COPS */
+
/* The first device defaults to I/O base '0', which means autoprobe. */
#ifndef ETH0_ADDR
# define ETH0_ADDR 0
diff --git a/drivers/net/cops.c b/drivers/net/cops.c
new file mode 100644
index 000000000..66e3c5fea
--- /dev/null
+++ b/drivers/net/cops.c
@@ -0,0 +1,1018 @@
+/* cops.c: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ *
+ * With more than a little help from;
+ * - Alan Cox <Alan.Cox@linux.org>
+ *
+ * Derived from:
+ * - skeleton.c: A network driver outline for linux.
+ * Written 1993-94 by Donald Becker.
+ * - ltpc.c: A driver for the LocalTalk PC card.
+ * Written by Bradford W. Johnson.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Changes:
+ * 19970608 Alan Cox Allowed dual card type support
+ * Can set board type in insmod
+ * Hooks for cops_setup routine
+ * (not yet implemented).
+ */
+
+static const char *version =
+ "cops.c:v0.01 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
+/*
+ * Sources:
+ * COPS Localtalk SDK. This provides almost all of the information
+ * needed.
+ */
+
+/*
+ * insmod/modprobe configurable stuff.
+ * - IO Port, choose one your card supports or 0 if you dare.
+ * - IRQ, also choose one your card supports or nothing and let
+ * the driver figure it out.
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/if_arp.h>
+#include <linux/if_ltalk.h> /* For ltalk_setup() */
+#include <linux/delay.h> /* For udelay() */
+#include <linux/atalk.h>
+
+#include "cops.h" /* Our Stuff */
+#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */
+#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static const char *cardname = "cops";
+
+#ifdef CONFIG_COPS_DAYNA
+static int board_type = DAYNA; /* Module exported */
+#else
+static int board_type = TANGENT;
+#endif
+
+#ifdef MODULE
+static int io = 0x240; /* Default IO for Dayna */
+static int irq = 5; /* Default IRQ */
+#else
+static int io = 0; /* Default IO for Dayna */
+static int irq = 0; /* Default IRQ */
+#endif
+
+/*
+ * COPS Autoprobe information.
+ * Right now if port address is right but IRQ is not 5 this will
+ * return a 5 no matter what since we will still get a status response.
+ * Need one more additional check to narrow down after we have gotten
+ * the ioaddr. But since only other possible IRQs is 3 and 4 so no real
+ * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with
+ * this driver.
+ *
+ * This driver has 2 modes and they are: Dayna mode and Tangent mode.
+ * Each mode corresponds with the type of card. It has been found
+ * that there are 2 main types of cards and all other cards are
+ * the same and just have different names or only have minor differences
+ * such as more IO ports. As this driver is tested it will
+ * become more clear on exactly what cards are supported. The driver
+ * defaults to using Dayna mode. To change the drivers mode adjust
+ * drivers/net/CONFIG, and the line COPS_OPTS = -DDAYNA to -DTANGENT.
+ *
+ * This driver should support:
+ * TANGENT driver mode:
+ * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
+ * DAYNA driver mode:
+ * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III
+ * Other cards possibly supported mode unkown though:
+ * Farallon PhoneNET PC II
+ * Dayna DL2000 (Full length)
+ *
+ * Cards NOT supported by this driver but supported by the ltpc.c
+ * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ * Farallon PhoneNET PC
+ * Original Apple LocalTalk PC card
+ */
+
+/*
+ * Zero terminated list of IO ports to probe.
+ */
+
+static unsigned int cops_portlist[] = {
+ 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
+ 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360,
+ 0
+};
+
+/*
+ * Zero terminated list of IRQ ports to probe.
+ */
+
+static int cops_irqlist[] = {
+ 5, 4, 3, 0
+};
+
+/* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */
+#ifndef COPS_DEBUG
+#define COPS_DEBUG 1
+#endif
+static unsigned int cops_debug = COPS_DEBUG;
+
+/* The number of low I/O ports used by the card. */
+#define COPS_IO_EXTENT 8
+
+/* Information that needs to be kept for each board. */
+
+struct cops_local
+{
+ struct enet_statistics stats;
+ int board; /* Holds what board type is. */
+ int nodeid; /* Set to 1 once have nodeid. */
+ unsigned char node_acquire; /* Node ID when acquired. */
+};
+
+/* Allocate a new device with the form of lt0, lt1, lt2, etc. */
+struct device *cops_dev_alloc(char *name)
+{
+ int i=0;
+ struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+
+ memset(d,0,sizeof(*d)); /* Clear the structure */
+ if(d==NULL)
+ return NULL;
+ d->name=(char *)(d+1); /* Name string space */
+
+ /* Get next free device name */
+ for(i=0;i<100;i++)
+ {
+ sprintf(d->name,name,i);
+ if(dev_get(d->name)==NULL)
+ return d;
+ }
+ return NULL; /* Over 100 of the things .. bail out! */
+}
+
+/* Index to functions, as function prototypes. */
+extern int cops_probe (struct device *dev);
+static int cops_probe1 (struct device *dev, int ioaddr);
+static int cops_irq (int ioaddr, int board);
+
+static int cops_open (struct device *dev);
+static int cops_jumpstart (struct device *dev);
+static void cops_reset (struct device *dev, int sleep);
+static void cops_load (struct device *dev);
+static int cops_nodeid (struct device *dev, int nodeid);
+
+static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void cops_rx (struct device *dev);
+static int cops_send_packet (struct sk_buff *skb, struct device *dev);
+static void set_multicast_list (struct device *dev);
+static int cops_hard_header (struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len);
+
+static int cops_ioctl (struct device *dev, struct ifreq *rq, int cmd);
+static int cops_close (struct device *dev);
+static struct enet_statistics *cops_get_stats (struct device *dev);
+
+
+/*
+ * Check for a network adaptor of this type, and return '0' iff one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+int cops_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr == 0 && io)
+ base_addr=io;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return cops_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i=0; cops_portlist[i]; i++) {
+ int ioaddr = cops_portlist[i];
+ if (check_region(ioaddr, COPS_IO_EXTENT))
+ continue;
+ if (cops_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ /* No "lt" devices found. */
+ printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name);
+ return -ENODEV;
+}
+
+/*
+ * 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.
+ */
+static int cops_probe1(struct device *dev, int ioaddr)
+{
+ struct cops_local *lp;
+ static unsigned version_printed = 0;
+ int irqaddr = 0;
+ int irqval;
+
+ int board = board_type;
+
+/* Defined here to save some trouble */
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ {
+ dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */
+ if(dev==NULL)
+ return -ENOMEM;
+ }
+
+ if (cops_debug && version_printed++ == 0)
+ printk("%s", version);
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ /*
+ * Since this board has jumpered interrupts, allocate the interrupt
+ * vector now. There is no point in waiting since no other device
+ * can use the interrupt, and this marks the irq as busy. Jumpered
+ * interrupts are typically not reported by the boards, and we must
+ * used AutoIRQ to find them.
+ *
+ */
+
+ if (dev->irq < 2 && irq)
+ dev->irq = irq;
+
+ if (dev->irq < 2)
+ {
+ irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */
+ if (irqaddr == 0)
+ return -EAGAIN; /* No IRQ found on this port */
+ else
+ dev->irq = irqaddr;
+ }
+ else if (dev->irq == 2)
+ /*
+ * Fixup for users that don't know that IRQ 2 is really
+ * IRQ 9, or don't know which one to set.
+ */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. */
+ irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, NULL);
+ if (irqval)
+ {
+ printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval);
+ return -EAGAIN;
+ }
+
+ dev->hard_start_xmit = &cops_send_packet;
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+
+ lp = (struct cops_local *)dev->priv;
+ memset(lp, 0, sizeof(struct cops_local));
+
+ /* Copy local board variable to lp struct. */
+ lp->board = board;
+
+ /* Tell the user where the card is and what mode were in. */
+ if(board==DAYNA)
+ printk("%s: %s found at %#3x, using IRQ %d, in Dayna mode.\n",
+ dev->name, cardname, ioaddr, dev->irq);
+ if(board==TANGENT)
+ printk("%s: %s found at %#3x, using IRQ %d, in Tangent mode.\n",
+ dev->name, cardname, ioaddr, dev->irq);
+
+ /* Grab the region so no one else tries to probe our ioports. */
+ request_region(ioaddr, COPS_IO_EXTENT, cardname);
+
+ /* Fill in the fields of the device structure with LocalTalk values. */
+ ltalk_setup(dev);
+
+ dev->hard_header = cops_hard_header;
+ dev->get_stats = cops_get_stats;
+ dev->open = cops_open;
+ dev->stop = cops_close;
+ dev->do_ioctl = &cops_ioctl;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->mc_list = NULL;
+
+ return 0;
+}
+
+static int cops_irq (int ioaddr, int board)
+{ /*
+ * This does not use the IRQ to determine where the IRQ is. We just
+ * assume that when we get a correct status response that is the IRQ then.
+ * This really just verifies the IO port but since we only have access
+ * to such a small number of IRQs (5, 4, 3) this is not bad.
+ * This will probably not work for more than one card.
+ */
+ int irqaddr=0;
+ int i, x, status;
+
+ if(board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET);
+ inb(ioaddr+DAYNA_RESET);
+ udelay(333333);
+ }
+ if(board==TANGENT)
+ {
+ inb(ioaddr);
+ outb(0, ioaddr);
+ outb(0, ioaddr+TANG_RESET);
+ }
+
+ for(i=0; cops_irqlist[i] !=0; i++)
+ {
+ irqaddr = cops_irqlist[i];
+ for(x = 0xFFFF; x>0; x --) /* wait for response */
+ {
+ if(board==DAYNA)
+ {
+ status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
+ if (status == 1)
+ return irqaddr;
+ }
+ if(board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0)
+ return irqaddr;
+ }
+ }
+ }
+ return 0; /* no IRQ found */
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ */
+static int cops_open(struct device *dev)
+{
+ irq2dev_map[dev->irq] = dev;
+
+ cops_jumpstart(dev); /* Start the card up. */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * This allows for a dynamic start/restart of the entire card.
+ */
+static int cops_jumpstart(struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+
+ /*
+ * Once the card has the firmware loaded and has acquired
+ * the nodeid, if it is reset it will lose it all.
+ */
+ cops_reset(dev,1); /* Need to reset card before load firmware. */
+ cops_load(dev); /* Load the firmware. */
+
+ /*
+ * If atalkd already gave us a nodeid we will use that
+ * one again, else we wait for atalkd to give us a nodeid
+ * in cops_ioctl. This may cause a problem if someone steals
+ * our nodeid while we are resetting.
+ */
+ if(lp->nodeid == 1)
+ cops_nodeid(dev,lp->node_acquire);
+
+ return 0;
+}
+
+static int tangent_wait_reset(int ioaddr)
+{
+ int timeout=0;
+
+ while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ udelay(1000); /* Wait 1000 useconds */
+
+ return 0;
+}
+
+/*
+ * Reset the LocalTalk board.
+ */
+static void cops_reset(struct device *dev, int sleep)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+
+ if(lp->board==TANGENT)
+ {
+ inb(ioaddr); /* Clear request latch. */
+ outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */
+ outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */
+
+ /* Can take 5 seconds max - youch! */
+ if(sleep)
+ {
+ long snapt=jiffies;
+ while(jiffies-snapt<5*HZ)
+ {
+ if(inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)
+ break;
+ schedule();
+ }
+ }
+ else
+ tangent_wait_reset(ioaddr);
+ outb(0, ioaddr+TANG_CLEAR_INT);
+ }
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */
+ inb(ioaddr+DAYNA_RESET); /* Clear the reset */
+ if(sleep)
+ {
+ long snap=jiffies;
+
+ /* Let card finish initializing, about 1/3 second */
+ while(jiffies-snap<HZ/3)
+ schedule();
+ }
+ else
+ udelay(333333);
+ }
+ dev->tbusy=0;
+
+ return;
+}
+
+static void cops_load (struct device *dev)
+{
+ struct ifreq ifr;
+ struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data;
+ struct cops_local *lp=(struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ int length, i = 0;
+
+ strcpy(ifr.ifr_name,"lt0");
+
+ /* Get card's firmware code and do some checks on it. */
+#ifdef CONFIG_COPS_DAYNA
+ if (lp->board==DAYNA)
+ {
+ ltf->length=sizeof(ffdrv_code);
+ ltf->data=ffdrv_code;
+ }
+ else
+#endif
+#ifdef CONFIG_COPS_TANGENT
+ if (lp->board==TANGENT)
+ {
+ ltf->length=sizeof(ltdrv_code);
+ ltf->data=ltdrv_code;
+ }
+ else
+#endif
+ {
+ printk(KERN_INFO "%s; unsupported board type.\n", dev->name);
+ return;
+ }
+
+ /* Check to make sure firmware is correct length. */
+ if(lp->board==DAYNA && ltf->length!=5983)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name);
+ return;
+ }
+ if(lp->board==TANGENT && ltf->length!=2501)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name);
+ return;
+ }
+
+ if(lp->board==DAYNA)
+ {
+ /*
+ * We must wait for a status response
+ * with the DAYNA board.
+ */
+ while(++i<65536)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1)
+ break;
+ }
+
+ if(i==65536)
+ return;
+ }
+
+ /*
+ * Upload the firmware and kick. Byte-by-byte works nicely here.
+ */
+ i=0;
+ length = ltf->length;
+ while(length--)
+ {
+ outb(ltf->data[i], ioaddr);
+ i++;
+ }
+
+ if(cops_debug > 1)
+ printk(KERN_DEBUG "%s: Uploaded firmware - %d bytes of %d bytes.\n", dev->name, i, ltf->length);
+
+ if(lp->board==DAYNA)
+ outb(1, ioaddr+DAYNA_INT_CARD); /* Tell Dayna to run the firmware code. */
+ else
+ inb(ioaddr); /* Tell Tang to run the firmware code. */
+
+ if(lp->board==TANGENT)
+ {
+ tangent_wait_reset(ioaddr);
+ inb(ioaddr); /* Clear initial ready signal. */
+ }
+
+ return;
+}
+
+/*
+ * Get the LocalTalk Nodeid from the card. We can suggest
+ * any nodeid 1-254. The card will try and get that exact
+ * address else we can specify 0 as the nodeid and the card
+ * will autoprobe for a nodeid.
+ */
+static int cops_nodeid (struct device *dev, int nodeid)
+{
+ struct cops_local *lp = (struct cops_local *) dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (lp->board == DAYNA)
+ {
+ /* Empty any pending adapter responses. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Kick out any packet waiting. */
+ schedule();
+ }
+
+ outb(2, ioaddr); /* Output command packet length as 2. */
+ outb(0, ioaddr);
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
+ outb(nodeid, ioaddr); /* Suggest node address. */
+ }
+
+ if (lp->board == TANGENT)
+ {
+ /* Empty any pending adapter responses. */
+ while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+ cops_rx(dev); /* Kick out any packet waiting. */
+ schedule();
+ }
+
+ /* Not sure what Tangent does if random nodeid we picked is already used. */
+ if(nodeid == 0) /* Seed. */
+ nodeid = jiffies&0xFF; /* Get a random try .*/
+ outb(2, ioaddr); /* Command length LSB. */
+ outb(0, ioaddr); /* Command length MSB. */
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
+ outb(nodeid, ioaddr); /* LAP address hint. */
+ outb(0xFF, ioaddr); /* Interrupt level to use (NONE). */
+ }
+
+ lp->node_acquire=0; /* Set nodeid holder to 0. */
+ while(lp->node_acquire==0) /* Get *True* nodeid finally. */
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+
+ if(lp->board == DAYNA)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ if(lp->board == TANGENT)
+ {
+ if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ schedule();
+ }
+
+ if(cops_debug > 1)
+ printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", dev->name, lp->node_acquire);
+
+ lp->nodeid=1; /* Set got nodeid to 1. */
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct cops_local *lp;
+ int ioaddr, status;
+ int boguscount = 0;
+
+ if (dev == NULL)
+ {
+ printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct cops_local *)dev->priv;
+
+ do
+ {
+ /* Clear any interrupt. */
+ outb(0, ioaddr + COPS_CLEAR_INT);
+
+ if(lp->board==DAYNA)
+ {
+ status=inb(ioaddr+DAYNA_CARD_STATUS);
+ if((status&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev);
+ }
+ else
+ {
+ status=inb(ioaddr+TANG_CARD_STATUS);
+ if (status&TANG_RX_READY)
+ cops_rx(dev);
+ }
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ } while (++boguscount < 20 );
+ dev->interrupt = 0;
+
+ return;
+}
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ */
+static void cops_rx(struct device *dev)
+{
+ int pkt_len = 0;
+ int rsp_type = 0;
+ struct sk_buff *skb;
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 0;
+
+ cli(); /* Disable interrupts. */
+
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr); /* Send out Zero length. */
+ outb(0, ioaddr);
+ outb(DATA_READ, ioaddr); /* Send read command out. */
+
+ /* Wait for DMA to turn around. */
+ while(++boguscount<1000000)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY)
+ break;
+ }
+
+ if(boguscount==1000000)
+ {
+ printk(KERN_WARNING "%s: DMA timed out.\n",dev->name);
+ return;
+ }
+ }
+
+ /* Get response length. */
+ pkt_len = inb(ioaddr) & 0xFF;
+ pkt_len |= (inb(ioaddr) << 8);
+ /* Input IO code. */
+ rsp_type=inb(ioaddr);
+
+ /* Malloc up new buffer. */
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL)
+ {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ while(pkt_len--) /* Discard packet */
+ inb(ioaddr);
+ return;
+ }
+ skb->dev = dev;
+ skb_put(skb, pkt_len);
+ skb->protocol = htons(ETH_P_LOCALTALK);
+
+ insb(ioaddr, skb->data, pkt_len); /* Eat the Data */
+
+ if(lp->board==DAYNA)
+ outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card. */
+
+ sti(); /* Restore interrupts. */
+
+ /* Check for bad response length */
+ if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
+ {
+ printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len);
+ lp->stats.tx_errors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* Set nodeid and then get out. */
+ if(rsp_type == LAP_INIT_RSP)
+ {
+ lp->node_acquire = skb->data[0]; /* Nodeid taken from received packet. */
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* One last check to make sure we have a good packet. */
+ if(rsp_type != LAP_RESPONSE)
+ {
+ printk("%s: Bad packet type %d.\n", dev->name, rsp_type);
+ lp->stats.tx_errors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ skb->mac.raw = skb->data; /* Point to entire packet. */
+ skb_pull(skb,3);
+ skb->h.raw = skb->data; /* Point to just the data (Skip header). */
+
+ /* Update the counters. */
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+
+ /* Send packet to a higher place. */
+ netif_rx(skb);
+
+ return;
+}
+
+/*
+ * Make the card transmit a LocalTalk packet.
+ */
+static int cops_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy)
+ {
+ /*
+ * If we get here, some higher level has decided we are broken.
+ * There should really be a "kick me" function call instead.
+ */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ lp->stats.tx_errors++;
+ if(lp->board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name);
+ }
+ printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name);
+ cops_jumpstart(dev); /* Restart the card. */
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping. This could better be
+ * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (test_and_set_bit(0, (void*) &dev->tbusy) != 0)
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ cli(); /* Disable interrupts. */
+ if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+ if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0);
+
+ /* Output IO length. */
+ if(lp->board == DAYNA)
+ {
+ outb(skb->len, ioaddr);
+ outb(skb->len >> 8, ioaddr);
+ }
+ else
+ {
+ outb(skb->len&0x0FF, ioaddr);
+ outb((skb->len >> 8)&0x0FF, ioaddr);
+ }
+
+ /* Output IO code. */
+ outb(LAP_WRITE, ioaddr);
+
+ if(lp->board == DAYNA) /* Check the transmit buffer again. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+
+ outsb(ioaddr, skb->data, skb->len); /* Send out the data. */
+
+ if(lp->board==DAYNA) /* The Dayna requires you kick the card. */
+ outb(1, ioaddr+DAYNA_INT_CARD);
+
+ sti(); /* Restore interrupts. */
+
+ /* Done sending packet, update counters and cleanup. */
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+/*
+ * Dummy function to keep the Appletalk layer happy.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ if(cops_debug >= 3)
+ printk("%s: set_mulicast_list executed. NeatO.\n", dev->name);
+}
+
+/*
+ * Another Dummy function to keep the Appletalk layer happy.
+ */
+
+static int cops_hard_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ if(cops_debug >= 3)
+ printk("%s: cops_hard_header executed. Wow!\n", dev->name);
+ return 0;
+}
+
+/*
+ * System ioctls for the COPS LocalTalk card.
+ */
+
+static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr;
+ struct at_addr *aa=(struct at_addr *)&dev->pa_addr;
+
+ switch(cmd)
+ {
+ case SIOCSIFADDR:
+ /* Get and set the nodeid and network # atalkd wants. */
+ cops_nodeid(dev, sa->sat_addr.s_node);
+ aa->s_net = sa->sat_addr.s_net;
+ aa->s_node = lp->node_acquire;
+
+ /* Set broardcast address. */
+ dev->broadcast[0] = 0xFF;
+
+ /* Set hardware address. */
+ dev->dev_addr[0] = aa->s_node;
+ dev->addr_len = 1;
+ return 0;
+
+ case SIOCGIFADDR:
+ sa->sat_addr.s_net = aa->s_net;
+ sa->sat_addr.s_node = aa->s_node;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * The inverse routine to cops_open().
+ */
+
+static int cops_close(struct device *dev)
+{
+ dev->tbusy = 1;
+ dev->start = 0;
+ irq2dev_map[dev->irq] = 0;
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct enet_statistics *cops_get_stats(struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ return &lp->stats;
+}
+
+#ifdef MODULE
+static struct device dev_cops =
+{
+ "lt0", /* device name */
+ 0, 0, 0, 0,
+ 0x0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, cops_probe
+};
+
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(board_type, "i");
+
+int init_module(void)
+{
+ int result;
+
+ if (io == 0)
+ printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname);
+
+ /* Copy the parameters from insmod into the device structure. */
+ dev_cops.base_addr = io;
+ dev_cops.irq = irq;
+
+ if ((result = register_netdev(&dev_cops)) != 0)
+ return result;
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+
+ free_irq(dev_cops.irq, NULL);
+ release_region(dev_cops.base_addr, COPS_IO_EXTENT);
+ unregister_netdev(&dev_cops);
+
+ if (dev_cops.priv)
+ kfree_s(dev_cops.priv, sizeof(struct cops_local));
+}
+#endif /* MODULE */
diff --git a/drivers/net/cops.h b/drivers/net/cops.h
new file mode 100644
index 000000000..a064bc12b
--- /dev/null
+++ b/drivers/net/cops.h
@@ -0,0 +1,60 @@
+/* cops.h: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#ifndef __LINUX_COPSLTALK_H
+#define __LINUX_COPSLTALK_H
+
+#ifdef __KERNEL__
+
+/* Max LLAP size we will accept. */
+#define MAX_LLAP_SIZE 603
+
+/* Tangent */
+#define TANG_CARD_STATUS 1
+#define TANG_CLEAR_INT 1
+#define TANG_RESET 3
+
+#define TANG_TX_READY 1
+#define TANG_RX_READY 2
+
+/* Dayna */
+#define DAYNA_CMD_DATA 0
+#define DAYNA_CLEAR_INT 1
+#define DAYNA_CARD_STATUS 2
+#define DAYNA_INT_CARD 3
+#define DAYNA_RESET 4
+
+#define DAYNA_RX_READY 0
+#define DAYNA_TX_READY 1
+#define DAYNA_RX_REQUEST 3
+
+/* Same on both card types */
+#define COPS_CLEAR_INT 1
+
+/* LAP response codes recieved from the cards. */
+#define LAP_INIT 1 /* Init cmd */
+#define LAP_INIT_RSP 2 /* Init response */
+#define LAP_WRITE 3 /* Write cmd */
+#define DATA_READ 4 /* Data read */
+#define LAP_RESPONSE 4 /* Received ALAP frame response */
+#define LAP_GETSTAT 5 /* Get LAP and HW status */
+#define LAP_RSPSTAT 6 /* Status response */
+
+#endif
+
+/*
+ * Structure to hold the firmware information.
+ */
+struct ltfirmware
+{
+ unsigned int length;
+ unsigned char * data;
+};
+
+#define DAYNA 1
+#define TANGENT 2
+
+#endif
diff --git a/drivers/net/cops_ffdrv.h b/drivers/net/cops_ffdrv.h
new file mode 100644
index 000000000..d3e337afc
--- /dev/null
+++ b/drivers/net/cops_ffdrv.h
@@ -0,0 +1,533 @@
+
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * seperate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_DAYNA
+
+unsigned char ffdrv_code[] = {
+ 58,3,0,50,228,149,33,255,255,34,226,149,
+ 249,17,40,152,33,202,154,183,237,82,77,68,
+ 11,107,98,19,54,0,237,176,175,50,80,0,
+ 62,128,237,71,62,32,237,57,51,62,12,237,
+ 57,50,237,57,54,62,6,237,57,52,62,12,
+ 237,57,49,33,107,137,34,32,128,33,83,130,
+ 34,40,128,33,86,130,34,42,128,33,112,130,
+ 34,36,128,33,211,130,34,38,128,62,0,237,
+ 57,16,33,63,148,34,34,128,237,94,205,15,
+ 130,251,205,168,145,24,141,67,111,112,121,114,
+ 105,103,104,116,32,40,67,41,32,49,57,56,
+ 56,32,45,32,68,97,121,110,97,32,67,111,
+ 109,109,117,110,105,99,97,116,105,111,110,115,
+ 32,32,32,65,108,108,32,114,105,103,104,116,
+ 115,32,114,101,115,101,114,118,101,100,46,32,
+ 32,40,68,40,68,7,16,8,34,7,22,6,
+ 16,5,12,4,8,3,6,140,0,16,39,128,
+ 0,4,96,10,224,6,0,7,126,2,64,11,
+ 118,12,6,13,0,14,193,15,0,5,96,3,
+ 192,1,64,9,8,62,9,211,66,62,192,211,
+ 66,62,100,61,32,253,6,28,33,205,129,14,
+ 66,237,163,194,253,129,6,28,33,205,129,14,
+ 64,237,163,194,9,130,201,62,47,50,71,152,
+ 62,47,211,68,58,203,129,237,57,20,58,204,
+ 129,237,57,21,33,77,152,54,132,205,233,129,
+ 58,228,149,254,209,40,6,56,4,62,0,24,
+ 2,219,96,33,233,149,119,230,62,33,232,149,
+ 119,213,33,8,152,17,7,0,25,119,19,25,
+ 119,209,201,251,237,77,245,197,213,229,221,229,
+ 205,233,129,62,1,50,106,137,205,158,139,221,
+ 225,225,209,193,241,251,237,77,245,197,213,219,
+ 72,237,56,16,230,46,237,57,16,237,56,12,
+ 58,72,152,183,32,26,6,20,17,128,2,237,
+ 56,46,187,32,35,237,56,47,186,32,29,219,
+ 72,230,1,32,3,5,32,232,175,50,72,152,
+ 229,221,229,62,1,50,106,137,205,158,139,221,
+ 225,225,24,25,62,1,50,72,152,58,201,129,
+ 237,57,12,58,202,129,237,57,13,237,56,16,
+ 246,17,237,57,16,209,193,241,251,237,77,245,
+ 197,229,213,221,229,237,56,16,230,17,237,57,
+ 16,237,56,20,58,34,152,246,16,246,8,211,
+ 68,62,6,61,32,253,58,34,152,246,8,211,
+ 68,58,203,129,237,57,20,58,204,129,237,57,
+ 21,237,56,16,246,34,237,57,16,221,225,209,
+ 225,193,241,251,237,77,33,2,0,57,126,230,
+ 3,237,100,1,40,2,246,128,230,130,245,62,
+ 5,211,64,241,211,64,201,229,213,243,237,56,
+ 16,230,46,237,57,16,237,56,12,251,70,35,
+ 35,126,254,175,202,77,133,254,129,202,15,133,
+ 230,128,194,191,132,43,58,44,152,119,33,76,
+ 152,119,35,62,132,119,120,254,255,40,4,58,
+ 49,152,119,219,72,43,43,112,17,3,0,237,
+ 56,52,230,248,237,57,52,219,72,230,1,194,
+ 141,131,209,225,237,56,52,246,6,237,57,52,
+ 62,1,55,251,201,62,3,211,66,62,192,211,
+ 66,62,48,211,66,0,0,219,66,230,1,40,
+ 4,219,67,24,240,205,203,135,58,75,152,254,
+ 255,202,128,132,58,49,152,254,161,250,207,131,
+ 58,34,152,211,68,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,24,0,62,
+ 14,211,66,62,33,211,66,62,1,211,66,62,
+ 64,211,66,62,3,211,66,62,209,211,66,62,
+ 100,71,219,66,230,1,32,6,5,32,247,195,
+ 248,132,219,67,71,58,44,152,184,194,248,132,
+ 62,100,71,219,66,230,1,32,6,5,32,247,
+ 195,248,132,219,67,62,100,71,219,66,230,1,
+ 32,6,5,32,247,195,248,132,219,67,254,133,
+ 32,7,62,0,50,74,152,24,17,254,173,32,
+ 7,62,1,50,74,152,24,6,254,141,194,248,
+ 132,71,209,225,58,49,152,254,132,32,10,62,
+ 50,205,2,134,205,144,135,24,27,254,140,32,
+ 15,62,110,205,2,134,62,141,184,32,5,205,
+ 144,135,24,8,62,10,205,2,134,205,8,134,
+ 62,1,50,106,137,205,158,139,237,56,52,246,
+ 6,237,57,52,175,183,251,201,62,20,135,237,
+ 57,20,175,237,57,21,237,56,16,246,2,237,
+ 57,16,237,56,20,95,237,56,21,123,254,10,
+ 48,244,237,56,16,230,17,237,57,16,209,225,
+ 205,144,135,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,183,251,201,209,
+ 225,243,219,72,230,1,40,13,62,10,211,66,
+ 0,0,219,66,230,192,202,226,132,237,56,52,
+ 246,6,237,57,52,62,1,55,251,201,205,203,
+ 135,62,1,50,106,137,205,158,139,237,56,52,
+ 246,6,237,57,52,183,251,201,209,225,62,1,
+ 50,106,137,205,158,139,237,56,52,246,6,237,
+ 57,52,62,2,55,251,201,209,225,243,219,72,
+ 230,1,202,213,132,62,10,211,66,0,0,219,
+ 66,230,192,194,213,132,229,62,1,50,106,137,
+ 42,40,152,205,65,143,225,17,3,0,205,111,
+ 136,62,6,211,66,58,44,152,211,66,237,56,
+ 52,246,6,237,57,52,183,251,201,209,197,237,
+ 56,52,230,248,237,57,52,219,72,230,1,32,
+ 15,193,225,237,56,52,246,6,237,57,52,62,
+ 1,55,251,201,14,23,58,37,152,254,0,40,
+ 14,14,2,254,1,32,5,62,140,119,24,3,
+ 62,132,119,43,43,197,205,203,135,193,62,1,
+ 211,66,62,64,211,66,62,3,211,66,62,193,
+ 211,66,62,100,203,39,71,219,66,230,1,32,
+ 6,5,32,247,195,229,133,33,238,151,219,67,
+ 71,58,44,152,184,194,229,133,119,62,100,71,
+ 219,66,230,1,32,6,5,32,247,195,229,133,
+ 219,67,35,119,13,32,234,193,225,62,1,50,
+ 106,137,205,158,139,237,56,52,246,6,237,57,
+ 52,175,183,251,201,33,234,151,35,35,62,255,
+ 119,193,225,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,251,201,243,61,
+ 32,253,251,201,62,3,211,66,62,192,211,66,
+ 58,49,152,254,140,32,19,197,229,213,17,181,
+ 129,33,185,129,1,2,0,237,176,209,225,193,
+ 24,27,229,213,33,187,129,58,49,152,230,15,
+ 87,30,2,237,92,25,17,181,129,126,18,19,
+ 35,126,18,209,225,58,34,152,246,8,211,68,
+ 58,49,152,254,165,40,14,254,164,40,10,62,
+ 10,211,66,62,224,211,66,24,25,58,74,152,
+ 254,0,40,10,62,10,211,66,62,160,211,66,
+ 24,8,62,10,211,66,62,128,211,66,62,11,
+ 211,66,62,6,211,66,205,147,143,62,5,211,
+ 66,62,224,211,66,62,5,211,66,62,96,211,
+ 66,62,5,61,32,253,62,5,211,66,62,224,
+ 211,66,62,14,61,32,253,62,5,211,66,62,
+ 233,211,66,62,128,211,66,58,181,129,61,32,
+ 253,62,1,211,66,62,192,211,66,1,254,19,
+ 237,56,46,187,32,6,13,32,247,195,226,134,
+ 62,192,211,66,0,0,219,66,203,119,40,250,
+ 219,66,203,87,40,250,243,237,56,16,230,17,
+ 237,57,16,237,56,20,251,62,5,211,66,62,
+ 224,211,66,58,182,129,61,32,253,229,33,181,
+ 129,58,183,129,203,63,119,35,58,184,129,119,
+ 225,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,62,47,211,68,62,5,211,
+ 66,62,233,211,66,58,181,129,61,32,253,62,
+ 5,211,66,62,224,211,66,58,182,129,61,32,
+ 253,62,5,211,66,62,96,211,66,201,229,213,
+ 58,50,152,230,15,87,30,2,237,92,33,187,
+ 129,25,17,181,129,126,18,35,19,126,18,209,
+ 225,58,71,152,246,8,211,68,58,50,152,254,
+ 165,40,14,254,164,40,10,62,10,211,66,62,
+ 224,211,66,24,8,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,195,248,135,
+ 62,3,211,66,62,192,211,66,197,229,213,17,
+ 181,129,33,183,129,1,2,0,237,176,209,225,
+ 193,62,47,211,68,62,10,211,66,62,224,211,
+ 66,62,11,211,66,62,118,211,66,62,1,211,
+ 66,62,0,211,66,205,147,143,195,16,136,62,
+ 3,211,66,62,192,211,66,197,229,213,17,181,
+ 129,33,183,129,1,2,0,237,176,209,225,193,
+ 62,47,211,68,62,10,211,66,62,224,211,66,
+ 62,11,211,66,62,118,211,66,205,147,143,62,
+ 5,211,66,62,224,211,66,62,5,211,66,62,
+ 96,211,66,62,5,61,32,253,62,5,211,66,
+ 62,224,211,66,62,14,61,32,253,62,5,211,
+ 66,62,233,211,66,62,128,211,66,58,181,129,
+ 61,32,253,62,1,211,66,62,192,211,66,1,
+ 254,19,237,56,46,187,32,6,13,32,247,195,
+ 88,136,62,192,211,66,0,0,219,66,203,119,
+ 40,250,219,66,203,87,40,250,62,5,211,66,
+ 62,224,211,66,58,182,129,61,32,253,62,5,
+ 211,66,62,96,211,66,201,197,14,67,6,0,
+ 62,3,211,66,62,192,211,66,62,48,211,66,
+ 0,0,219,66,230,1,40,4,219,67,24,240,
+ 62,5,211,66,62,233,211,66,62,128,211,66,
+ 58,181,129,61,32,253,237,163,29,62,192,211,
+ 66,219,66,230,4,40,250,237,163,29,32,245,
+ 219,66,230,4,40,250,62,255,71,219,66,230,
+ 4,40,3,5,32,247,219,66,230,4,40,250,
+ 62,5,211,66,62,224,211,66,58,182,129,61,
+ 32,253,62,5,211,66,62,96,211,66,58,71,
+ 152,254,1,202,18,137,62,16,211,66,62,56,
+ 211,66,62,14,211,66,62,33,211,66,62,1,
+ 211,66,62,248,211,66,237,56,48,246,153,230,
+ 207,237,57,48,62,3,211,66,62,221,211,66,
+ 193,201,58,71,152,211,68,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,62,14,
+ 211,66,62,33,211,66,62,1,211,66,62,248,
+ 211,66,237,56,48,246,145,246,8,230,207,237,
+ 57,48,62,3,211,66,62,221,211,66,193,201,
+ 44,3,1,0,70,69,1,245,197,213,229,175,
+ 50,72,152,237,56,16,230,46,237,57,16,237,
+ 56,12,62,1,211,66,0,0,219,66,95,230,
+ 160,32,3,195,20,139,123,230,96,194,72,139,
+ 62,48,211,66,62,1,211,66,62,64,211,66,
+ 237,91,40,152,205,207,143,25,43,55,237,82,
+ 218,70,139,34,42,152,98,107,58,44,152,190,
+ 194,210,138,35,35,62,130,190,194,200,137,62,
+ 1,50,48,152,62,175,190,202,82,139,62,132,
+ 190,32,44,50,50,152,62,47,50,71,152,229,
+ 175,50,106,137,42,40,152,205,65,143,225,54,
+ 133,43,70,58,44,152,119,43,112,17,3,0,
+ 62,10,205,2,134,205,111,136,195,158,138,62,
+ 140,190,32,19,50,50,152,58,233,149,230,4,
+ 202,222,138,62,1,50,71,152,195,219,137,126,
+ 254,160,250,185,138,254,166,242,185,138,50,50,
+ 152,43,126,35,229,213,33,234,149,95,22,0,
+ 25,126,254,132,40,18,254,140,40,14,58,50,
+ 152,230,15,87,126,31,21,242,65,138,56,2,
+ 175,119,58,50,152,230,15,87,58,233,149,230,
+ 62,31,21,242,85,138,218,98,138,209,225,195,
+ 20,139,58,50,152,33,100,137,230,15,95,22,
+ 0,25,126,50,71,152,209,225,58,50,152,254,
+ 164,250,135,138,58,73,152,254,0,40,4,54,
+ 173,24,2,54,133,43,70,58,44,152,119,43,
+ 112,17,3,0,205,70,135,175,50,106,137,205,
+ 208,139,58,199,129,237,57,12,58,200,129,237,
+ 57,13,237,56,16,246,17,237,57,16,225,209,
+ 193,241,251,237,77,62,129,190,194,227,138,54,
+ 130,43,70,58,44,152,119,43,112,17,3,0,
+ 205,144,135,195,20,139,35,35,126,254,132,194,
+ 227,138,175,50,106,137,205,158,139,24,42,58,
+ 201,154,254,1,40,7,62,1,50,106,137,24,
+ 237,58,106,137,254,1,202,222,138,62,128,166,
+ 194,222,138,221,229,221,33,67,152,205,127,142,
+ 205,109,144,221,225,225,209,193,241,251,237,77,
+ 58,106,137,254,1,202,44,139,58,50,152,254,
+ 164,250,44,139,58,73,152,238,1,50,73,152,
+ 221,229,221,33,51,152,205,127,142,221,225,62,
+ 1,50,106,137,205,158,139,195,13,139,24,208,
+ 24,206,24,204,230,64,40,3,195,20,139,195,
+ 20,139,43,126,33,8,152,119,35,58,44,152,
+ 119,43,237,91,35,152,205,203,135,205,158,139,
+ 195,13,139,175,50,78,152,62,3,211,66,62,
+ 192,211,66,201,197,33,4,0,57,126,35,102,
+ 111,62,1,50,106,137,219,72,205,141,139,193,
+ 201,62,1,50,78,152,34,40,152,54,0,35,
+ 35,54,0,195,163,139,58,78,152,183,200,229,
+ 33,181,129,58,183,129,119,35,58,184,129,119,
+ 225,62,47,211,68,62,14,211,66,62,193,211,
+ 66,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,195,3,140,58,78,152,183,
+ 200,58,71,152,211,68,254,69,40,4,254,70,
+ 32,17,58,73,152,254,0,40,10,62,10,211,
+ 66,62,160,211,66,24,8,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,219,66,
+ 230,1,40,4,219,67,24,240,62,14,211,66,
+ 62,33,211,66,42,40,152,205,65,143,62,1,
+ 211,66,62,248,211,66,237,56,48,246,145,246,
+ 8,230,207,237,57,48,62,3,211,66,62,221,
+ 211,66,201,62,16,211,66,62,56,211,66,62,
+ 48,211,66,0,0,219,66,230,1,40,4,219,
+ 67,24,240,62,14,211,66,62,33,211,66,62,
+ 1,211,66,62,248,211,66,237,56,48,246,153,
+ 230,207,237,57,48,62,3,211,66,62,221,211,
+ 66,201,229,213,33,234,149,95,22,0,25,126,
+ 254,132,40,4,254,140,32,2,175,119,123,209,
+ 225,201,6,8,14,0,31,48,1,12,16,250,
+ 121,201,33,4,0,57,94,35,86,33,2,0,
+ 57,126,35,102,111,221,229,34,89,152,237,83,
+ 91,152,221,33,63,152,205,127,142,58,81,152,
+ 50,82,152,58,80,152,135,50,80,152,205,162,
+ 140,254,3,56,16,58,81,152,135,60,230,15,
+ 50,81,152,175,50,80,152,24,23,58,79,152,
+ 205,162,140,254,3,48,13,58,81,152,203,63,
+ 50,81,152,62,255,50,79,152,58,81,152,50,
+ 82,152,58,79,152,135,50,79,152,62,32,50,
+ 83,152,50,84,152,237,56,16,230,17,237,57,
+ 16,219,72,62,192,50,93,152,62,93,50,94,
+ 152,58,93,152,61,50,93,152,32,9,58,94,
+ 152,61,50,94,152,40,44,62,170,237,57,20,
+ 175,237,57,21,237,56,16,246,2,237,57,16,
+ 219,72,230,1,202,29,141,237,56,20,71,237,
+ 56,21,120,254,10,48,237,237,56,16,230,17,
+ 237,57,16,243,62,14,211,66,62,65,211,66,
+ 251,58,39,152,23,23,60,50,39,152,71,58,
+ 82,152,160,230,15,40,22,71,14,10,219,66,
+ 230,16,202,186,141,219,72,230,1,202,186,141,
+ 13,32,239,16,235,42,89,152,237,91,91,152,
+ 205,47,131,48,7,61,202,186,141,195,227,141,
+ 221,225,33,0,0,201,221,33,55,152,205,127,
+ 142,58,84,152,61,50,84,152,40,19,58,82,
+ 152,246,1,50,82,152,58,79,152,246,1,50,
+ 79,152,195,29,141,221,225,33,1,0,201,221,
+ 33,59,152,205,127,142,58,80,152,246,1,50,
+ 80,152,58,82,152,135,246,1,50,82,152,58,
+ 83,152,61,50,83,152,194,29,141,221,225,33,
+ 2,0,201,221,229,33,0,0,57,17,4,0,
+ 25,126,50,44,152,230,128,50,85,152,58,85,
+ 152,183,40,6,221,33,88,2,24,4,221,33,
+ 150,0,58,44,152,183,40,53,60,40,50,60,
+ 40,47,61,61,33,86,152,119,35,119,35,54,
+ 129,175,50,48,152,221,43,221,229,225,124,181,
+ 40,42,33,86,152,17,3,0,205,189,140,17,
+ 232,3,27,123,178,32,251,58,48,152,183,40,
+ 224,58,44,152,71,62,7,128,230,127,71,58,
+ 85,152,176,50,44,152,24,162,221,225,201,183,
+ 221,52,0,192,221,52,1,192,221,52,2,192,
+ 221,52,3,192,55,201,245,62,1,211,100,241,
+ 201,245,62,1,211,96,241,201,33,2,0,57,
+ 126,35,102,111,237,56,48,230,175,237,57,48,
+ 62,48,237,57,49,125,237,57,32,124,237,57,
+ 33,62,0,237,57,34,62,88,237,57,35,62,
+ 0,237,57,36,237,57,37,33,128,2,125,237,
+ 57,38,124,237,57,39,237,56,48,246,97,230,
+ 207,237,57,48,62,0,237,57,0,62,0,211,
+ 96,211,100,201,33,2,0,57,126,35,102,111,
+ 237,56,48,230,175,237,57,48,62,12,237,57,
+ 49,62,76,237,57,32,62,0,237,57,33,237,
+ 57,34,125,237,57,35,124,237,57,36,62,0,
+ 237,57,37,33,128,2,125,237,57,38,124,237,
+ 57,39,237,56,48,246,97,230,207,237,57,48,
+ 62,1,211,96,201,33,2,0,57,126,35,102,
+ 111,229,237,56,48,230,87,237,57,48,125,237,
+ 57,40,124,237,57,41,62,0,237,57,42,62,
+ 67,237,57,43,62,0,237,57,44,58,106,137,
+ 254,1,32,5,33,6,0,24,3,33,128,2,
+ 125,237,57,46,124,237,57,47,237,56,50,230,
+ 252,246,2,237,57,50,225,201,33,4,0,57,
+ 94,35,86,33,2,0,57,126,35,102,111,237,
+ 56,48,230,87,237,57,48,125,237,57,40,124,
+ 237,57,41,62,0,237,57,42,62,67,237,57,
+ 43,62,0,237,57,44,123,237,57,46,122,237,
+ 57,47,237,56,50,230,244,246,0,237,57,50,
+ 237,56,48,246,145,230,207,237,57,48,201,213,
+ 237,56,46,95,237,56,47,87,237,56,46,111,
+ 237,56,47,103,183,237,82,32,235,33,128,2,
+ 183,237,82,209,201,213,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,209,201,245,
+ 197,1,52,0,237,120,230,253,237,121,193,241,
+ 201,245,197,1,52,0,237,120,246,2,237,121,
+ 193,241,201,33,2,0,57,126,35,102,111,126,
+ 35,110,103,201,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,203,124,40,6,33,0,125,34,100,
+ 152,42,104,152,35,35,35,229,205,120,139,193,
+ 201,205,186,149,229,42,40,152,35,35,35,229,
+ 205,39,144,193,124,230,3,103,221,117,254,221,
+ 116,255,237,91,42,152,35,35,35,183,237,82,
+ 32,12,17,5,0,42,42,152,205,171,149,242,
+ 169,144,42,40,152,229,205,120,139,193,195,198,
+ 149,237,91,42,152,42,98,152,25,34,98,152,
+ 19,19,19,42,102,152,25,34,102,152,237,91,
+ 100,152,33,158,253,25,237,91,102,152,205,171,
+ 149,242,214,144,33,0,0,34,102,152,62,1,
+ 50,95,152,205,225,144,195,198,149,58,95,152,
+ 183,200,237,91,96,152,42,102,152,205,171,149,
+ 242,5,145,237,91,102,152,33,98,2,25,237,
+ 91,96,152,205,171,149,250,37,145,237,91,96,
+ 152,42,102,152,183,237,82,32,7,42,98,152,
+ 125,180,40,13,237,91,102,152,42,96,152,205,
+ 171,149,242,58,145,237,91,104,152,42,102,152,
+ 25,35,35,35,229,205,120,139,193,175,50,95,
+ 152,201,195,107,139,205,206,149,250,255,243,205,
+ 225,144,251,58,230,149,183,194,198,149,17,1,
+ 0,42,98,152,205,171,149,250,198,149,62,1,
+ 50,230,149,237,91,96,152,42,104,152,25,221,
+ 117,252,221,116,253,237,91,104,152,42,96,152,
+ 25,35,35,35,221,117,254,221,116,255,35,35,
+ 35,229,205,39,144,124,230,3,103,35,35,35,
+ 221,117,250,221,116,251,235,221,110,252,221,102,
+ 253,115,35,114,35,54,4,62,1,211,100,211,
+ 84,195,198,149,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,33,109,152,54,0,33,107,152,229,
+ 205,240,142,193,62,47,50,34,152,62,132,50,
+ 49,152,205,241,145,205,61,145,58,39,152,60,
+ 50,39,152,24,241,205,206,149,251,255,33,109,
+ 152,126,183,202,198,149,110,221,117,251,33,109,
+ 152,54,0,221,126,251,254,1,40,28,254,3,
+ 40,101,254,4,202,190,147,254,5,202,147,147,
+ 254,8,40,87,33,107,152,229,205,240,142,195,
+ 198,149,58,201,154,183,32,21,33,111,152,126,
+ 50,229,149,205,52,144,33,110,152,110,38,0,
+ 229,205,11,142,193,237,91,96,152,42,104,152,
+ 25,221,117,254,221,116,255,35,35,54,2,17,
+ 2,0,43,43,115,35,114,58,44,152,35,35,
+ 119,58,228,149,35,119,62,1,211,100,211,84,
+ 62,1,50,201,154,24,169,205,153,142,58,231,
+ 149,183,40,250,175,50,231,149,33,110,152,126,
+ 254,255,40,91,58,233,149,230,63,183,40,83,
+ 94,22,0,33,234,149,25,126,183,40,13,33,
+ 110,152,94,33,234,150,25,126,254,3,32,36,
+ 205,81,148,125,180,33,110,152,94,22,0,40,
+ 17,33,234,149,25,54,0,33,107,152,229,205,
+ 240,142,193,195,198,149,33,234,150,25,54,0,
+ 33,110,152,94,22,0,33,234,149,25,126,50,
+ 49,152,254,132,32,37,62,47,50,34,152,42,
+ 107,152,229,33,110,152,229,205,174,140,193,193,
+ 125,180,33,110,152,94,22,0,33,234,150,202,
+ 117,147,25,52,195,120,147,58,49,152,254,140,
+ 32,7,62,1,50,34,152,24,210,62,32,50,
+ 106,152,24,19,58,49,152,95,58,106,152,163,
+ 183,58,106,152,32,11,203,63,50,106,152,58,
+ 106,152,183,32,231,254,2,40,51,254,4,40,
+ 38,254,8,40,26,254,16,40,13,254,32,32,
+ 158,62,165,50,49,152,62,69,24,190,62,164,
+ 50,49,152,62,70,24,181,62,163,50,49,152,
+ 175,24,173,62,162,50,49,152,62,1,24,164,
+ 62,161,50,49,152,62,3,24,155,25,54,0,
+ 221,126,251,254,8,40,7,58,230,149,183,202,
+ 32,146,33,107,152,229,205,240,142,193,211,84,
+ 195,198,149,237,91,96,152,42,104,152,25,221,
+ 117,254,221,116,255,35,35,54,6,17,2,0,
+ 43,43,115,35,114,58,228,149,35,35,119,58,
+ 233,149,35,119,205,146,142,195,32,146,237,91,
+ 96,152,42,104,152,25,229,205,160,142,193,58,
+ 231,149,183,40,250,175,50,231,149,243,237,91,
+ 96,152,42,104,152,25,221,117,254,221,116,255,
+ 78,35,70,221,113,252,221,112,253,89,80,42,
+ 98,152,183,237,82,34,98,152,203,124,40,19,
+ 33,0,0,34,98,152,34,102,152,34,96,152,
+ 62,1,50,95,152,24,40,221,94,252,221,86,
+ 253,19,19,19,42,96,152,25,34,96,152,237,
+ 91,100,152,33,158,253,25,237,91,96,152,205,
+ 171,149,242,55,148,33,0,0,34,96,152,175,
+ 50,230,149,251,195,32,146,245,62,1,50,231,
+ 149,62,16,237,57,0,211,80,241,251,237,77,
+ 201,205,186,149,229,229,33,0,0,34,37,152,
+ 33,110,152,126,50,234,151,58,44,152,33,235,
+ 151,119,221,54,253,0,221,54,254,0,195,230,
+ 148,33,236,151,54,175,33,3,0,229,33,234,
+ 151,229,205,174,140,193,193,33,236,151,126,254,
+ 255,40,74,33,245,151,110,221,117,255,33,249,
+ 151,126,221,166,255,221,119,255,33,253,151,126,
+ 221,166,255,221,119,255,58,232,149,95,221,126,
+ 255,163,221,119,255,183,40,15,230,191,33,110,
+ 152,94,22,0,33,234,149,25,119,24,12,33,
+ 110,152,94,22,0,33,234,149,25,54,132,33,
+ 0,0,195,198,149,221,110,253,221,102,254,35,
+ 221,117,253,221,116,254,17,32,0,221,110,253,
+ 221,102,254,205,171,149,250,117,148,58,233,149,
+ 203,87,40,84,33,1,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,53,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,14,33,110,
+ 152,94,22,0,33,234,149,25,54,140,24,159,
+ 221,110,253,221,102,254,35,221,117,253,221,116,
+ 254,17,32,0,221,110,253,221,102,254,205,171,
+ 149,250,12,149,33,2,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,54,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,15,33,110,
+ 152,94,22,0,33,234,149,25,54,132,195,211,
+ 148,221,110,253,221,102,254,35,221,117,253,221,
+ 116,254,17,32,0,221,110,253,221,102,254,205,
+ 171,149,250,96,149,33,1,0,195,198,149,124,
+ 170,250,179,149,237,82,201,124,230,128,237,82,
+ 60,201,225,253,229,221,229,221,33,0,0,221,
+ 57,233,221,249,221,225,253,225,201,233,225,253,
+ 229,221,229,221,33,0,0,221,57,94,35,86,
+ 35,235,57,249,235,233,0,0,0,0,0,0,
+ 62,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 175,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,133,1,0,0,0,63,
+ 255,255,255,255,0,0,0,63,0,0,0,0,
+ 0,0,0,0,0,0,0,24,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0
+ } ;
+
+#endif
diff --git a/drivers/net/cops_ltdrv.h b/drivers/net/cops_ltdrv.h
new file mode 100644
index 000000000..33f3d9a06
--- /dev/null
+++ b/drivers/net/cops_ltdrv.h
@@ -0,0 +1,242 @@
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * seperate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_TANGENT
+
+unsigned char ltdrv_code[] = {
+ 58,3,0,50,148,10,33,143,15,62,85,119,
+ 190,32,9,62,170,119,190,32,3,35,24,241,
+ 34,146,10,249,17,150,10,33,143,15,183,237,
+ 82,77,68,11,107,98,19,54,0,237,176,62,
+ 16,237,57,51,62,0,237,57,50,237,57,54,
+ 62,12,237,57,49,62,195,33,39,2,50,56,
+ 0,34,57,0,237,86,205,30,2,251,205,60,
+ 10,24,169,67,111,112,121,114,105,103,104,116,
+ 32,40,99,41,32,49,57,56,56,45,49,57,
+ 57,50,44,32,80,114,105,110,116,105,110,103,
+ 32,67,111,109,109,117,110,105,99,97,116,105,
+ 111,110,115,32,65,115,115,111,99,105,97,116,
+ 101,115,44,32,73,110,99,46,65,108,108,32,
+ 114,105,103,104,116,115,32,114,101,115,101,114,
+ 118,101,100,46,32,32,4,4,22,40,255,60,
+ 4,96,10,224,6,0,7,126,2,64,11,246,
+ 12,6,13,0,14,193,15,0,5,96,3,192,
+ 1,0,9,8,62,3,211,82,62,192,211,82,
+ 201,62,3,211,82,62,213,211,82,201,62,5,
+ 211,82,62,224,211,82,201,62,5,211,82,62,
+ 224,211,82,201,62,5,211,82,62,96,211,82,
+ 201,6,28,33,180,1,14,82,237,163,194,4,
+ 2,33,39,2,34,64,0,58,3,0,230,1,
+ 192,62,11,237,121,62,118,237,121,201,33,182,
+ 10,54,132,205,253,1,201,245,197,213,229,42,
+ 150,10,14,83,17,98,2,67,20,237,162,58,
+ 179,1,95,219,82,230,1,32,6,29,32,247,
+ 195,17,3,62,1,211,82,219,82,95,230,160,
+ 32,10,237,162,32,225,21,32,222,195,15,3,
+ 237,162,123,230,96,194,21,3,62,48,211,82,
+ 62,1,211,82,175,211,82,237,91,150,10,43,
+ 55,237,82,218,19,3,34,152,10,98,107,58,
+ 154,10,190,32,81,62,1,50,158,10,35,35,
+ 62,132,190,32,44,54,133,43,70,58,154,10,
+ 119,43,112,17,3,0,205,137,3,62,16,211,
+ 82,62,56,211,82,205,217,1,42,150,10,14,
+ 83,17,98,2,67,20,58,178,1,95,195,59,
+ 2,62,129,190,194,227,2,54,130,43,70,58,
+ 154,10,119,43,112,17,3,0,205,137,3,195,
+ 254,2,35,35,126,254,132,194,227,2,205,61,
+ 3,24,20,62,128,166,194,222,2,221,229,221,
+ 33,175,10,205,93,6,205,144,7,221,225,225,
+ 209,193,241,251,237,77,221,229,221,33,159,10,
+ 205,93,6,221,225,205,61,3,195,247,2,24,
+ 237,24,235,24,233,230,64,40,2,24,227,24,
+ 225,175,50,179,10,205,208,1,201,197,33,4,
+ 0,57,126,35,102,111,205,51,3,193,201,62,
+ 1,50,179,10,34,150,10,54,0,58,179,10,
+ 183,200,62,14,211,82,62,193,211,82,62,10,
+ 211,82,62,224,211,82,62,6,211,82,58,154,
+ 10,211,82,62,16,211,82,62,56,211,82,62,
+ 48,211,82,219,82,230,1,40,4,219,83,24,
+ 242,62,14,211,82,62,33,211,82,62,1,211,
+ 82,62,9,211,82,62,32,211,82,205,217,1,
+ 201,14,83,205,208,1,24,23,14,83,205,208,
+ 1,205,226,1,58,174,1,61,32,253,205,244,
+ 1,58,174,1,61,32,253,205,226,1,58,175,
+ 1,61,32,253,62,5,211,82,62,233,211,82,
+ 62,128,211,82,58,176,1,61,32,253,237,163,
+ 27,62,192,211,82,219,82,230,4,40,250,237,
+ 163,27,122,179,32,243,219,82,230,4,40,250,
+ 58,178,1,71,219,82,230,4,40,3,5,32,
+ 247,219,82,230,4,40,250,205,235,1,58,177,
+ 1,61,32,253,205,244,1,201,229,213,35,35,
+ 126,230,128,194,145,4,43,58,154,10,119,43,
+ 70,33,181,10,119,43,112,17,3,0,243,62,
+ 10,211,82,219,82,230,128,202,41,4,209,225,
+ 62,1,55,251,201,205,144,3,58,180,10,254,
+ 255,202,127,4,205,217,1,58,178,1,71,219,
+ 82,230,1,32,6,5,32,247,195,173,4,219,
+ 83,71,58,154,10,184,194,173,4,58,178,1,
+ 71,219,82,230,1,32,6,5,32,247,195,173,
+ 4,219,83,58,178,1,71,219,82,230,1,32,
+ 6,5,32,247,195,173,4,219,83,254,133,194,
+ 173,4,58,179,1,24,4,58,179,1,135,61,
+ 32,253,209,225,205,137,3,205,61,3,183,251,
+ 201,209,225,243,62,10,211,82,219,82,230,128,
+ 202,164,4,62,1,55,251,201,205,144,3,205,
+ 61,3,183,251,201,209,225,62,2,55,251,201,
+ 243,62,14,211,82,62,33,211,82,251,201,33,
+ 4,0,57,94,35,86,33,2,0,57,126,35,
+ 102,111,221,229,34,193,10,237,83,195,10,221,
+ 33,171,10,205,93,6,58,185,10,50,186,10,
+ 58,184,10,135,50,184,10,205,112,6,254,3,
+ 56,16,58,185,10,135,60,230,15,50,185,10,
+ 175,50,184,10,24,23,58,183,10,205,112,6,
+ 254,3,48,13,58,185,10,203,63,50,185,10,
+ 62,255,50,183,10,58,185,10,50,186,10,58,
+ 183,10,135,50,183,10,62,32,50,187,10,50,
+ 188,10,6,255,219,82,230,16,32,3,5,32,
+ 247,205,180,4,6,40,219,82,230,16,40,3,
+ 5,32,247,62,10,211,82,219,82,230,128,194,
+ 46,5,219,82,230,16,40,214,237,95,71,58,
+ 186,10,160,230,15,40,32,71,14,10,62,10,
+ 211,82,219,82,230,128,202,119,5,205,180,4,
+ 195,156,5,219,82,230,16,202,156,5,13,32,
+ 229,16,225,42,193,10,237,91,195,10,205,252,
+ 3,48,7,61,202,156,5,195,197,5,221,225,
+ 33,0,0,201,221,33,163,10,205,93,6,58,
+ 188,10,61,50,188,10,40,19,58,186,10,246,
+ 1,50,186,10,58,183,10,246,1,50,183,10,
+ 195,46,5,221,225,33,1,0,201,221,33,167,
+ 10,205,93,6,58,184,10,246,1,50,184,10,
+ 58,186,10,135,246,1,50,186,10,58,187,10,
+ 61,50,187,10,194,46,5,221,225,33,2,0,
+ 201,221,229,33,0,0,57,17,4,0,25,126,
+ 50,154,10,230,128,50,189,10,58,189,10,183,
+ 40,6,221,33,88,2,24,4,221,33,150,0,
+ 58,154,10,183,40,49,60,40,46,61,33,190,
+ 10,119,35,119,35,54,129,175,50,158,10,221,
+ 43,221,229,225,124,181,40,42,33,190,10,17,
+ 3,0,205,206,4,17,232,3,27,123,178,32,
+ 251,58,158,10,183,40,224,58,154,10,71,62,
+ 7,128,230,127,71,58,189,10,176,50,154,10,
+ 24,166,221,225,201,183,221,52,0,192,221,52,
+ 1,192,221,52,2,192,221,52,3,192,55,201,
+ 6,8,14,0,31,48,1,12,16,250,121,201,
+ 33,2,0,57,94,35,86,35,78,35,70,35,
+ 126,35,102,105,79,120,68,103,237,176,201,33,
+ 2,0,57,126,35,102,111,62,17,237,57,48,
+ 125,237,57,40,124,237,57,41,62,0,237,57,
+ 42,62,64,237,57,43,62,0,237,57,44,33,
+ 128,2,125,237,57,46,124,237,57,47,62,145,
+ 237,57,48,211,68,58,149,10,211,66,201,33,
+ 2,0,57,126,35,102,111,62,33,237,57,48,
+ 62,64,237,57,32,62,0,237,57,33,237,57,
+ 34,125,237,57,35,124,237,57,36,62,0,237,
+ 57,37,33,128,2,125,237,57,38,124,237,57,
+ 39,62,97,237,57,48,211,67,58,149,10,211,
+ 66,201,237,56,46,95,237,56,47,87,237,56,
+ 46,111,237,56,47,103,183,237,82,32,235,33,
+ 128,2,183,237,82,201,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,201,205,106,
+ 10,221,110,6,221,102,7,126,35,110,103,195,
+ 118,10,205,106,10,33,0,0,34,205,10,34,
+ 198,10,34,200,10,33,143,15,34,207,10,237,
+ 91,207,10,42,146,10,183,237,82,17,0,255,
+ 25,34,203,10,203,124,40,6,33,0,125,34,
+ 203,10,42,207,10,229,205,37,3,195,118,10,
+ 205,106,10,229,42,150,10,35,35,35,229,205,
+ 70,7,193,124,230,3,103,221,117,254,221,116,
+ 255,237,91,152,10,35,35,35,183,237,82,32,
+ 12,17,5,0,42,152,10,205,91,10,242,203,
+ 7,42,150,10,229,205,37,3,195,118,10,237,
+ 91,152,10,42,200,10,25,34,200,10,42,205,
+ 10,25,34,205,10,237,91,203,10,33,158,253,
+ 25,237,91,205,10,205,91,10,242,245,7,33,
+ 0,0,34,205,10,62,1,50,197,10,205,5,
+ 8,33,0,0,57,249,195,118,10,205,106,10,
+ 58,197,10,183,202,118,10,237,91,198,10,42,
+ 205,10,205,91,10,242,46,8,237,91,205,10,
+ 33,98,2,25,237,91,198,10,205,91,10,250,
+ 78,8,237,91,198,10,42,205,10,183,237,82,
+ 32,7,42,200,10,125,180,40,13,237,91,205,
+ 10,42,198,10,205,91,10,242,97,8,237,91,
+ 207,10,42,205,10,25,229,205,37,3,175,50,
+ 197,10,195,118,10,205,29,3,33,0,0,57,
+ 249,195,118,10,205,106,10,58,202,10,183,40,
+ 22,205,14,7,237,91,209,10,19,19,19,205,
+ 91,10,242,139,8,33,1,0,195,118,10,33,
+ 0,0,195,118,10,205,126,10,252,255,205,108,
+ 8,125,180,194,118,10,237,91,200,10,33,0,
+ 0,205,91,10,242,118,10,237,91,207,10,42,
+ 198,10,25,221,117,254,221,116,255,35,35,35,
+ 229,205,70,7,193,124,230,3,103,35,35,35,
+ 221,117,252,221,116,253,229,221,110,254,221,102,
+ 255,229,33,212,10,229,205,124,6,193,193,221,
+ 110,252,221,102,253,34,209,10,33,211,10,54,
+ 4,33,209,10,227,205,147,6,193,62,1,50,
+ 202,10,243,221,94,252,221,86,253,42,200,10,
+ 183,237,82,34,200,10,203,124,40,17,33,0,
+ 0,34,200,10,34,205,10,34,198,10,50,197,
+ 10,24,37,221,94,252,221,86,253,42,198,10,
+ 25,34,198,10,237,91,203,10,33,158,253,25,
+ 237,91,198,10,205,91,10,242,68,9,33,0,
+ 0,34,198,10,205,5,8,33,0,0,57,249,
+ 251,195,118,10,205,106,10,33,49,13,126,183,
+ 40,16,205,42,7,237,91,47,13,19,19,19,
+ 205,91,10,242,117,9,58,142,15,198,1,50,
+ 142,15,195,118,10,33,49,13,126,254,1,40,
+ 25,254,3,202,7,10,254,5,202,21,10,33,
+ 49,13,54,0,33,47,13,229,205,207,6,195,
+ 118,10,58,141,15,183,32,72,33,51,13,126,
+ 50,149,10,205,86,7,33,50,13,126,230,127,
+ 183,32,40,58,142,15,230,127,50,142,15,183,
+ 32,5,198,1,50,142,15,33,50,13,126,111,
+ 23,159,103,203,125,58,142,15,40,5,198,128,
+ 50,142,15,33,50,13,119,33,50,13,126,111,
+ 23,159,103,229,205,237,5,193,33,211,10,54,
+ 2,33,2,0,34,209,10,58,154,10,33,212,
+ 10,119,58,148,10,33,213,10,119,33,209,10,
+ 229,205,147,6,193,24,128,42,47,13,229,33,
+ 50,13,229,205,191,4,193,24,239,33,211,10,
+ 54,6,33,3,0,34,209,10,58,154,10,33,
+ 212,10,119,58,148,10,33,213,10,119,33,214,
+ 10,54,5,33,209,10,229,205,147,6,24,200,
+ 205,106,10,33,49,13,54,0,33,47,13,229,
+ 205,207,6,33,209,10,227,205,147,6,193,205,
+ 80,9,205,145,8,24,248,124,170,250,99,10,
+ 237,82,201,124,230,128,237,82,60,201,225,253,
+ 229,221,229,221,33,0,0,221,57,233,221,249,
+ 221,225,253,225,201,233,225,253,229,221,229,221,
+ 33,0,0,221,57,94,35,86,35,235,57,249,
+ 235,233,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0
+ } ;
+
+#endif
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c
index e0d71b202..d803e953e 100644
--- a/drivers/net/ibmtr.c
+++ b/drivers/net/ibmtr.c
@@ -55,7 +55,10 @@
* + completed multiple adapter support. (November 20 1996)
* + implemented csum_partial_copy in tr_rx and increased receive
* buffer size and count. Minor fixes. (March 15, 1997)
-*/
+ *
+ * Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk>
+ * + Now compiles ok as a module again.
+ */
#ifdef PCMCIA
#define MODULE
@@ -163,9 +166,9 @@ unsigned char ibmtr_debug_trace=0;
int ibmtr_probe(struct device *dev);
static int ibmtr_probe1(struct device *dev, int ioaddr);
-unsigned char get_sram_size(struct tok_info *adapt_info);
+static unsigned char get_sram_size(struct tok_info *adapt_info);
static int tok_init_card(struct device *dev);
-int trdev_init(struct device *dev);
+static int trdev_init(struct device *dev);
void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void initial_tok_int(struct device *dev);
static void open_sap(unsigned char type,struct device *dev);
@@ -1583,7 +1586,7 @@ int init_module(void)
dev_ibmtr[i]->init = &ibmtr_probe;
if (register_trdev(dev_ibmtr[i]) != 0) {
- kfree_s(dev_ibmtr[i], sizeof(struct dev));
+ kfree_s(dev_ibmtr[i], sizeof(struct device));
dev_ibmtr[i] = NULL;
if (i == 0) {
printk("ibmtr: register_trdev() returned non-zero.\n");
diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c
index e282847a9..5f8cb5cca 100644
--- a/drivers/net/lapbether.c
+++ b/drivers/net/lapbether.c
@@ -407,7 +407,7 @@ static int lapbeth_close(struct device *dev)
return 0;
}
-__initfunc(static int lapbeth_dev_init(struct device *dev))
+static int lapbeth_dev_init(struct device *dev)
{
return 0;
}
diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c
index d2dc0eb90..95f1ae739 100644
--- a/drivers/net/sdla_fr.c
+++ b/drivers/net/sdla_fr.c
@@ -10,6 +10,49 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+*
+* Jun 29, 1997 Alan Cox o Hacked up vendor source 1.0.3 to remove
+* C++ style comments, and a massive security
+* hole (the UDP management junk).
+*
+* May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem
+* With multiple boards a problem was seen where
+* the second board always stopped transmitting
+* packet after running for a while. The code
+* got into a stage where the interrupts were
+* disabled and dev->tbusy was set to 1.
+* This caused the If_send() routine to get into
+* the if clause for set_bit(0,dev->tbusy)
+* forever.
+* The code got into this stage due to an
+* interrupt occuring within the if clause for
+* set_bit(0,dev->tbusy). Since an interrupt
+* disables furhter transmit interrupt and
+* makes dev->tbusy = 0, this effect was undone
+* by making dev->tbusy = 1 in the if clause.
+* The Fix checks to see if Transmit interrupts
+* are disabled then do not make dev->tbusy = 1
+* Introduced a global variable: int_occur and
+* added tx_int_enabled in the wan_device
+* structure.
+* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple
+* boards.
+*
+* Apr 25, 1997 Farhan Thawar o added UDP Management stuff
+* o fixed bug in if_send() and tx_intr() to
+* sleep and wakeup all devices
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o fixed (+1) bug in fr508_rx_intr()
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* o added tx_intr() routine
+* Jan 30, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
+* o fixed a bug causing driver configured as
+* a FR switch to be stuck in WAN_DISCONNECTED
+* mode
* Jan 02, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,12 +65,13 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/if_arp.h> /* ARPHRD_* defines */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
#include <asm/io.h> /* for inb(), outb(), etc. */
+#include <asm/uaccess.h>
#define _GNUC_
#include <linux/sdla_fr.h> /* frame relay firmware API definitions */
@@ -67,6 +111,9 @@ typedef struct dlci_status
unsigned char state PACKED;
} dlci_status_t;
+static char TracingEnabled;
+/* variable for checking interrupts within the ISR routine */
+static int int_occur = 0;
/****** Function Prototypes *************************************************/
/* WAN link driver entry points. These are called by the WAN router module. */
@@ -75,14 +122,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -103,6 +152,8 @@ static int fr_configure (sdla_t* card, fr_conf_t *conf);
static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu);
static int fr_comm_enable (sdla_t* card);
static int fr_comm_disable (sdla_t* card);
+static int fr_get_err_stats (sdla_t* card);
+static int fr_get_stats (sdla_t* card);
static int fr_add_dlci (sdla_t* card, int dlci, int num);
static int fr_activate_dlci (sdla_t* card, int dlci, int num);
static int fr_issue_isf (sdla_t* card, int isf);
@@ -120,7 +171,6 @@ static void set_chan_state (struct device* dev, int state);
static struct device* find_channel (sdla_t* card, unsigned dlci);
static int is_tx_ready (sdla_t* card);
static unsigned int dec_to_uint (unsigned char* str, int len);
-static unsigned int hex_to_uint (unsigned char* str, int len);
/****** Public Functions ****************************************************/
@@ -198,7 +248,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
u.cfg.n392 = 3;
u.cfg.n393 = 4;
u.cfg.kbps = conf->bps / 1000;
- u.cfg.cir_fwd = max(min(u.cfg.kbps, 512), 1);
+ u.cfg.cir_fwd = 16;
u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd;
u.cfg.options = 0x0081; /* direct Rx, no CIR check */
switch (conf->u.fr.signalling)
@@ -214,7 +264,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
{
u.cfg.station = 1; /* switch emulation mode */
card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16;
- card->u.f.dlci_num = max(min(conf->u.fr.dlci_num, 1), 100);
+ card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
}
if (conf->clocking == WANOPT_INTERNAL)
u.cfg.port |= 0x0001
@@ -270,11 +320,14 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.clocking = conf->clocking;
card->wandev.station = conf->station;
card->poll = &wpf_poll;
+ card->exec = &wpf_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
card->wandev.state = WAN_DISCONNECTED;
- return 0;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
+ return 0;
}
/******* WAN Device Driver Entry Points *************************************/
@@ -284,9 +337,22 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+ fr_get_err_stats(card);
+ fr_get_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -377,6 +443,43 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err, len;
+ fr_cmd_t cmd;
+
+ if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
+ return -EFAULT;
+ /* execute command */
+ do
+ {
+ memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+ if (cmd.length)
+ if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
+ return -EFAULT;
+ if (sdla_exec(mbox))
+ err = mbox->cmd.result;
+ else
+ return -EIO;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -416,6 +519,9 @@ static int if_init (struct device* dev)
dev->mem_start = wandev->maddr;
dev->mem_end = wandev->maddr + wandev->msize - 1;
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i])
@@ -451,12 +557,14 @@ static int if_open (struct device* dev)
err = -EIO;
goto done;
}
+ wanpipe_set_state(card, WAN_CONNECTED);
+
if (card->wandev.station == WANOPT_CPE)
{
/* CPE: issue full status enquiry */
fr_issue_isf(card, FR_ISF_FSE);
}
- else /* Switch: activate DLCI(s) */
+ else /* FR switch: activate DLCI(s) */
{
fr_add_dlci(card,
card->u.f.node_dlci, card->u.f.dlci_num)
@@ -465,7 +573,6 @@ static int if_open (struct device* dev)
card->u.f.node_dlci, card->u.f.dlci_num)
;
}
- wanpipe_set_state(card, WAN_CONNECTED);
}
dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN);
dev->interrupt = 0;
@@ -538,14 +645,13 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- fr_channel_t* chan = dev->priv;
+ fr_channel_t* chan = skb->dev->priv;
sdla_t* card = chan->card;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -570,10 +676,11 @@ static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
*/
static int if_send (struct sk_buff* skb, struct device* dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
- int retry = 0;
-
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
+ int retry=0, err;
+ struct device *dev2;
+
if (test_and_set_bit(0, (void*)&card->wandev.critical))
{
#ifdef _DEBUG_
@@ -581,7 +688,8 @@ static int if_send (struct sk_buff* skb, struct device* dev)
card->devname)
;
#endif
- return 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
if (test_and_set_bit(0, (void*)&dev->tbusy))
@@ -592,23 +700,55 @@ static int if_send (struct sk_buff* skb, struct device* dev)
;
#endif
++chan->ifstats.collisions;
+ ++card->wandev.stats.collisions;
+
retry = 1;
+ if(card->wandev.tx_int_enabled)
+ {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+ {
+ dev2->tbusy = 1;
+ }
+ }
+ }
+ else if (card->wandev.state != WAN_CONNECTED)
+ {
+ ++chan->ifstats.tx_dropped;
+ ++card->wandev.stats.tx_dropped;
+ }
+ else if (chan->state != WAN_CONNECTED)
+ {
+ update_chan_state(dev);
+ ++chan->ifstats.tx_dropped;
+ ++card->wandev.stats.tx_dropped;
+ }
+ else if (!is_tx_ready(card))
+ {
+ retry = 1;
+ if(card->wandev.tx_int_enabled)
+ {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+ {
+ dev2->tbusy = 1;
+ }
+ }
}
- else if ((card->wandev.state != WAN_CONNECTED) ||
- (chan->state != WAN_CONNECTED))
- ++chan->ifstats.tx_dropped
- ;
- else if (!is_tx_ready(card))
- retry = 1
- ;
else
{
- int err = (card->hw.fwid == SFID_FR508) ?
+ err = (card->hw.fwid == SFID_FR508) ?
fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
fr502_send(card, chan->dlci, 0, skb->len, skb->data)
;
- if (err) ++chan->ifstats.tx_errors;
- else ++chan->ifstats.tx_packets;
+ if (err)
+ {
+ ++chan->ifstats.tx_errors;
+ ++card->wandev.stats.tx_errors;
+ }
+ else
+ {
+ ++chan->ifstats.tx_packets;
+ ++card->wandev.stats.tx_packets;
+ }
}
if (!retry)
{
@@ -619,6 +759,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
return retry;
}
+
/*============================================================================
* Get ethernet-style interface statistics.
* Return a pointer to struct enet_statistics.
@@ -663,7 +804,14 @@ static void fr508_isr (sdla_t* card)
{
fr508_flags_t* flags = card->flags;
fr_buf_ctl_t* bctl;
-
+
+ if(int_occur){
+#ifdef _DEBUG_
+ printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname);
+#endif
+ return;
+ }
+ int_occur=1;
switch (flags->iflag)
{
case 0x01: /* receive interrupt */
@@ -681,6 +829,7 @@ static void fr508_isr (sdla_t* card)
default:
spur_intr(card);
}
+ int_occur = 0;
flags->iflag = 0;
}
@@ -690,13 +839,14 @@ static void fr508_isr (sdla_t* card)
static void fr502_rx_intr (sdla_t* card)
{
fr_mbox_t* mbox = card->rxmb;
- struct sk_buff* skb;
- struct device* dev;
- fr_channel_t* chan;
+ struct sk_buff *skb;
+ struct device *dev;
+ fr_channel_t *chan;
unsigned dlci, len;
void* buf;
-
+
sdla_mapmem(&card->hw, FR502_RX_VECTOR);
+
dlci = mbox->cmd.dlci;
len = mbox->cmd.length;
@@ -741,13 +891,14 @@ static void fr502_rx_intr (sdla_t* card)
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
+ ++card->wandev.stats.rx_errors;
}
else
{
netif_rx(skb);
++chan->ifstats.rx_packets;
+ ++card->wandev.stats.rx_packets;
}
-
rx_done:
sdla_mapmem(&card->hw, FR_MB_VECTOR);
}
@@ -763,7 +914,7 @@ static void fr508_rx_intr (sdla_t* card)
fr_channel_t* chan;
unsigned dlci, len, offs;
void* buf;
-
+
if (frbuf->flag != 0x01)
{
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
@@ -804,9 +955,9 @@ static void fr508_rx_intr (sdla_t* card)
}
/* Copy data to the socket buffer */
- if ((offs + len) > card->u.f.rx_top)
+ if ((offs + len) > card->u.f.rx_top + 1)
{
- unsigned tmp = card->u.f.rx_top - offs;
+ unsigned tmp = card->u.f.rx_top - offs + 1;
buf = skb_put(skb, tmp);
sdla_peek(&card->hw, offs, buf, tmp);
@@ -814,8 +965,7 @@ static void fr508_rx_intr (sdla_t* card)
len -= tmp;
}
buf = skb_put(skb, len);
- sdla_peek(&card->hw, offs, buf, len);
-
+ sdla_peek(&card->hw, offs, buf, len);
/* Decapsulate packet and pass it up the protocol stack */
skb->dev = dev;
buf = skb_pull(skb, 1); /* remove hardware header */
@@ -824,13 +974,14 @@ static void fr508_rx_intr (sdla_t* card)
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
+ ++card->wandev.stats.rx_errors;
}
else
{
netif_rx(skb);
++chan->ifstats.rx_packets;
+ ++card->wandev.stats.rx_packets;
}
-
rx_done:
/* Release buffer element and calculate a pointer to the next one */
frbuf->flag = 0;
@@ -848,7 +999,17 @@ rx_done:
*/
static void tx_intr (sdla_t* card)
{
+ struct device* dev = card->wandev.dev;
+
+ for (; dev; dev = dev->slave) {
+ if( !dev || !dev->start ) continue;
+ dev->tbusy = 0;
+ dev_tint(dev);
+ }
+ card->wandev.tx_int_enabled = 0;
+/*
printk(KERN_INFO "%s: transmit interrupt!\n", card->devname);
+*/
}
/*============================================================================
@@ -877,8 +1038,14 @@ static void spur_intr (sdla_t* card)
*/
static void wpf_poll (sdla_t* card)
{
- fr502_flags_t* flags = card->flags;
+ static unsigned long last_poll;
+ fr502_flags_t* flags;
+ if ((jiffies - last_poll) < HZ)
+ return
+ ;
+
+ flags = card->flags;
if (flags->event)
{
fr_mbox_t* mbox = card->mbox;
@@ -889,6 +1056,7 @@ static void wpf_poll (sdla_t* card)
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
if (err) fr_event(card, err, mbox);
}
+ last_poll = jiffies;
}
/****** Frame Relay Firmware-Specific Functions *****************************/
@@ -1025,6 +1193,65 @@ static int fr_comm_disable (sdla_t* card)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int fr_get_err_stats (sdla_t* card)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+ mbox->cmd.command = FR_READ_ERROR_STATS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ if (!err)
+ {
+ fr_comm_stat_t* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_over_errors = stats->rx_overruns;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_aborts;
+ card->wandev.stats.rx_length_errors = stats->rx_too_long;
+ card->wandev.stats.tx_aborted_errors = stats->tx_aborts;
+ }
+ return err;
+}
+
+/*============================================================================
+ * Get statistics.
+ */
+static int fr_get_stats (sdla_t* card)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+ mbox->cmd.command = FR_READ_STATISTICS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ if (!err)
+ {
+ fr_link_stat_t* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_frame_errors = stats->rx_bad_format;
+ card->wandev.stats.rx_dropped =
+ stats->rx_dropped + stats->rx_dropped2
+ ;
+ }
+ return err;
+}
+
+/*============================================================================
* Add DLCI(s) (Access Node only!).
*/
static int fr_add_dlci (sdla_t* card, int dlci, int num)
@@ -1363,6 +1590,7 @@ static int is_tx_ready (sdla_t* card)
if (sb & 0x02) return 1;
flags->imask |= 0x02;
+ card->wandev.tx_int_enabled = 1;
}
else
{
@@ -1389,27 +1617,4 @@ static unsigned int dec_to_uint (unsigned char* str, int len)
return val;
}
-/*============================================================================
- * Convert hex string to unsigned integer.
- * If len != 0 then only 'len' characters of the string are conferted.
- */
-static unsigned int hex_to_uint (unsigned char* str, int len)
-{
- unsigned val, ch;
-
- if (!len) len = strlen(str);
- for (val = 0; len; ++str, --len)
- {
- ch = *str;
- if (is_digit(ch))
- val = (val << 4) + (ch - (unsigned)'0')
- ;
- else if (is_hex_digit(ch))
- val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10)
- ;
- else break;
- }
- return val;
-}
-
/****** End *****************************************************************/
diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c
index c5c5ecc55..fa636040b 100644
--- a/drivers/net/sdla_ppp.c
+++ b/drivers/net/sdla_ppp.c
@@ -10,6 +10,25 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* Jun 29, 1997 Alan Cox o Dumped the idiot UDP management system.
+*
+* May 22, 1997 Jaspreet Singh o Added change in the PPP_SET_CONFIG command for
+* 508 card to reflect changes in the new
+* ppp508.sfm for supporting:continous transmission
+* of Configure-Request packets without receiving a
+* reply
+* OR-ed 0x300 to conf_flags
+* o Changed connect_tmout from 900 to 0
+* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple boards
+* Apr 25, 1997 Farhan Thawar o added UDP Management stuff
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o fixed (+1) bug in rx_intr()
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
* Jan 06, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,11 +41,12 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/if_arp.h> /* ARPHRD_* defines */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h>
#define _GNUC_
#include <linux/sdla_ppp.h> /* PPP firmware API definitions */
@@ -57,14 +77,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -74,6 +96,7 @@ static int ppp_configure (sdla_t* card, void* data);
static int ppp_set_intr_mode (sdla_t* card, unsigned mode);
static int ppp_comm_enable (sdla_t* card);
static int ppp_comm_disable (sdla_t* card);
+static int ppp_get_err_stats (sdla_t* card);
static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto);
static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb);
@@ -94,6 +117,7 @@ static int config508 (sdla_t* card);
static void show_disc_cause (sdla_t* card, unsigned cause);
static unsigned char bps_to_speed_code (unsigned long bps);
+static char TracingEnabled;
/****** Public Functions ****************************************************/
/*============================================================================
@@ -163,10 +187,13 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.station = conf->station;
card->isr = &wpp_isr;
card->poll = &wpp_poll;
+ card->exec = &wpp_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
card->wandev.state = WAN_DISCONNECTED;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
return 0;
}
@@ -177,9 +204,22 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+
+ ppp_get_err_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -228,6 +268,38 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ ppp_mbox_t* mbox = card->mbox;
+ int len;
+
+ if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len)
+ {
+ if(copy_from_user((void*)&mbox->data, u_data, len))
+ return -EFAULT;
+ }
+
+ /* execute command */
+ if (!sdla_exec(mbox))
+ return -EIO;
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -264,6 +336,9 @@ static int if_init (struct device* dev)
dev->mem_start = wandev->maddr;
dev->mem_end = wandev->maddr + wandev->msize - 1;
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i])
@@ -408,13 +483,12 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- sdla_t* card = dev->priv;
+ sdla_t* card = skb->dev->priv;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -460,8 +534,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
#endif
++card->wandev.stats.collisions;
retry = 1;
- }
- else if (card->wandev.state != WAN_CONNECTED)
+ } else if (card->wandev.state != WAN_CONNECTED)
++card->wandev.stats.tx_dropped
;
else if (!skb->protocol)
@@ -489,6 +562,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
* Get ethernet-style interface statistics.
* Return a pointer to struct enet_statistics.
*/
+
static struct enet_statistics* if_stats (struct device* dev)
{
sdla_t* card = dev->priv;
@@ -599,6 +673,31 @@ static int ppp_comm_disable (sdla_t* card)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int ppp_get_err_stats (sdla_t* card)
+{
+ ppp_mbox_t* mb = card->mbox;
+ int err;
+
+ memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
+ mb->cmd.command = PPP_READ_ERROR_STATS;
+ err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+ if (err == CMD_OK)
+ {
+ ppp_err_stats_t* stats = (void*)mb->data;
+
+ card->wandev.stats.rx_over_errors = stats->rx_overrun;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_abort;
+ card->wandev.stats.rx_length_errors = stats->rx_lost;
+ card->wandev.stats.tx_aborted_errors = stats->tx_abort;
+ }
+ else ppp_error(card, err, mb);
+ return err;
+}
+
+/*============================================================================
* Send packet.
* Return: 0 - o.k.
* 1 - no transmit buffers available
@@ -701,7 +800,7 @@ static void rx_intr (sdla_t* card)
struct sk_buff* skb;
unsigned len;
void* buf;
-
+
if (rxbuf->flag != 0x01)
{
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
@@ -738,9 +837,9 @@ static void rx_intr (sdla_t* card)
{
unsigned addr = rxbuf->buf.ptr;
- if ((addr + len) > card->u.p.rx_top)
+ if ((addr + len) > card->u.p.rx_top + 1)
{
- unsigned tmp = card->u.p.rx_top - addr;
+ unsigned tmp = card->u.p.rx_top - addr + 1;
buf = skb_put(skb, tmp);
sdla_peek(&card->hw, addr, buf, tmp);
@@ -751,8 +850,8 @@ static void rx_intr (sdla_t* card)
sdla_peek(&card->hw, addr, buf, len);
}
- /* Decapsulate packet and pass it up the protocol stack */
- switch (rxbuf->proto)
+ /* Decapsulate packet */
+ switch (rxbuf->proto)
{
case 0x00:
skb->protocol = htons(ETH_P_IP);
@@ -762,10 +861,11 @@ static void rx_intr (sdla_t* card)
skb->protocol = htons(ETH_P_IPX);
break;
}
+
+ /* Pass it up the protocol stack */
skb->dev = dev;
netif_rx(skb);
++card->wandev.stats.rx_packets;
-
rx_done:
/* Release buffer element and calculate a pointer to the next one */
rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00;
@@ -891,13 +991,14 @@ static int config502 (sdla_t* card)
cfg.auth_wait_tmr = 300;
cfg.mdm_fail_tmr = 5;
cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 900;
+ cfg.connect_tmout = 0; /* changed it from 900 */
cfg.conf_retry = 10;
cfg.term_retry = 2;
cfg.fail_retry = 5;
cfg.auth_retry = 10;
cfg.ip_options = 0x80;
cfg.ipx_options = 0xA0;
+ cfg.conf_flags |= 0x0E;
/*
cfg.ip_local = dev->pa_addr;
cfg.ip_remote = dev->pa_dstaddr;
@@ -921,6 +1022,7 @@ static int config508 (sdla_t* card)
if (card->wandev.interface == WANOPT_RS232)
cfg.conf_flags |= 0x0020;
;
+ cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/
cfg.txbuf_percent = 60; /* % of Tx bufs */
cfg.mtu_local = card->wandev.mtu;
cfg.mtu_remote = card->wandev.mtu;
@@ -929,7 +1031,7 @@ static int config508 (sdla_t* card)
cfg.auth_wait_tmr = 300;
cfg.mdm_fail_tmr = 5;
cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 900;
+ cfg.connect_tmout = 0; /* changed it from 900 */
cfg.conf_retry = 10;
cfg.term_retry = 2;
cfg.fail_retry = 5;
diff --git a/drivers/net/sdla_x25.c b/drivers/net/sdla_x25.c
index 3296c2819..77dfc43b0 100644
--- a/drivers/net/sdla_x25.c
+++ b/drivers/net/sdla_x25.c
@@ -10,6 +10,17 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o added support for V35
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* o added support for single '@' address to
+* accept all incoming calls
+* o fixed bug in set_chan_state() to disconnect
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
* Jan 07, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,10 +33,11 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h> /* copy_from_user, etc */
#define _GNUC_
#include <linux/sdla_x25.h> /* X.25 firmware API definitions */
@@ -90,14 +102,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -118,6 +132,8 @@ static void poll_active (sdla_t* card);
/* X.25 firmware interface functions */
static int x25_get_version (sdla_t* card, char* str);
static int x25_configure (sdla_t* card, TX25Config* conf);
+static int x25_get_err_stats (sdla_t* card);
+static int x25_get_stats (sdla_t* card);
static int x25_set_intr_mode (sdla_t* card, int mode);
static int x25_close_hdlc (sdla_t* card);
static int x25_open_hdlc (sdla_t* card);
@@ -235,7 +251,9 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
{
u.cfg.station = 0; /* DCE mode */
}
-
+ if (conf->interface != WANOPT_RS232 ) {
+ u.cfg.hdlcOptions |= 0x80; /* V35 mode */
+ }
/* adjust MTU */
if (!conf->mtu || (conf->mtu >= 1024))
card->wandev.mtu = 1024
@@ -299,6 +317,7 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.station = conf->station;
card->isr = &wpx_isr;
card->poll = &wpx_poll;
+ card->exec = &wpx_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
@@ -313,9 +332,23 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+
+ x25_get_err_stats(card);
+ x25_get_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -412,6 +445,45 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err, len;
+ TX25Cmd cmd;
+
+ if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
+ return -EFAULT;
+
+ /* execute command */
+ do
+ {
+ memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+ if (cmd.length)
+ if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
+ return -EFAULT;
+ if (sdla_exec(mbox))
+ err = mbox->cmd.result;
+ else
+ return -EIO;
+ }
+ while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn));
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data)
+ if(copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -555,14 +627,13 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- x25_channel_t* chan = dev->priv;
+ x25_channel_t* chan = skb->dev->priv;
sdla_t* card = chan->card;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -588,7 +659,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
- int retry = 0, queued = 0;
+ int queued = 0;
if (test_and_set_bit(0, (void*)&card->wandev.critical))
{
@@ -597,7 +668,8 @@ static int if_send (struct sk_buff* skb, struct device* dev)
card->devname)
;
#endif
- return 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
if (test_and_set_bit(0, (void*)&dev->tbusy))
@@ -608,7 +680,10 @@ static int if_send (struct sk_buff* skb, struct device* dev)
;
#endif
++chan->ifstats.collisions;
- retry = 1;
+ ++card->wandev.stats.collisions;
+ dev_kfree_skb(skb, FREE_WRITE);
+ card->wandev.critical = 0;
+ return 0;
}
else if (chan->protocol && (chan->protocol != skb->protocol))
{
@@ -646,13 +721,13 @@ static int if_send (struct sk_buff* skb, struct device* dev)
++chan->ifstats.tx_dropped;
}
- if (!retry && !queued)
+ if (!queued)
{
dev_kfree_skb(skb, FREE_WRITE);
dev->tbusy = 0;
}
card->wandev.critical = 0;
- return retry;
+ return 0;
}
/*============================================================================
@@ -996,6 +1071,62 @@ static int x25_configure (sdla_t* card, TX25Config* conf)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int x25_get_err_stats (sdla_t* card)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+ mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ } while (err && retry-- &&
+ x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0))
+ ;
+ if (!err)
+ {
+ THdlcCommErr* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_over_errors = stats->rxOverrun;
+ card->wandev.stats.rx_crc_errors = stats->rxBadCrc;
+ card->wandev.stats.rx_missed_errors = stats->rxAborted;
+ card->wandev.stats.tx_aborted_errors = stats->txAborted;
+ }
+ return err;
+}
+
+/*============================================================================
+ * Get protocol statistics.
+ */
+static int x25_get_stats (sdla_t* card)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+ mbox->cmd.command = X25_READ_STATISTICS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ } while (err && retry-- &&
+ x25_error(card, err, X25_READ_STATISTICS, 0))
+ ;
+ if (!err)
+ {
+ TX25Stats* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_packets = stats->rxData;
+ card->wandev.stats.tx_packets = stats->rxData;
+ }
+ return err;
+}
+
+/*============================================================================
* Close HDLC link.
*/
static int x25_close_hdlc (sdla_t* card)
@@ -1478,6 +1609,10 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
if (strcmp(info->src, chan->addr) == 0)
break
;
+ // If just an '@' is specified, accept all incomming calls
+ if (strcmp(chan->addr, "") == 0)
+ break
+ ;
}
if (dev == NULL)
@@ -1755,9 +1890,10 @@ static void set_chan_state (struct device* dev, int state)
printk (KERN_INFO "%s: interface %s disconnected!\n",
card->devname, dev->name)
;
- if (chan->svc)
- *(unsigned short*)dev->dev_addr = 0
- ;
+ if (chan->svc) {
+ *(unsigned short*)dev->dev_addr = 0;
+ chan->lcn = 0;
+ }
break;
}
chan->state = state;
diff --git a/drivers/net/sdlamain.c b/drivers/net/sdlamain.c
index 75d7df944..eeb0b40f3 100644
--- a/drivers/net/sdlamain.c
+++ b/drivers/net/sdlamain.c
@@ -26,9 +26,10 @@
#include <linux/module.h> /* support for loadable modules */
#include <linux/ioport.h> /* request_region(), release_region() */
#include <linux/tqueue.h> /* for kernel task queues */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
-#include <asm/segment.h> /* kernel <-> user copy */
+#include <asm/uaccess.h> /* kernel <-> user copy */
+
/****** Defines & Macros ****************************************************/
@@ -404,20 +405,13 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump)
unsigned long oldvec; /* DPM window vector */
int err = 0;
- if ((u_dump == NULL) ||
- verify_area(VERIFY_READ, u_dump, sizeof(sdla_dump_t)))
- return -EFAULT
- ;
- memcpy_fromfs((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t));
+ if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)))
+ return -EFAULT;
+
if ((dump.magic != WANPIPE_MAGIC) ||
(dump.offset + dump.length > card->hw.memory))
- return -EINVAL
- ;
- if ((dump.ptr == NULL) ||
- verify_area(VERIFY_WRITE, dump.ptr, dump.length))
- return -EFAULT
- ;
-
+ return -EINVAL;
+
winsize = card->hw.dpmsize;
cli(); /* >>> critical section start <<< */
oldvec = card->hw.vector;
@@ -433,9 +427,12 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump)
err = -EIO;
break;
}
- memcpy_tofs((void*)(dump.ptr),
- (void*)(card->hw.dpmbase + pos), len)
- ;
+ /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */
+ sti(); /* Not ideal but tough we have to do this */
+ if(copy_to_user((void*)(dump.ptr),
+ (void*)(card->hw.dpmbase + pos), len))
+ return -EFAULT;
+ cli();
dump.length -= len;
dump.offset += len;
(char*)dump.ptr += len;
@@ -456,16 +453,12 @@ static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec)
sdla_exec_t exec;
if (card->exec == NULL)
- return -ENODEV
- ;
- if ((u_exec == NULL) ||
- verify_area(VERIFY_READ, u_exec, sizeof(sdla_exec_t)))
- return -EFAULT
- ;
- memcpy_fromfs((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t));
+ return -ENODEV;
+
+ if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t)))
+ return -EFAULT;
if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL))
- return -EINVAL
- ;
+ return -EINVAL;
return card->exec(card, exec.cmd, exec.data);
}
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index a40f16599..fff408c42 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -70,11 +70,11 @@
#include <linux/if_arp.h>
#include <linux/init.h>
#include <net/dst.h>
-#include "shaper.h"
+#include <linux/if_shaper.h>
int sh_debug; /* Debug flag */
-#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.03 for Linux 2.1\n"
+#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n"
/*
* Locking
@@ -426,17 +426,26 @@ static int shaper_header(struct sk_buff *skb, struct device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
struct shaper *sh=dev->priv;
+ int v;
if(sh_debug)
printk("Shaper header\n");
- return sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+ skb->dev=sh->dev;
+ v=sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+ skb->dev=dev;
+ return v;
}
static int shaper_rebuild_header(struct sk_buff *skb)
{
struct shaper *sh=skb->dev->priv;
+ struct device *dev=skb->dev;
+ int v;
if(sh_debug)
printk("Shaper rebuild header\n");
- return sh->rebuild_header(skb);
+ skb->dev=sh->dev;
+ v=sh->rebuild_header(skb);
+ skb->dev=dev;
+ return v;
}
static int shaper_cache(struct dst_entry *dst, struct neighbour *neigh, struct hh_cache *hh)
@@ -483,6 +492,7 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device
else
shdev->rebuild_header = NULL;
+#if 0
if(dev->hard_header_cache)
{
sh->hard_header_cache = dev->hard_header_cache;
@@ -500,6 +510,10 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device
}
else
shdev->header_cache_update= NULL;
+#else
+ shdev->header_cache_update = NULL;
+ shdev->hard_header_cache = NULL;
+#endif
shdev->hard_header_len=dev->hard_header_len;
shdev->type=dev->type;
diff --git a/drivers/net/shaper.h b/drivers/net/shaper.h
deleted file mode 100644
index abb6198af..000000000
--- a/drivers/net/shaper.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef __LINUX_SHAPER_H
-#define __LINUX_SHAPER_H
-
-#ifdef __KERNEL__
-
-#define SHAPER_QLEN 10
-/*
- * This is a bit speed dependant (read it shouldnt be a constant!)
- *
- * 5 is about right for 28.8 upwards. Below that double for every
- * halving of speed or so. - ie about 20 for 9600 baud.
- */
-#define SHAPER_LATENCY (5*HZ)
-#define SHAPER_MAXSLIP 2
-#define SHAPER_BURST (HZ/50) /* Good for >128K then */
-
-struct shaper
-{
- struct sk_buff_head sendq;
- __u32 bytespertick;
- __u32 shapelatency;
- __u32 shapeclock;
- __u32 recovery; /* Time we can next clock a packet out on
- an empty queue */
- char locked;
- struct device *dev;
- int (*hard_start_xmit) (struct sk_buff *skb,
- struct device *dev);
- int (*hard_header) (struct sk_buff *skb,
- struct device *dev,
- unsigned short type,
- void *daddr,
- void *saddr,
- unsigned len);
- int (*rebuild_header)(struct sk_buff *skb);
- int (*hard_header_cache)(struct dst_entry *dst, struct neighbour *neigh,
- struct hh_cache *hh);
- void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
- struct net_device_stats* (*get_stats)(struct device *dev);
- struct wait_queue *wait_queue;
- struct timer_list timer;
-};
-
-#endif
-
-#define SHAPER_SET_DEV 0x0001
-#define SHAPER_SET_SPEED 0x0002
-
-struct shaperconf
-{
- __u16 ss_cmd;
- union
- {
- char ssu_name[14];
- __u32 ssu_speed;
- } ss_u;
-#define ss_speed ss_u.ssu_speed
-#define ss_name ss_u.ssu_name
-};
-
-#endif
diff --git a/drivers/net/strip.c b/drivers/net/strip.c
index 32c02b4f0..0104f6dc9 100644
--- a/drivers/net/strip.c
+++ b/drivers/net/strip.c
@@ -14,53 +14,62 @@
* for kernel-based devices like TTY. It interfaces between a
* raw TTY, and the kernel's INET protocol layers (via DDI).
*
- * Version: @(#)strip.c 0.9.8 June 1996
+ * Version: @(#)strip.c 1.2 February 1997
*
* Author: Stuart Cheshire <cheshire@cs.stanford.edu>
*
- * Fixes: v0.9 12th Feb 1996.
+ * 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
+ * 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
+ * v0.9.2 13th March 1996 (SC)
* Uses arp cache lookups (but doesn't send arp packets yet)
*
- * v0.9.3 17th April 1996
+ * 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
+ * v0.9.4 27th April 1996 (SC)
* First attempt at using "&COMMAND" Starmode AT commands
*
- * v0.9.5 29th May 1996
+ * 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.6 5th June 1996 (Elliot)
+ * Put "message level" tags in every "printk" statement
*
- * v0.9.7 13th June 1996
- * Added support for the /proc fs (laik)
+ * v0.9.7 13th June 1996 (laik)
+ * Added support for the /proc fs
*
- * v0.9.8 July 1996
- * Added packet logging (Mema)
- */
-
-/*
- * Undefine this symbol if you don't have PROC_NET_STRIP_STATUS
- * defined in include/linux/proc_fs.h
+ * 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
*/
-#define DO_PROC_NET_STRIP_STATUS 1
-
-/*
- * Define this symbol if you want to enable STRIP packet tracing.
- */
+#ifdef MODULE
+static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR";
+#else
+static const char StripVersion[] = "1.2-STUART.CHESHIRE";
+#endif
-#define DO_PROC_NET_STRIP_TRACE 0
+#define TICKLE_TIMERS 0
+#define EXT_COUNTERS 1
/************************************************************************/
@@ -146,13 +155,19 @@ 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)
+
/*
- * Note: A Metricom packet looks like this: *<address>*<key><payload><CR>
- * eg. *0000-1234*SIP0<payload><CR>
- * 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.
+ * 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
@@ -162,83 +177,20 @@ typedef struct
unsigned short protocol; /* The protocol type, using Ethernet codes */
} STRIP_Header;
-typedef struct GeographicLocation
-{
- char s[18];
-} GeographicLocation;
-
-typedef enum {
- NodeValid = 0x1,
- NodeHasWAN = 0x2,
- NodeIsRouter = 0x4
-} NodeType;
-
-typedef struct MetricomNode
-{
- NodeType type; /* Some flags about the type of node */
- GeographicLocation gl; /* The location of the node. */
- MetricomAddress addr; /* The metricom address of this node */
- int poll_latency; /* The latency to poll that node ? */
- int rssi; /* The Receiver Signal Strength Indicator */
- struct MetricomNode *next; /* The next node */
-} MetricomNode;
-
-enum { FALSE = 0, TRUE = 1 };
-
-/*
- * Holds the packet signature for an IP packet.
- */
typedef struct
{
- IPaddr src;
- /* Data is stored in the following field in network byte order. */
- __u16 id;
-} IPSignature;
+ char c[60];
+} MetricomNode;
-/*
- * Holds the packet signature for an ARP packet.
- */
+#define NODE_TABLE_SIZE 32
typedef struct
{
- IPaddr src;
- /* Data is stored in the following field in network byte order. */
- __u16 op;
-} ARPSignature;
-
-/*
- * Holds the signature of a packet.
- */
-typedef union
-{
- IPSignature ip_sig;
- ARPSignature arp_sig;
- __u8 print_sig[6];
-} PacketSignature;
-
-typedef enum {
- EntrySend = 0,
- EntryReceive = 1
-} LogEntry;
-
-/* Structure for Packet Logging */
-typedef struct stripLog
-{
- LogEntry entry_type;
- u_long seqNum;
- int packet_type;
- PacketSignature sig;
- MetricomAddress src;
- MetricomAddress dest;
- struct timeval timeStamp;
- u_long rawSize;
- u_long stripSize;
- u_long slipSize;
- u_long valid;
-} StripLog;
+ struct timeval timestamp;
+ int num_nodes;
+ MetricomNode node[NODE_TABLE_SIZE];
+} MetricomNodeTable;
-#define ENTRY_TYPE_TO_STRING(X) ((X) ? "r" : "s")
-
-#define BOOLEAN_TO_STRING(X) ((X) ? "true" : "false")
+enum { FALSE = 0, TRUE = 1 };
/*
* Holds the radio's firmware version.
@@ -246,7 +198,7 @@ typedef struct stripLog
typedef struct
{
char c[50];
-} MetricomFirmwareVersion;
+} FirmwareVersion;
/*
* Holds the radio's serial number.
@@ -254,7 +206,7 @@ typedef struct
typedef struct
{
char c[18];
-} MetricomSerialNumber;
+} SerialNumber;
/*
* Holds the radio's battery voltage.
@@ -262,7 +214,19 @@ typedef struct
typedef struct
{
char c[11];
-} MetricomBatteryVoltage;
+} 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 */
+} FirmwareLevel;
struct strip
{
@@ -292,6 +256,25 @@ struct strip
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.
*/
@@ -300,52 +283,142 @@ struct strip
struct strip **referrer; /* The pointer that points to us*/
int discard; /* Set if serial error */
int working; /* Is radio working correctly? */
- int structured_messages; /* Parsable AT response msgs? */
+ int firmware_level; /* Message structuring level */
+ int next_command; /* Next periodic command */
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 */
- MetricomNode *neighbor_list; /* The list of neighbor nodes */
- int neighbor_list_locked; /* Indicates the list is locked */
- MetricomFirmwareVersion firmware_version; /* The radio's firmware version */
- MetricomSerialNumber serial_number; /* The radio's serial number */
- MetricomBatteryVoltage battery_voltage; /* The radio's battery voltage */
+ 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 */
- char if_name[8]; /* Dynamically generated name */
+ char8 if_name; /* Dynamically generated name */
struct device dev; /* Our device structure */
/*
- * Packet Logging Structures.
+ * Neighbour radio records
*/
- u_long num_sent;
- u_long num_received;
-
- int next_entry; /* The index of the oldest packet; */
- /* Also the next to be logged. */
- StripLog packetLog[610];
+ 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 */
-#ifdef MODULE
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE-MODULAR";
-#else
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE";
-#endif
+/*
+ * 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 TickleString1[] = "***&COMMAND*ATS305?\r";
-static const char TickleString2[] = "***&COMMAND*ATS305?\r\r"
- "*&COMMAND*ATS300?\r\r*&COMMAND*ATS325?\r\r*&COMMAND*AT~I2 nn\r\r";
+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";
@@ -354,26 +427,27 @@ static const MetricomAddress broadcast_address = { { 0xFF,0xFF,0xFF,0xFF,0xFF,0x
static const MetricomKey SIP0Key = { { "SIP0" } };
static const MetricomKey ARP0Key = { { "ARP0" } };
-static const MetricomKey ERR_Key = { { "ERR_" } };
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 (including starmode address) is 1183 bytes.
- * Allowing 32 bytes for header, and 65/64 expansion for STRIP encoding,
- * that translates to a maximum payload MTU of 1132.
+ * 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_STRIP_MTU = 1132;
-static const unsigned short DEFAULT_STRIP_MTU = 1024;
+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;
-static const int STRIP_NODE_LEN = 64;
-static const char STRIP_PORTABLE_CHAR = 'P';
-static const char STRIP_ROUTER_CHAR = 'r';
-static const int STRIP_PROC_BUFFER_SIZE = 4096;
-static const int STRIP_LOG_INT_SIZE = 10;
/************************************************************************/
/* Global variables */
@@ -384,10 +458,18 @@ static struct strip *struct_strip_list = NULL;
/************************************************************************/
/* Macros */
+/* Returns TRUE if text T begins with prefix P */
+#define has_prefix(T,P) (!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 MIN(X, Y) ((X) < (Y) ? (X) : (Y))
@@ -395,17 +477,6 @@ static struct strip *struct_strip_list = NULL;
#define ELEMENTS_OF(X) (sizeof(X) / sizeof((X)[0]))
#define ARRAY_END(X) (&((X)[ELEMENTS_OF(X)]))
-/* Encapsulation can expand packet of size x to 65/64x + 1 */
-/* Sent packet looks like "*<address>*<key><encaps payload><CR>" */
-/* 1 1-18 1 4 ? 1 */
-/* We allow 31 bytes for the stars, the key, the address and the <CR> */
-#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L)
-
-#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]) )
-
#define JIFFIE_TO_SEC(X) ((X) / HZ)
@@ -605,7 +676,15 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr)
}
/* else, we only have one so far, so switch to Stuff_Diff code */
code = Stuff_Diff;
- /* and fall through to Stuff_Diff case below */
+ /* 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:
@@ -639,10 +718,10 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr)
src++; /* Consume the byte */
break;
}
- if (count == Stuff_MaxCount)
- {
- StuffData_FinishBlock(code + count);
- }
+ if (count == Stuff_MaxCount)
+ {
+ StuffData_FinishBlock(code + count);
+ }
}
if (code == Stuff_NoCode)
{
@@ -758,14 +837,21 @@ static __u8 *UnStuffData(__u8 *src, __u8 *end, __u8 *dst, __u32 dst_length)
* Convert a string to a Metricom Address.
*/
-static void string_to_radio_address(MetricomAddress *addr, __u8 *p)
+#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);
}
/*
@@ -780,19 +866,18 @@ static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddres
/*
* Note: Must make sure sx_size is big enough to receive a stuffed
- * MAX_STRIP_MTU packet. Additionally, we also want to ensure that it's
+ * 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)
{
struct device *dev = &strip_info->dev;
- int stuffedlen = STRIP_ENCAP_SIZE(dev->mtu);
- int sx_size = MAX(stuffedlen, 4096);
- int tx_size = stuffedlen + sizeof(TickleString2);
- __u8 *r = kmalloc(MAX_STRIP_MTU, GFP_ATOMIC);
- __u8 *s = kmalloc(sx_size, GFP_ATOMIC);
- __u8 *t = kmalloc(tx_size, GFP_ATOMIC);
+ int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096);
+ int tx_size = STRIP_ENCAP_SIZE(dev->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;
@@ -824,10 +909,10 @@ static void strip_changedmtu(struct strip *strip_info)
unsigned char *otbuff = strip_info->tx_buff;
InterruptStatus intstat;
- if (dev->mtu > MAX_STRIP_MTU)
+ if (dev->mtu > MAX_SEND_MTU)
{
printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n",
- strip_info->dev.name, MAX_STRIP_MTU);
+ strip_info->dev.name, MAX_SEND_MTU);
dev->mtu = old_mtu;
return;
}
@@ -856,9 +941,8 @@ static void strip_changedmtu(struct strip *strip_info)
memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count);
else
{
- strip_info->sx_count = 0;
+ strip_info->discard = strip_info->sx_count;
strip_info->rx_over_errors++;
- strip_info->discard = 1;
}
}
@@ -889,7 +973,7 @@ static void strip_unlock(struct strip *strip_info)
/*
* Set the time to go off in one second.
*/
- strip_info->idle_timer.expires = jiffies + HZ;
+ strip_info->idle_timer.expires = jiffies + 1*HZ;
add_timer(&strip_info->idle_timer);
if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy))
printk(KERN_ERR "%s: trying to unlock already unlocked device!\n",
@@ -900,8 +984,6 @@ static void strip_unlock(struct strip *strip_info)
/************************************************************************/
/* Callback routines for exporting information through /proc */
-#if DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
/*
* This function updates the total amount of data printed so far. It then
* determines if the amount of data printed into a buffer has reached the
@@ -912,29 +994,29 @@ static void strip_unlock(struct strip *strip_info)
*/
static int
shift_buffer(char *buffer, int requested_offset, int requested_len,
- int *total, int *slop, char **buf)
+ int *total, int *slop, char **buf)
{
int printed;
/* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n",
- (int) buffer, requested_offset, requested_len, *total,
- (int) *buf); */
+ (int) buffer, requested_offset, requested_len, *total,
+ (int) *buf); */
printed = *buf - buffer;
if (*total + printed <= requested_offset) {
- *total += printed;
- *buf = buffer;
+ *total += printed;
+ *buf = buffer;
}
else {
- if (*total < requested_offset) {
- *slop = requested_offset - *total;
- }
- *total = requested_offset + printed - *slop;
+ if (*total < requested_offset) {
+ *slop = requested_offset - *total;
+ }
+ *total = requested_offset + printed - *slop;
}
if (*total > requested_offset + requested_len) {
- return 1;
+ return 1;
}
else {
- return 0;
+ return 0;
}
}
@@ -945,12 +1027,12 @@ shift_buffer(char *buffer, int requested_offset, int requested_len,
*/
static int
calc_start_len(char *buffer, char **start, int requested_offset,
- int requested_len, int total, char *buf)
+ int requested_len, int total, char *buf)
{
int return_len, buffer_len;
buffer_len = buf - buffer;
- if (buffer_len >= STRIP_PROC_BUFFER_SIZE - 1) {
+ if (buffer_len >= 4095) {
printk(KERN_ERR "STRIP: exceeded /proc buffer size\n");
}
@@ -960,20 +1042,16 @@ calc_start_len(char *buffer, char **start, int requested_offset,
*/
return_len = total - requested_offset;
if (return_len < 0) {
- return_len = 0;
+ return_len = 0;
}
*start = buf - return_len;
if (return_len > requested_len) {
- return_len = requested_len;
+ return_len = requested_len;
}
/* printk(KERN_DEBUG "return_len: %d\n", return_len); */
return return_len;
}
-#endif DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
-#if DO_PROC_NET_STRIP_STATUS
-
/*
* 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.
@@ -991,537 +1069,171 @@ static char *time_delta(char buffer[], long time)
return(buffer);
}
-/*
- * This function prints radio status information into the specified
- * buffer.
- */
-static int
-sprintf_status_info(char *buffer, struct strip *strip_info)
-{
- char temp_buffer[32];
- MetricomAddressString addr_string;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, "Interface name\t\t%s\n", strip_info->if_name);
- buf += sprintf(buf, " Radio working:\t\t%s\n",
- strip_info->working &&
- (long)jiffies - strip_info->watchdog_doreset < 0 ? "Yes" : "No");
- (void) radio_address_to_string((MetricomAddress *)
- &strip_info->dev.dev_addr,
- &addr_string);
- buf += sprintf(buf, " Device address:\t%s\n", addr_string.c);
- buf += sprintf(buf, " Firmware version:\t%s\n",
- !strip_info->working ? "Unknown" :
- !strip_info->structured_messages ? "Should be upgraded" :
- strip_info->firmware_version.c);
- buf += sprintf(buf, " Serial number:\t\t%s\n", strip_info->serial_number.c);
- buf += sprintf(buf, " Battery voltage:\t%s\n", strip_info->battery_voltage.c);
- buf += sprintf(buf, " Transmit queue (bytes):%d\n", strip_info->tx_left);
- buf += sprintf(buf, " Next watchdog probe:\t%s\n",
- time_delta(temp_buffer, strip_info->watchdog_doprobe));
- buf += sprintf(buf, " Next watchdog reset:\t%s\n",
- time_delta(temp_buffer, strip_info->watchdog_doreset));
- buf += sprintf(buf, " Next gratuitous ARP:\t%s\n",
- time_delta(temp_buffer, strip_info->gratuitous_arp));
- buf += sprintf(buf, " Next ARP interval:\t%ld seconds\n",
- JIFFIE_TO_SEC(strip_info->arp_interval));
- return buf - buffer;
-}
-
-static int
-sprintf_portables(char *buffer, struct strip *strip_info)
-{
-
- MetricomAddressString addr_string;
- MetricomNode *node;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, " portables: name\t\tpoll_latency\tsignal strength\n");
- for (node = strip_info->neighbor_list; node != NULL;
- node = node->next) {
- if (!(node->type & NodeValid)) {
- break;
- }
- if (node->type & NodeHasWAN) {
- continue;
- }
- (void) radio_address_to_string(&node->addr, &addr_string);
- buf += sprintf(buf, " %s\t\t\t\t%d\t\t%d\n",
- addr_string.c, node->poll_latency, node->rssi);
- }
- return buf - buffer;
-}
-
-static int
-sprintf_poletops(char *buffer, struct strip *strip_info)
-{
- MetricomNode *node;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, " poletops: GPS\t\t\tpoll_latency\tsignal strength\n");
- for (node = strip_info->neighbor_list;
- node != NULL; node = node->next) {
- if (!(node->type & NodeValid)) {
- break;
- }
- if (!(node->type & NodeHasWAN)) {
- continue;
- }
- buf += sprintf(buf, " %s\t\t\t%d\t\t%d\n",
- node->gl.s, node->poll_latency, node->rssi);
- }
- return buf - buffer;
-}
-
-/*
- * This function is exports status information from the STRIP driver through
- * the /proc file system. /proc filesystem should be fixed:
- * 1) slow (sprintfs here, a memory copy in the proc that calls this one)
- * 2) length of buffer not passed
- * 3) dummy isn't client data set when the callback was registered
- * 4) poorly documented (this function is called until the requested amount
- * of data is returned, buffer is only 4K long, dummy is the permissions
- * of the file (?), the proc_dir_entry passed to proc_net_register must
- * be kmalloc-ed)
- */
-
-static int
-strip_get_status_info(char *buffer, char **start, off_t requested_offset,
- int requested_len, int dummy)
+static int sprintf_neighbours(char *buffer, MetricomNodeTable *table, char *title)
{
- char *buf;
- int total = 0, slop = 0, len_exceeded;
- InterruptStatus i_status;
- struct strip *strip_info;
-
- buf = buffer;
- buf += sprintf(buf, "strip_version: %s\n", StripVersion);
-
- i_status = DisableInterrupts();
- strip_info = struct_strip_list;
- RestoreInterrupts(i_status);
-
- while (strip_info != NULL) {
- i_status = DisableInterrupts();
- buf += sprintf_status_info(buf, strip_info);
- RestoreInterrupts(i_status);
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info->neighbor_list_locked = TRUE;
- buf += sprintf_portables(buf, strip_info);
- strip_info->neighbor_list_locked = FALSE;
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info->neighbor_list_locked = TRUE;
- buf += sprintf_poletops(buf, strip_info);
- strip_info->neighbor_list_locked = FALSE;
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info = strip_info->next;
- }
-done:
- return calc_start_len(buffer, start, requested_offset, requested_len,
- total, buf);
+ /* 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;
+ char *ptr;
+ do
+ {
+ int i;
+ t = table->timestamp;
+ ptr = buffer;
+ if (table->num_nodes) ptr += sprintf(ptr, "\n %s\n", title);
+ for (i=0; i<table->num_nodes; i++)
+ {
+ InterruptStatus intstat = DisableInterrupts();
+ MetricomNode node = table->node[i];
+ RestoreInterrupts(intstat);
+ ptr += sprintf(ptr, " %s\n", node.c);
+ }
+ } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec);
+ return ptr - buffer;
}
-#endif DO_PROC_NET_STRIP_STATUS
-
-#if DO_PROC_NET_STRIP_TRACE
-
/*
- * Convert an Ethernet protocol to a string
- * Returns the number of characters printed.
+ * This function prints radio status information into the specified buffer.
+ * I think the buffer size is 4K, so this routine should never print more
+ * than 4K of data into it. With the maximum of 32 portables and 32 poletops
+ * reported, the routine outputs 3107 bytes into the buffer.
*/
-
-static int protocol_to_string(int protocol, __u8 *p)
-{
- int printed;
-
- switch (protocol) {
- case ETH_P_IP:
- printed = sprintf(p, "IP");
- break;
- case ETH_P_ARP:
- printed = sprintf(p, "ARP");
- break;
- default:
- printed = sprintf(p, "%d", protocol);
- }
- return printed;
-}
-
static int
-sprintf_log_entry(char *buffer, struct strip *strip_info, int packet_index)
+sprintf_status_info(char *buffer, struct strip *strip_info)
{
- StripLog *entry;
+ char temp[32];
+ char *p = buffer;
MetricomAddressString addr_string;
- __u8 sig_buf[24], *s;
- char *buf, proto_buf[10];
-
- entry = &strip_info->packetLog[packet_index];
- if (!entry->valid) {
- return 0;
- }
- buf = buffer;
- buf += sprintf(buf, "%-4s %s %7lu ", strip_info->if_name,
- ENTRY_TYPE_TO_STRING(entry->entry_type), entry->seqNum);
- (void) protocol_to_string(entry->packet_type, proto_buf);
- buf += sprintf(buf, "%-4s", proto_buf);
- s = entry->sig.print_sig;
- sprintf(sig_buf, "%d.%d.%d.%d.%d.%d", s[0], s[1], s[2], s[3], s[4], s[5]);
- buf += sprintf(buf, "%-24s", sig_buf);
- (void) radio_address_to_string((MetricomAddress *) &entry->src,
- &addr_string);
- buf += sprintf(buf, "%-10s", addr_string.c);
- (void) radio_address_to_string((MetricomAddress *) &entry->dest,
- &addr_string);
- buf += sprintf(buf, "%-10s", addr_string.c);
- buf += sprintf(buf, "%8d %6d %5lu %6lu %5lu\n", entry->timeStamp.tv_sec,
- entry->timeStamp.tv_usec, entry->rawSize,
- entry->stripSize, entry->slipSize);
- return buf - buffer;
-}
-
-/*
- * This function exports trace information from the STRIP driver through the
- * /proc file system.
- */
-static int
-strip_get_trace_info(char *buffer, char **start, off_t requested_offset,
- int requested_len, int dummy)
-{
- char *buf;
- int len_exceeded, total = 0, slop = 0, packet_index, oldest;
- InterruptStatus i_status;
- struct strip *strip_info;
-
- buf = buffer;
- buf += sprintf(buf, "if s/r seqnum t signature ");
- buf += sprintf(buf,
- "src dest sec usec raw strip slip\n");
-
- i_status = DisableInterrupts();
- strip_info = struct_strip_list;
- oldest = strip_info->next_entry;
- RestoreInterrupts(i_status);
-
- /*
- * If we disable interrupts for this entire loop,
- * characters from the serial port could be lost,
- * so we only disable interrupts when accessing
- * a log entry. If more than STRIP_LOG_INT_SIZE
- * packets are logged before the first entry is
- * printed, then some of the entries could be
- * printed out of order.
- */
- while (strip_info != NULL) {
- for (packet_index = oldest + STRIP_LOG_INT_SIZE;
- packet_index != oldest;
- packet_index = (packet_index + 1) %
- ELEMENTS_OF(strip_info->packetLog)) {
- i_status = DisableInterrupts();
- buf += sprintf_log_entry(buf, strip_info, packet_index);
- RestoreInterrupts(i_status);
- len_exceeded = shift_buffer(buffer, requested_offset,
- requested_len, &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- }
- strip_info = strip_info->next;
- }
-done:
- return calc_start_len(buffer, start, requested_offset, requested_len,
- total, buf);
-}
+ /* First, we must copy all of our data to a safe place, */
+ /* in case a serial interrupt comes in and changes it. */
+ InterruptStatus intstat = DisableInterrupts();
+ 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;
+ char8 if_name = strip_info->if_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
+ RestoreInterrupts(intstat);
-static int slip_len(unsigned char *data, int len)
-{
- static const unsigned char SLIP_END=0300; /* indicates end of SLIP frame */
- static const unsigned char SLIP_ESC=0333; /* indicates SLIP byte stuffing */
- int count = len;
- while (--len >= 0)
+ p += sprintf(p, "\nInterface name\t\t%s\n", if_name.c);
+ p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No");
+ radio_address_to_string(&true_dev_addr, &addr_string);
+ p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c);
+ if (manual_dev_addr)
{
- if (*data == SLIP_END || *data == SLIP_ESC) count++;
- data++;
- }
- return(count);
-}
-
-/* Copied from kernel/sched.c */
-static void jiffiestotimeval(unsigned long jiffies, struct timeval *value)
-{
- value->tv_usec = (jiffies % HZ) * (1000000.0 / HZ);
- value->tv_sec = jiffies / HZ;
- return;
-}
-
-/*
- * This function logs a packet.
- * A pointer to the packet itself is passed so that some of the data can be
- * used to compute a signature. The pointer should point the the
- * part of the packet following the STRIP_header.
- */
-
-static void packet_log(struct strip *strip_info, __u8 *packet,
- LogEntry entry_type, STRIP_Header *hdr,
- int raw_size, int strip_size, int slip_size)
-{
- StripLog *entry;
- struct iphdr *iphdr;
- struct arphdr *arphdr;
-
- entry = &strip_info->packetLog[strip_info->next_entry];
- if (entry_type == EntrySend) {
- entry->seqNum = strip_info->num_sent++;
+ radio_address_to_string(&dev_dev_addr, &addr_string);
+ p += sprintf(p, " Device address:\t%s\n", addr_string.c);
+ }
+ p += sprintf(p, " Firmware version:\t%s", !working ? "Unknown" :
+ !firmware_level ? "Should be upgraded" :
+ firmware_version.c);
+ if (firmware_level >= ChecksummedMessages) p += sprintf(p, " (Checksums Enabled)");
+ p += sprintf(p, "\n");
+ p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c);
+ p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c);
+ p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left);
+ p += sprintf(p, " Receive packet rate: %ld packets per second\n", rx_average_pps / 8);
+ p += sprintf(p, " Transmit packet rate: %ld packets per second\n", tx_average_pps / 8);
+ p += sprintf(p, " Sent packet rate: %ld packets per second\n", sx_average_pps / 8);
+ p += sprintf(p, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe));
+ p += sprintf(p, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset));
+ p += sprintf(p, " Next gratuitous ARP:\t");
+
+ if (!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)))
+ p += sprintf(p, "Disabled\n");
+ else
+ {
+ p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp));
+ p += sprintf(p, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval));
}
- else {
- entry->seqNum = strip_info->num_received++;
- }
- entry->entry_type = entry_type;
- entry->packet_type = ntohs(hdr->protocol);
- switch (entry->packet_type) {
- case ETH_P_IP:
- /*
- * The signature for IP is the sender's ip address and
- * the identification field.
- */
- iphdr = (struct iphdr *) packet;
- entry->sig.ip_sig.id = iphdr->id;
- entry->sig.ip_sig.src.l = iphdr->saddr;
- break;
- case ETH_P_ARP:
- /*
- * The signature for ARP is the sender's ip address and
- * the operation.
- */
- arphdr = (struct arphdr *) packet;
- entry->sig.arp_sig.op = arphdr->ar_op;
- memcpy(&entry->sig.arp_sig.src.l, packet + 8 + arphdr->ar_hln,
- sizeof(entry->sig.arp_sig.src.l));
- entry->sig.arp_sig.src.l = entry->sig.arp_sig.src.l;
- break;
- default:
- printk(KERN_DEBUG "STRIP: packet_log: unknown packet type: %d\n",
- entry->packet_type);
- break;
- }
- memcpy(&entry->src, &hdr->src_addr, sizeof(MetricomAddress));
- memcpy(&entry->dest, &hdr->dst_addr, sizeof(MetricomAddress));
-
- jiffiestotimeval(jiffies, &(entry->timeStamp));
- entry->rawSize = raw_size;
- entry->stripSize = strip_size;
- entry->slipSize = slip_size;
- entry->valid = 1;
-
- strip_info->next_entry = (strip_info->next_entry + 1) %
- ELEMENTS_OF(strip_info->packetLog);
-}
-#endif DO_PROC_NET_STRIP_TRACE
+ if (working)
+ {
+#ifdef EXT_COUNTERS
+ p += sprintf(p, "\n");
+ p += sprintf(p, " Total bytes: \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes);
+ p += sprintf(p, " thru radio: \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes);
+ p += sprintf(p, " thru serial port: \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes);
+ p += sprintf(p, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes);
+#endif
+ p += sprintf_neighbours(p, &strip_info->poletops, "Poletops:");
+ p += sprintf_neighbours(p, &strip_info->portables, "Portables:");
+ }
-/*
- * 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(len, sizeof(MetricomFirmwareVersion) - 1);
- 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(MetricomSerialNumber) - 1;
- if (p + len <= end) {
- sprintf(strip_info->serial_number.c, "%.*s", len, p);
- }
- else {
- printk(KERN_ERR "STRIP: radio serial number shorter (%d) than expected (%d)\n",
- end - p, len);
- }
+ return p - buffer;
}
/*
- * This function parses the response to the ATS325? command,
- * extracting the radio battery voltage.
+ * This function is exports status information from the STRIP driver through
+ * the /proc file system.
*/
-static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end)
-{
- int len;
- len = sizeof(MetricomBatteryVoltage) - 1;
- if (ptr + len <= end) {
- sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr);
- }
- else {
- printk(KERN_ERR "STRIP: radio voltage string shorter (%d) than expected (%d)\n",
- end - ptr, len);
- }
-}
-
-/*
- * This function parses the response to the AT~I2 command,
- * which gives the names of the radio's nearest neighbors.
- * It relies on the format of the response.
- */
-static void get_radio_neighbors(struct strip *strip_info, __u8 *ptr, __u8 *end)
+static int get_status_info(char *buffer, char **start, off_t req_offset, int req_len, int dummy)
{
- __u8 *p, *line_begin;
- int num_nodes_reported, num_nodes_counted;
- MetricomNode *node, *last;
-
- /* Check if someone is reading the list */
- if (strip_info->neighbor_list_locked) {
- return;
- }
-
- /* Determine the number of Nodes */
- p = ptr;
- num_nodes_reported = simple_strtoul(p, NULL, 10);
- /* printk(KERN_DEBUG "num_nodes: %d\n", num_nodes_reported); */
-
- /* Determine the beginning of the next line */
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- p++;
+ int total = 0, slop = 0;
+ struct strip *strip_info = struct_strip_list;
+ char *buf = buffer;
- /*
- * The node list should never be empty because we allocate one empty
- * node when the strip_info is allocated. The nodes which were allocated
- * when the number of neighbors was high but are no longer needed because
- * there aren't as many neighbors any more are marked invalid. Invalid nodes
- * are kept at the end of the list.
- */
- node = strip_info->neighbor_list;
- last = node;
- if (node == NULL) {
- DumpData("Neighbor list is NULL:", strip_info, p, end);
- return;
- }
- line_begin = p;
- num_nodes_counted = 0;
- while (line_begin < end) {
- /* Check to see if the format is what we expect. */
- if ((line_begin + STRIP_NODE_LEN) > end) {
- printk(KERN_ERR "STRIP: radio neighbor node string shorter (%d) than expected (%d)\n",
- end - line_begin, STRIP_NODE_LEN);
- break;
- }
-
- /* Get a node */
- if (node == NULL) {
- node = kmalloc(sizeof(MetricomNode), GFP_ATOMIC);
- node->next = NULL;
- }
- node->type = NodeValid;
-
- /* Fill the node in */
-
- /* Determine if it has a GPS location and fill it in if it does. */
- p = line_begin;
- /* printk(KERN_DEBUG "node: %64s\n", p); */
- if (p[0] != STRIP_PORTABLE_CHAR) {
- node->type |= NodeHasWAN;
- sprintf(node->gl.s, "%.*s", (int) sizeof(GeographicLocation) - 1, p);
- }
-
- /* Determine if it is a router */
- p = line_begin + 18;
- if (p[0] == STRIP_ROUTER_CHAR) {
- node->type |= NodeIsRouter;
- }
-
- /* Could be a radio address or some weird poletop address. */
- p = line_begin + 20;
- /* printk(KERN_DEBUG "before addr: %6s\n", p); */
- string_to_radio_address(&node->addr, p);
- /* radio_address_to_string(&node->addr, addr_string);
- printk(KERN_DEBUG "after addr: %s\n", addr_string); */
-
- if (IS_RADIO_ADDRESS(p)) {
- string_to_radio_address(&node->addr, p);
- }
- else {
- memset(&node->addr, 0, sizeof(MetricomAddress));
- }
-
- /* Get the poll latency. %$#!@ simple_strtoul can't skip white space */
- p = line_begin + 41;
- while (isspace(*p) && (p < end)) {
- p++;
- }
- node->poll_latency = simple_strtoul(p, NULL, 10);
-
- /* Get the signal strength. simple_strtoul doesn't do minus signs */
- p = line_begin + 60;
- node->rssi = -simple_strtoul(p, NULL, 10);
-
- if (last != node) {
- last->next = node;
- last = node;
- }
- node = node->next;
- line_begin += STRIP_NODE_LEN;
- num_nodes_counted++;
- }
-
- /* invalidate all remaining nodes */
- for (;node != NULL; node = node->next) {
- node->type &= ~NodeValid;
- }
+ buf += sprintf(buf, "strip_version: %s\n", StripVersion);
+ if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) goto exit;
- /*
- * If the number of nodes reported is different
- * from the number counted, might need to up the number
- * requested.
- */
- if (num_nodes_reported != num_nodes_counted) {
- printk(KERN_DEBUG "nodes reported: %d \tnodes counted: %d\n",
- num_nodes_reported, num_nodes_counted);
- }
+ while (strip_info != NULL)
+ {
+ buf += sprintf_status_info(buf, strip_info);
+ if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) break;
+ strip_info = strip_info->next;
+ }
+ exit:
+ return(calc_start_len(buffer, start, req_offset, req_len, total, buf));
}
+static const char proc_strip_status_name[] = "strip";
+static struct proc_dir_entry proc_strip_get_status_info =
+{
+ PROC_NET_STRIP_STATUS, /* unsigned short low_ino */
+ sizeof(proc_strip_status_name)-1, /* unsigned short namelen */
+ proc_strip_status_name, /* const char *name */
+ S_IFREG | S_IRUGO, /* mode_t mode */
+ 1, /* nlink_t nlink */
+ 0, 0, 0, /* uid_t uid, gid_t gid, unsigned long size */
+ &proc_net_inode_operations, /* struct inode_operations * ops */
+ &get_status_info, /* int (*get_info)(...) */
+ NULL, /* void (*fill_inode)(struct inode *); */
+ NULL, NULL, NULL, /* struct proc_dir_entry *next, *parent, *subdir; */
+ NULL /* void *data; */
+};
+
/************************************************************************/
/* Sending routines */
+#define InitString "ate0q1dt**starmode"
+
static void ResetRadio(struct strip *strip_info)
-{
- static const char InitString[] = "\rat\r\rate0q1dt**starmode\r\r**";
+{
+ static const char s[] = "\r" InitString "\r**";
/* If the radio isn't working anymore, we should clear the old status information. */
if (strip_info->working)
@@ -1530,14 +1242,32 @@ static void ResetRadio(struct strip *strip_info)
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->dev.dev_addr = zero_address;
+ *(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->structured_messages = 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;
- strip_info->tty->driver.write(strip_info->tty, 0, (char *)InitString, sizeof(InitString)-1);
+ strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1);
+#ifdef EXT_COUNTERS
+ strip_info->tx_ebytes += sizeof(s) - 1;
+#endif
}
/*
@@ -1573,6 +1303,9 @@ static void strip_write_some_more(struct tty_struct *tty)
int num_written = tty->driver.write(tty, 0, 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
RestoreInterrupts(intstat);
}
else /* Else start transmission of another packet */
@@ -1583,12 +1316,21 @@ static void strip_write_some_more(struct tty_struct *tty)
}
}
-static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb)
+static __u8 *add_checksum(__u8 *buffer, __u8 *end)
{
-#if DO_PROC_NET_STRIP_TRACE
- unsigned char *start_ptr;
-#endif DO_PROC_NET_STRIP_TRACE
+ __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;
@@ -1603,7 +1345,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
{
printk(KERN_ERR "%s: strip_make_packet: Unknown packet type 0x%04X\n",
strip_info->dev.name, ntohs(header->protocol));
- strip_info->tx_dropped++;
return(NULL);
}
@@ -1611,7 +1352,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
{
printk(KERN_ERR "%s: Dropping oversized transmit packet: %d bytes\n",
strip_info->dev.name, len);
- strip_info->tx_dropped++;
return(NULL);
}
@@ -1629,6 +1369,12 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
}
}
+ /*
+ * 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)))
+ return(NULL);
*ptr++ = '*';
*ptr++ = hextable[haddr.c[2] >> 4];
*ptr++ = hextable[haddr.c[2] & 0xF];
@@ -1645,17 +1391,9 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
*ptr++ = key.c[2];
*ptr++ = key.c[3];
-#if DO_PROC_NET_STRIP_TRACE
- start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
ptr = StuffData(skb->data + sizeof(STRIP_Header), len, ptr, &stuffstate);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, skb->data + sizeof(STRIP_Header), EntrySend,
- header, len, ptr-start_ptr,
- slip_len(skb->data + sizeof(STRIP_Header), len));
-#endif DO_PROC_NET_STRIP_TRACE
+ if (strip_info->firmware_level >= ChecksummedMessages) ptr = add_checksum(buffer+1, ptr);
*ptr++ = 0x0D;
return(ptr);
@@ -1664,57 +1402,108 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
static void strip_send(struct strip *strip_info, struct sk_buff *skb)
{
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;
- /* If we have a packet, encapsulate it and put it in the buffer */
+ /*
+ * 1. If we have a packet, encapsulate it and put it in the buffer
+ */
if (skb)
{
- ptr = strip_make_packet(ptr, strip_info, skb);
- /* If error, unlock and return */
- if (!ptr) { strip_unlock(strip_info); return; }
- strip_info->tx_packets++; /* Count another successful packet */
- /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/
- /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/
- }
-
- /* Set up the strip_info ready to send the data */
- strip_info->tx_head = strip_info->tx_buff;
- strip_info->tx_left = ptr - strip_info->tx_buff;
- strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-
- /* If watchdog has expired, reset the radio */
- if ((long)jiffies - strip_info->watchdog_doreset >= 0)
- {
- ResetRadio(strip_info);
- return;
- /* Note: if there's a packet to send, strip_write_some_more
- will do it after the reset has finished */
+ 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);*/
+ }
}
- /* No reset.
- * If it is time for another tickle, tack it on the end of the packet
+ /*
+ * 2. If it is time for another tickle, tack it on, after the packet
*/
- if ((long)jiffies - strip_info->watchdog_doprobe >= 0)
+ if (doprobe)
{
- /* Send tickle to make radio protest */
- /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/
- const char *TickleString = TickleString1;
- int length = sizeof(TickleString1)-1;
- if (strip_info->structured_messages)
+ StringDescriptor ts = CommandString[strip_info->next_command];
+#if TICKLE_TIMERS
{
- TickleString = TickleString2;
- length = sizeof(TickleString2)-1;
+ 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);
}
- memcpy(ptr, TickleString, length);
- strip_info->tx_left += length;
+#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 <CR> */
+
+ /* Cycle to next periodic command? */
+ if (strip_info->firmware_level >= StructuredMessages)
+ if (++strip_info->next_command >= ELEMENTS_OF(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);*/
}
/*
- * If it is time for a periodic ARP, queue one up to be sent
+ * 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;
+ strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+ /*
+ * 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; }
+
+ /*
+ * 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)))
+ memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
+ *strip_info->dev.broadcast!=0xFF)
{
/*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
strip_info->dev.name, strip_info->arp_interval / HZ);*/
@@ -1722,16 +1511,18 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
strip_info->arp_interval *= 2;
if (strip_info->arp_interval > MaxARPInterval)
strip_info->arp_interval = MaxARPInterval;
- arp_send(ARPOP_REPLY, ETH_P_ARP, strip_info->dev.pa_addr,
- &strip_info->dev, strip_info->dev.pa_addr,
- NULL, strip_info->dev.dev_addr, NULL);
+ arp_send(ARPOP_REPLY, ETH_P_ARP,
+ strip_info->dev.pa_addr, /* Target address of ARP packet is our address */
+ &strip_info->dev, /* Device to send packet on */
+ strip_info->dev.pa_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) */
}
- 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);
-
- /* All ready. Start the transmission */
+ /*
+ * 7. All ready. Start the transmission
+ */
strip_write_some_more(strip_info->tty);
}
@@ -1752,6 +1543,33 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
if (strip_info->mtu != strip_info->dev.mtu)
strip_changedmtu(strip_info);
+ if (jiffies - strip_info->pps_timer > HZ)
+ {
+ unsigned long t = jiffies - strip_info->pps_timer;
+ unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t/2) / t;
+ unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t/2) / t;
+ unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t/2) / 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);
+ }
+
strip_send(strip_info, skb);
if (skb) dev_kfree_skb(skb, FREE_WRITE);
@@ -1759,6 +1577,17 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
}
/*
+ * 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 device *)parameter);
+}
+
+/*
* Create the MAC header for an arbitrary protocol layer
*
* saddr!=NULL means use this specific address (n/a for Metricom)
@@ -1772,19 +1601,20 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
static int strip_header(struct sk_buff *skb, struct device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
+ struct strip *strip_info = (struct strip *)(dev->priv);
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" : "");*/
- memcpy(header->src_addr.c, dev->dev_addr, dev->addr_len);
+ header->src_addr = strip_info->true_dev_addr;
header->protocol = htons(type);
/*HexDump("strip_header", (struct strip *)(dev->priv), skb->data, skb->data + skb->len);*/
if (!daddr) return(-dev->hard_header_len);
- memcpy(header->dst_addr.c, daddr, dev->addr_len);
+ header->dst_addr = *(MetricomAddress*)daddr;
return(dev->hard_header_len);
}
@@ -1811,43 +1641,130 @@ static int strip_rebuild_header(struct sk_buff *skb)
#endif
}
+
+/************************************************************************/
+/* Receiving routines */
+
+static int strip_receive_room(struct tty_struct *tty)
+{
+ return 0x10000; /* We can handle an infinite amount of data. :-) */
+}
+
/*
- * 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
+ * This function parses the response to the ATS300? command,
+ * extracting the radio version and serial number.
*/
-
-static void strip_IdleTask(unsigned long parameter)
+static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end)
{
- strip_xmit(NULL, (struct device *)parameter);
+ __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(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 (%d) 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;
-/************************************************************************/
-/* Receiving routines */
+ 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 (%d) than expected (%d)\n",
+ end - ptr, len);
+ }
+}
-static int strip_receive_room(struct tty_struct *tty)
+/*
+ * 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)
{
- return 0x10000; /* We can handle an infinite amount of data. :-) */
+ 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 void get_radio_address(struct strip *strip_info, __u8 *p)
+static int get_radio_address(struct strip *strip_info, __u8 *p)
{
MetricomAddress addr;
- string_to_radio_address(&addr, p);
+ if (string_to_radio_address(&addr, p)) return(1);
/* See if our radio address has changed */
- if (memcmp(strip_info->dev.dev_addr, addr.c, sizeof(addr)))
+ 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: My radio address = %s\n", strip_info->dev.name, addr_string.c);
- memcpy(strip_info->dev.dev_addr, addr.c, sizeof(addr));
+ 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 + 6 * HZ;
+ 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)
@@ -1860,114 +1777,156 @@ static void RecvErr(char *msg, struct strip *strip_info)
static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg)
{
- static const char ERR_001[] = "001"; /* Not in StarMode! */
- static const char ERR_002[] = "002"; /* Remap handle */
- static const char ERR_003[] = "003"; /* Can't resolve name */
- static const char ERR_004[] = "004"; /* Name too small or missing */
- static const char ERR_005[] = "005"; /* Bad count specification */
- static const char ERR_006[] = "006"; /* Header too big */
- static const char ERR_007[] = "007"; /* Body too big */
- static const char ERR_008[] = "008"; /* Bad character in name */
- static const char ERR_009[] = "009"; /* No count or line terminator */
-
- if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1))
+ if (has_prefix(msg, "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 (!strncmp(msg, ERR_002, sizeof(ERR_002)-1))
+
+ else if (has_prefix(msg, "002")) /* Remap handle */
{
- RecvErr("Error Msg:", strip_info);
-#ifdef notyet /*Kernel doesn't have scanf!*/
- int handle;
- __u8 newname[64];
- sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname);
- printk(KERN_INFO "%s: Radio name %s is handle %d\n",
- strip_info->dev.name, newname, handle);
-#endif
+ /* We ignore "Remap handle" messages for now */
}
- else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1))
+
+ else if (has_prefix(msg, "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 (!strncmp(msg, ERR_004, sizeof(ERR_004)-1))
+
+ else if (has_prefix(msg, "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);
+ 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->structured_messages && sendername)
+ 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)
{
- strip_info->structured_messages = TRUE;
- printk(KERN_INFO "%s: Radio provides structured messages\n",
- strip_info->dev.name);
+ 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 (!strncmp(msg, ERR_005, sizeof(ERR_005)-1))
+
+ else if (has_prefix(msg, "005")) /* Bad count specification */
RecvErr("Error Msg:", strip_info);
- else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1))
+
+ else if (has_prefix(msg, "006")) /* Header too big */
RecvErr("Error Msg:", strip_info);
- else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1))
+
+ else if (has_prefix(msg, "007")) /* Body too big */
{
- /*
- * Note: This error knocks the radio back into
- * command mode.
- */
RecvErr("Error Msg:", strip_info);
- printk(KERN_ERR "%s: Error! Packet size too big for radio.",
+ printk(KERN_ERR "%s: Error! Packet size too big for radio.\n",
strip_info->dev.name);
- strip_info->watchdog_doreset = jiffies; /* Do reset ASAP */
}
- else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1))
+
+ else if (has_prefix(msg, "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 (!strncmp(msg, ERR_009, sizeof(ERR_009)-1))
+
+ else if (has_prefix(msg, "009")) /* No count or line terminator */
+ RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "010")) /* Invalid checksum */
RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "011")) /* Checksum didn't match */
+ RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "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)
{
- static const char ATS305[] = "ATS305?";
- static const char ATS300[] = "ATS300?";
- static const char ATS325[] = "ATS325?";
- static const char ATI2[] = "AT~I2 nn";
-
- /* Skip to the first newline character */
__u8 *p = ptr;
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- p++;
+ 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. */
- if (!strncmp(ptr, ATS305, sizeof(ATS305)-1))
+#if TICKLE_TIMERS
{
- if (IS_RADIO_ADDRESS(p)) get_radio_address(strip_info, p);
- }
- else if (!strncmp(ptr, ATS300, sizeof(ATS300)-1)) {
- get_radio_version(strip_info, p, end);
- }
- else if (!strncmp(ptr, ATS325, sizeof(ATS325)-1)) {
- get_radio_voltage(strip_info, p, end);
+ 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);
}
- else if (!strncmp(ptr, ATI2, sizeof(ATI2)-1)) {
- get_radio_neighbors(strip_info, p, end);
+#endif
+
+ if (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end);
+ else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p);
+ else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end);
+ else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info);
+ else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end);
+ else if (has_prefix(ptr, "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 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 device *dev = dev_base;
+ while (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);
+ return(dev);
+ }
+ dev = dev->next;
+ }
}
- else RecvErr("Unknown AT Response:", strip_info);
+ return(&strip_info->dev);
}
/*
@@ -1979,14 +1938,14 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16
struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen);
if (!skb)
{
- printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", strip_info->dev.name);
+ 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 = &strip_info->dev;
+ skb->dev = get_strip_dev(strip_info);
skb->protocol = header->protocol;
skb->mac.raw = skb->data;
@@ -1997,6 +1956,10 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16
/* 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);
}
}
@@ -2005,10 +1968,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
{
__u16 packetlen;
-#if DO_PROC_NET_STRIP_TRACE
- __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
/* Decode start of the IP packet header */
ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4);
if (!ptr)
@@ -2019,9 +1978,9 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3];
- if (packetlen > MAX_STRIP_MTU)
+ if (packetlen > MAX_RECV_MTU)
{
- printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+ printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n",
strip_info->dev.name, packetlen);
strip_info->rx_dropped++;
return;
@@ -2045,11 +2004,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
header->protocol = htons(ETH_P_IP);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
- packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen));
-#endif DO_PROC_NET_STRIP_TRACE
-
deliver_packet(strip_info, header, packetlen);
}
@@ -2058,10 +2012,6 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
__u16 packetlen;
struct arphdr *arphdr = (struct arphdr *)strip_info->rx_buff;
-#if DO_PROC_NET_STRIP_TRACE
- __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
/* Decode start of the ARP packet */
ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8);
if (!ptr)
@@ -2072,9 +2022,9 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2;
- if (packetlen > MAX_STRIP_MTU)
+ if (packetlen > MAX_RECV_MTU)
{
- printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+ printk(KERN_INFO "%s: Dropping oversized received ARP packet: %d bytes\n",
strip_info->dev.name, packetlen);
strip_info->rx_dropped++;
return;
@@ -2100,15 +2050,47 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
header->protocol = htons(ETH_P_ARP);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
- packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen));
-#endif DO_PROC_NET_STRIP_TRACE
-
deliver_packet(strip_info, header, packetlen);
}
-static void process_packet(struct strip *strip_info)
+/*
+ * process_text_message processes a <CR>-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 (text_equal(msg, len, InitString)) 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, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; }
+
+ RecvErr("No initial *", strip_info);
+}
+
+/*
+ * process_message processes a <CR>-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;
@@ -2116,29 +2098,11 @@ static void process_packet(struct strip *strip_info)
__u8 sendername[32], *sptr = sendername;
MetricomKey key;
- /* Ignore 'OK' responses from prior commands */
- if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') return;
-
- /* Check for anything that looks like it might be our radio name: dddd-dddd */
- /* (This is here for backwards compatibility with old firmware) */
- if (strip_info->sx_count == 9 && IS_RADIO_ADDRESS(ptr))
- {
- get_radio_address(strip_info, ptr);
- return;
- }
-
/*HexDump("Receiving", strip_info, ptr, end);*/
/* Check for start of address marker, and then skip over it */
- if (*ptr != '*')
- {
- /* Catch other error messages */
- if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_')
- RecvErr_Message(strip_info, NULL, &ptr[4]);
- else RecvErr("No initial *", strip_info);
- return;
- }
- ptr++; /* Skip the initial '*' */
+ 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++;
@@ -2156,55 +2120,85 @@ static void process_packet(struct strip *strip_info)
/* (This is here for backwards compatibility with old firmware) */
if (!strcmp(sendername, "&COMMAND"))
{
- strip_info->structured_messages = FALSE;
+ strip_info->firmware_level = NoStructure;
+ strip_info->next_command = CompatibilityCommand;
return;
}
- if (ptr+4 >= end)
+ if (ptr+4 > end)
{
RecvErr("No proto key", 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.
- */
- memcpy(&header.dst_addr, strip_info->dev.dev_addr, sizeof(MetricomAddress));
- if (IS_RADIO_ADDRESS(sendername)) string_to_radio_address(&header.src_addr, sendername);
-
/* 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 (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 == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
- else /* RecvErr("Unrecognized protocol key", strip_info); */
-
- /* Note, this "else" block is temporary, until Metricom fix their */
- /* packet corruption bug */
+ /* If we're using checksums, verify the checksum at the end of the packet */
+ if (strip_info->firmware_level >= ChecksummedMessages)
{
- RecvErr("Unrecognized protocol key (retrying)", strip_info);
- ptr -= 3; /* Back up and try again */
- key.c[0] = *ptr++;
- key.c[1] = *ptr++;
- key.c[2] = *ptr++;
- key.c[3] = *ptr++;
- 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 == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
- else RecvErr("Unrecognized protocol key", strip_info);
+ 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);
+ } 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);
+ 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
@@ -2229,17 +2223,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
{
struct timeval tv;
do_gettimeofday(&tv);
- printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %d.%06d\n",
+ 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 */
{
- strip_info->discard = 1;
+ /* If we have some characters in the buffer, discard them */
+ strip_info->discard = strip_info->sx_count;
strip_info->rx_errors++;
}
@@ -2249,21 +2249,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
if (*cp == 0x0D) /* If end of packet, decide what to do with it */
{
if (strip_info->sx_count > 3000)
- printk(KERN_INFO "Cut a %d byte packet (%d bytes remaining)%s\n",
- strip_info->sx_count, end-cp-1,
+ printk(KERN_INFO "%s: Cut a %d byte packet (%d 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->discard = 1;
strip_info->rx_over_errors++;
printk(KERN_INFO "%s: sx_buff overflow (%d bytes total)\n",
strip_info->dev.name, strip_info->sx_count);
}
- if (!strip_info->discard) process_packet(strip_info);
+ 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 if (!strip_info->discard) /* If we're not discarding, store the character */
+ else
{
/* Make sure we have space in the buffer */
if (strip_info->sx_count < strip_info->sx_size)
@@ -2279,9 +2281,28 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
/************************************************************************/
/* General control routines */
-static int strip_set_dev_mac_address(struct device *dev, void *addr)
+static int set_mac_address(struct strip *strip_info, MetricomAddress *addr)
{
- return -1; /* You cannot override a Metricom radio's address */
+ /*
+ * 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 dev_set_mac_address(struct device *dev, void *addr)
+{
+ struct strip *strip_info = (struct strip *)(dev->priv);
+ 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 device *dev)
@@ -2341,7 +2362,8 @@ static int strip_open_low(struct device *dev)
strip_info->discard = 0;
strip_info->working = FALSE;
- strip_info->structured_messages = FALSE;
+ strip_info->firmware_level = NoStructure;
+ strip_info->next_command = CompatibilityCommand;
strip_info->sx_count = 0;
strip_info->tx_left = 0;
@@ -2442,7 +2464,7 @@ static int strip_dev_init(struct device *dev)
dev->rebuild_header = strip_rebuild_header;
/* dev->type_trans unused */
/* dev->set_multicast_list unused */
- dev->set_mac_address = strip_set_dev_mac_address;
+ dev->set_mac_address = dev_set_mac_address;
/* dev->do_ioctl unused */
/* dev->set_config unused */
dev->get_stats = strip_get_stats;
@@ -2455,19 +2477,10 @@ static int strip_dev_init(struct device *dev)
static void strip_free(struct strip *strip_info)
{
- MetricomNode *node, *free;
-
*(strip_info->referrer) = strip_info->next;
if (strip_info->next)
strip_info->next->referrer = strip_info->referrer;
strip_info->magic = 0;
-
- for (node = strip_info->neighbor_list; node != NULL; )
- {
- free = node;
- node = node->next;
- kfree(free);
- }
kfree(strip_info);
}
@@ -2522,13 +2535,9 @@ static struct strip *strip_alloc(void)
strip_info->idle_timer.data = (long)&strip_info->dev;
strip_info->idle_timer.function = strip_IdleTask;
- strip_info->neighbor_list = kmalloc(sizeof(MetricomNode), GFP_KERNEL);
- strip_info->neighbor_list->type = 0;
- strip_info->neighbor_list->next = NULL;
-
/* Note: strip_info->if_name is currently 8 characters long */
- sprintf(strip_info->if_name, "st%d", channel_id);
- strip_info->dev.name = strip_info->if_name;
+ sprintf(strip_info->if_name.c, "st%d", channel_id);
+ strip_info->dev.name = strip_info->if_name.c;
strip_info->dev.base_addr = channel_id;
strip_info->dev.priv = (void*)strip_info;
strip_info->dev.next = NULL;
@@ -2598,6 +2607,9 @@ static int strip_open(struct tty_struct *tty)
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
+
+ printk(KERN_INFO "STRIP: device \"%s\" activated\n", strip_info->if_name.c);
+
/*
* Done. We have linked the TTY line to a channel.
*/
@@ -2627,6 +2639,7 @@ static void strip_close(struct tty_struct *tty)
tty->disc_data = 0;
strip_info->tty = NULL;
+ printk(KERN_INFO "STRIP: device \"%s\" closed down\n", strip_info->if_name.c);
strip_free(strip_info);
tty->disc_data = NULL;
#ifdef MODULE
@@ -2657,12 +2670,17 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
err = verify_area(VERIFY_WRITE, (void*)arg, 16);
if (err)
return -err;
- copy_to_user((void*)arg, strip_info->dev.name,
- strlen(strip_info->dev.name) + 1);
- return 0;
+ return copy_to_user((void*)arg, strip_info->dev.name,
+ strlen(strip_info->dev.name) + 1)?-EFAULT:0;
case SIOCSIFHWADDR:
- return -EINVAL;
+ {
+ MetricomAddress addr;
+ printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name);
+ if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress)))
+ return -EFAULT;
+ return(set_mac_address(strip_info, &addr));
+ }
/*
* Allow stty to read, but not set, the serial port
@@ -2683,32 +2701,6 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
/* Initialization */
/*
- * Registers with the /proc file system to create different /proc/net files.
- */
-
-static int strip_proc_net_register(unsigned short type, char *file_name,
- int (*get_info)(char *, char **, off_t, int, int))
-{
- struct proc_dir_entry *strip_entry;
-
- strip_entry = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC);
-
- memset(strip_entry, 0, sizeof(struct proc_dir_entry));
- strip_entry->low_ino = type;
- strip_entry->namelen = strlen(file_name);
- strip_entry->name = file_name;
- strip_entry->mode = S_IFREG | S_IRUGO;
- strip_entry->nlink = 1;
- strip_entry->uid = 0;
- strip_entry->gid = 0;
- strip_entry->size = 0;
- strip_entry->ops = &proc_net_inode_operations;
- strip_entry->get_info = get_info;
-
- return proc_net_register(strip_entry);
-}
-
-/*
* Initialize the STRIP driver.
* This routine is called at boot time, to bootstrap the multi-channel
* STRIP driver
@@ -2722,7 +2714,7 @@ int strip_init_ctrl_dev(struct device *dummy)
static struct tty_ldisc strip_ldisc;
int status;
- printk("STRIP: version %s (unlimited channels)\n", StripVersion);
+ printk(KERN_INFO "STRIP: Version %s (unlimited channels)\n", StripVersion);
/*
* Fill in our line protocol discipline, and register it
@@ -2747,24 +2739,12 @@ int strip_init_ctrl_dev(struct device *dummy)
}
/*
- * Register the status and trace files with /proc
+ * Register the status file with /proc
*/
-
-#if DO_PROC_NET_STRIP_STATUS
- if (strip_proc_net_register(PROC_NET_STRIP_STATUS, "strip_status",
- &strip_get_status_info) != 0)
+ if (proc_net_register(&proc_strip_get_status_info) != 0)
{
- printk(KERN_ERR "strip: status strip_proc_net_register() failed.\n");
+ printk(KERN_ERR "strip: status proc_net_register() failed.\n");
}
-#endif
-
-#if DO_PROC_NET_STRIP_TRACE
- if (strip_proc_net_register(PROC_NET_STRIP_TRACE, "strip_trace",
- &strip_get_trace_info) != 0)
- {
- printk(KERN_ERR "strip: trace strip_proc_net_register() failed.\n");
- }
-#endif
#ifdef MODULE
return status;
@@ -2794,16 +2774,12 @@ void cleanup_module(void)
while (struct_strip_list)
strip_free(struct_strip_list);
- /* Unregister with the /proc/net files here. */
-
-#if DO_PROC_NET_STRIP_TRACE
- proc_net_unregister(PROC_NET_STRIP_TRACE);
-#endif
-#if DO_PROC_NET_STRIP_STATUS
+ /* Unregister with the /proc/net file here. */
proc_net_unregister(PROC_NET_STRIP_STATUS);
-#endif
if ((i = tty_register_ldisc(N_STRIP, NULL)))
printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i);
+
+ printk(KERN_INFO "STRIP: Module Unloaded\n");
}
#endif /* MODULE */
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 7299b0f4b..dca243c4f 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -946,7 +946,8 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
- hb->happy_meal_rxd[i].rx_addr = (unsigned int) skb->data;
+ hb->happy_meal_rxd[i].rx_addr =
+ (u32) ((unsigned long)skb->data);
skb_reserve(skb, RX_OFFSET);
hb->happy_meal_rxd[i].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1615,7 +1616,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
/* Return it to the Happy meal. */
drop_it:
hp->net_stats.rx_dropped++;
- this->rx_addr = (unsigned int) hp->rx_skbs[elem]->data;
+ this->rx_addr =
+ (u32) ((unsigned long)hp->rx_skbs[elem]->data);
this->rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
goto next;
@@ -1634,7 +1636,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
hp->rx_skbs[elem] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
- rxbase[elem].rx_addr = (unsigned int) new_skb->data;
+ rxbase[elem].rx_addr =
+ (u32) ((unsigned long)new_skb->data);
skb_reserve(new_skb, RX_OFFSET);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1655,7 +1658,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
memcpy(copy_skb->data, skb->data, len);
/* Reuse original ring buffer. */
- rxbase[elem].rx_addr = (unsigned int) skb->data;
+ rxbase[elem].rx_addr =
+ (u32) ((unsigned long)skb->data);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1913,7 +1917,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
SXD(("SX<l[%d]e[%d]>", len, entry));
hp->tx_skbs[entry] = skb;
- hp->happy_block->happy_meal_txd[entry].tx_addr = (unsigned int) skb->data;
+ hp->happy_block->happy_meal_txd[entry].tx_addr =
+ (u32) ((unsigned long)skb->data);
hp->happy_block->happy_meal_txd[entry].tx_flags =
(TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE));
hp->tx_new = NEXT_TX(entry);
@@ -2087,6 +2092,8 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
printk("\n");
hp = (struct happy_meal *) dev->priv;
+ memset(hp, 0, sizeof(*hp));
+
hp->happy_sbus_dev = sdev;
if(sdev->num_registers != 5) {
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 18e758c80..6b417e34b 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -152,8 +152,8 @@ static void qe_init_rings(struct sunqe *qep, int from_irq)
skb_put(skb, ETH_FRAME_LEN);
skb_reserve(skb, 34);
- /* FIX FOR ULTRA */
- qb->qe_rxd[i].rx_addr = (unsigned int) skb->data;
+ qb->qe_rxd[i].rx_addr =
+ (unsigned int) ((unsigned long)skb->data);
qb->qe_rxd[i].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
}
@@ -491,7 +491,8 @@ static inline void qe_rx(struct sunqe *qep)
drop_it:
/* Return it to the QE. */
qep->net_stats.rx_dropped++;
- this->rx_addr = (unsigned int) qep->rx_skbs[elem]->data;
+ this->rx_addr =
+ (unsigned int) ((unsigned long)qep->rx_skbs[elem]->data);
this->rx_flags =
(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
goto next;
@@ -512,8 +513,8 @@ static inline void qe_rx(struct sunqe *qep)
skb_put(new_skb, ETH_FRAME_LEN);
skb_reserve(new_skb, 34);
- /* FIX FOR ULTRA */
- rxbase[elem].rx_addr = (unsigned int) new_skb->data;
+ rxbase[elem].rx_addr =
+ (unsigned int) ((unsigned long)new_skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
@@ -533,7 +534,8 @@ static inline void qe_rx(struct sunqe *qep)
eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0);
/* Reuse original ring buffer. */
- rxbase[elem].rx_addr = (unsigned int) skb->data;
+ rxbase[elem].rx_addr =
+ (unsigned int) ((unsigned long)skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
new file mode 100644
index 000000000..d3d9d0e1c
--- /dev/null
+++ b/drivers/net/tlan.c
@@ -0,0 +1,2309 @@
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.c
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4 and colums>=132.
+ *
+ ** Useful (if not required) reading:
+ *
+ * Texas Instruments, ThunderLAN Programmer's Guide,
+ * TI Literature Number SPWU013A
+ * available in PDF format from www.ti.com
+ * National Semiconductor, DP83840A Data Sheet
+ * available in PDF format from www.national.com
+ * Microchip Technology, 24C01A/02A/04A Data Sheet
+ * available in PDF format from www.microchip.com
+ *
+ ********************************************************************/
+
+
+#include <linux/module.h>
+
+
+#include "tlan.h"
+
+
+#include <linux/bios32.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+
+
+
+
+#ifdef MODULE
+ static struct device *TLanDevices = NULL;
+ static int TLanDevicesInstalled = 0;
+#endif
+ static int debug = 0;
+ static u8 *TLanPadBuffer;
+ static char TLanSignature[] = "TLAN";
+ static int TLanVersionMajor = 0;
+ static int TLanVersionMinor = 27;
+
+ static TLanPciId TLanDeviceList[] = {
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3_INTEGRATED, "Compaq Integrated NetFlex-3" },
+ { 0, 0, NULL } /* End of List */
+ };
+
+
+ static int TLan_MiiReadReg(u16, u16, u16, u16 *);
+ static void TLan_MiiSendData( u16, u32, unsigned );
+ static void TLan_MiiSync(u16);
+ static void TLan_MiiWriteReg(u16, u16, u16, u16);
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver MII Routines
+
+ These routines are based on the information in Chap. 2 of the
+ "ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /*************************************************************************
+ * TLan_MiiReadReg
+ *
+ * Returns: 0 if ack received ok, 1 otherwise.
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be queried.
+ * reg The register whose contents are to be
+ * retreived.
+ * val A pointer to a variable to store the retrieved
+ * value.
+ *
+ * This function uses the TLAN's MII bus to retreive the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * reads the 16-bit register value from the MII bus via the TLAN SIO
+ * register.
+ *
+ ************************************************************************/
+
+ int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
+ {
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+
+ err = FALSE;
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ cli();
+
+ TLan_MiiSync(base_port);
+
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); /* Disable PHY ints */
+
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */
+
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACKed, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); /* Enable PHY ints */
+
+ *val = tmp;
+
+ sti();
+
+ return err;
+
+ } /* TLan_MiiReadReg */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiSendData
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be queried.
+ * data The value to be placed on the MII bus.
+ * num_bits The number of bits in data that are to be
+ * placed on the MII bus.
+ *
+ * This function sends on sequence of bits on the MII configuration
+ * bus.
+ *
+ ************************************************************************/
+
+ void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
+ {
+ u16 sio;
+ u32 i;
+
+ if ( num_bits == 0 )
+ return;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
+
+ for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ if ( data & i )
+ TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+ } /* TLan_MiiSendData */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiSync
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ *
+ * This functions syncs all PHYs in terms of the MII configuration bus.
+ *
+ ************************************************************************/
+
+ void TLan_MiiSync( u16 base_port )
+ {
+ int i;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
+ for ( i = 0; i < 32; i++ ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+ } /* TLan_MiiSync */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiWriteReg
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be written to.
+ * reg The register whose contents are to be
+ * written.
+ * val The value to be written to the register.
+ *
+ * This function uses the TLAN's MII bus to write the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * writes the 16-bit register value from the MII configuration bus
+ * via the TLAN SIO register.
+ *
+ ************************************************************************/
+
+ void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
+ {
+ u16 sio;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ cli();
+
+ TLan_MiiSync( base_port );
+
+ TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */
+ TLan_MiiSendData( base_port, val, 16 ); /* Send Data */
+
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+
+ TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
+
+ sti();
+
+ } /* TLan_MiiWriteReg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver PHY Layer Routines
+
+ The TLAN chip can drive any number of PHYs (physical devices). Rather
+ than having lots of 'if' or '#ifdef' statements, I have created a
+ second driver layer for the PHYs. Each PHY can be identified from its
+ id in registers 2 and 3, and can be given a Check and Service routine
+ that will be called when the adapter is reset and when the adapter
+ receives a Network Status interrupt, respectively.
+
+******************************************************************************
+*****************************************************************************/
+
+ static int TLan_PhyNop( struct device * );
+ static void TLan_PhyPrint( struct device * );
+ static void TLan_PhySelect( struct device * );
+ static int TLan_PhyInternalCheck( struct device * );
+ static int TLan_PhyInternalService( struct device * );
+ static int TLan_PhyDp83840aCheck( struct device * );
+
+
+
+
+ static TLanPhyIdEntry TLanPhyIdTable[] = {
+ { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
+ { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
+ { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x0000, 0x0000, NULL, NULL, 0 }
+ };
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyPrint
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to the device structure of the adapter
+ * which the desired PHY is located.
+ *
+ * This function prints the registers a PHY.
+ *
+ ************************************************************************/
+
+ void TLan_PhyPrint( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 i, data0, data1, data2, data3, phy;
+ u32 io;
+
+ phy = priv->phyAddr;
+ io = dev->base_addr;
+
+ if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) {
+ printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
+ printk( "TLAN: Off. +0 +1 +2 +3 \n" );
+ for ( i = 0; i < 0x20; i+= 4 ) {
+ TLan_MiiReadReg( io, phy, i, &data0 );
+ TLan_MiiReadReg( io, phy, i + 1, &data1 );
+ TLan_MiiReadReg( io, phy, i + 2, &data2 );
+ TLan_MiiReadReg( io, phy, i + 3, &data3 );
+ printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 );
+ }
+ } else {
+ printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy );
+ }
+
+ } /* TLan_PhyPrint */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhySelect
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to the device structure of the adapter
+ * for which the PHY needs determined.
+ *
+ * This function decides which PHY amoung those attached to the TLAN chip
+ * is to be used. The TLAN chip can be attached to multiple PHYs, and
+ * the driver needs to decide which one to talk to. Currently this
+ * routine picks the PHY with the lowest address as the internal PHY
+ * address is 0x1F, the highest possible. This strategy assumes that
+ * there can be only one other PHY, and, if it exists, it is the one to
+ * be used. If token ring PHYs are ever supported, this routine will
+ * become a little more interesting...
+ *
+ ************************************************************************/
+
+ void TLan_PhySelect( struct device *dev )
+ {
+ int err;
+ int phy;
+ int entry;
+ u16 id_hi;
+ u16 id_lo;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 val;
+
+ priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL
+ priv->phyService = &TLan_PhyNop;
+
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val );
+ if ( ! err ) {
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi );
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo );
+ entry = 0;
+ while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) {
+ if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLanPhyIdTable[entry].check;
+ priv->phyService = TLanPhyIdTable[entry].service;
+ priv->phyFlags = TLanPhyIdTable[entry].flags;
+ break;
+ }
+ entry++;
+ }
+ break;
+ }
+ }
+
+ } /* TLan_PhySelect */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyNop
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure.
+ *
+ * This function does nothing and is meant as a stand-in for when
+ * a Check or Service function would be meaningless.
+ *
+ ************************************************************************/
+
+ int TLan_PhyNop( struct device *dev )
+ {
+ dev = NULL;
+ return 0;
+
+ } /* TLan_PhyNop */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyInternalCheck
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be checked.
+ *
+ * This function resets the internal PHY on a TLAN chip. See Chap. 7,
+ * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide"
+ *
+ ************************************************************************/
+
+ int TLan_PhyInternalCheck( struct device *dev )
+ {
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+
+ return 0;
+
+ } /* TLanPhyInternalCheck */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyInternalService
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be serviced.
+ *
+ * This function services an interrupt generated by the internal PHY.
+ * It can turn on/off the link LED. See Chap. 7,
+ * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide".
+ *
+ ************************************************************************/
+
+ int TLan_PhyInternalService( struct device *dev )
+ {
+ u16 tlphy_sts;
+ u16 gen_sts;
+ u16 an_exp;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
+ TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ return 0;
+
+ } /* TLan_PhyInternalService */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyDp83840aCheck
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be reset.
+ *
+ * This function resets a National Semiconductor DP83840A 10/100 Mb/s
+ * PHY device. See National Semiconductor's data sheet for more info.
+ * This PHY is used on Compaq Netelligent 10/100 cards.
+ *
+ ************************************************************************/
+
+ static int TLan_PhyDp83840aCheck( struct device *dev )
+ {
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
+
+ for ( i = 0; i < 50000; i++ )
+ SLOW_DOWN_IO;
+
+/*
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+*/
+ priv->phyOnline = 1;
+
+ return 0;
+
+ } /* TLan_PhyDp83840aCheck */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Adapter Related Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+
+ static void TLan_ResetLists( struct device * );
+ static void TLan_PrintDio( u16 );
+ static void TLan_PrintList( TLanList *, char *, int );
+ static void TLan_ReadAndClearStats( struct device *, int );
+ static int TLan_Reset( struct device * );
+ static void TLan_SetMac( struct device *, int areg, char *mac );
+
+
+
+
+ /*************************************************************************
+ * TLan_ResetLists
+ *
+ * Returns: Nothing
+ * Parms: dev The device structure with the list stuctures to
+ * be reset.
+ *
+ * This routine sets the variables associated with managing the TLAN
+ * lists to their initial values.
+ *
+ ************************************************************************/
+
+ void TLan_ResetLists( struct device *dev )
+ {
+ int i;
+ TLanList *list;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ priv->txHead = 0;
+ priv->txTail = 0;
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ list->cStat = TLAN_CSTAT_UNUSED;
+ list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ }
+
+ priv->rxHead = 0;
+ priv->rxTail = TLAN_NUM_RX_LISTS - 1;
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ list->cStat = TLAN_CSTAT_READY;
+ list->frameSize = TLAN_MAX_FRAME_SIZE;
+ list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ list->buffer[1].count = 0;
+ list->buffer[1].address = 0;
+ if ( i < TLAN_NUM_RX_LISTS - 1 )
+ list->forward = virt_to_bus( list + 1 );
+ else
+ list->forward = 0;
+ }
+
+ } /* TLan_ResetLists */
+
+
+
+
+ /*************************************************************************
+ * TLan_PrintDio
+ *
+ * Returns: Nothing
+ * Parms: io_base Base IO port of the device of which to print
+ * DIO registers.
+ *
+ * This function prints out all the the internal (DIO) registers of a
+ * TLAN chip.
+ *
+ ************************************************************************/
+
+ void TLan_PrintDio( u16 io_base )
+ {
+ u32 data0, data1;
+ int i;
+
+ printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base );
+ printk( "TLAN: Off. +0 +4\n" );
+ for ( i = 0; i < 0x4C; i+= 8 ) {
+ data0 = TLan_DioRead32( io_base, i );
+ data1 = TLan_DioRead32( io_base, i + 0x4 );
+ printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 );
+ }
+
+ } /* TLan_PrintDio */
+
+
+
+
+ /*************************************************************************
+ * TLan_PrintList
+ *
+ * Returns: Nothing
+ * Parms: list A pointer to the TLanList structure to be printed.
+ * type A string to designate type of list, "Rx" or "Tx".
+ * num The index of the list.
+ *
+ * This function prints out the contents of the list pointed to by the
+ * list parameter.
+ *
+ ************************************************************************/
+
+ void TLan_PrintList( TLanList *list, char *type, int num)
+ {
+ int i;
+
+ printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list );
+ printk( "TLAN: Forward = 0x%08x\n", list->forward );
+ printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
+ printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
+ for ( i = 0; i < 10; i++ ) {
+ printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
+ }
+
+ } /* TLan_PrintList */
+
+
+
+
+ /*************************************************************************
+ * TLan_ReadAndClearStats
+ *
+ * Returns: Nothing
+ * Parms: dev Pointer to device structure of adapter to which
+ * to read stats.
+ * record Flag indicating whether to add
+ *
+ * This functions reads all the internal status registers of the TLAN
+ * chip, which clears them as a side effect. It then either adds the
+ * values to the device's status struct, or discards them, depending
+ * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0).
+ *
+ ************************************************************************/
+
+ void TLan_ReadAndClearStats( struct device *dev, int record )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 tx_good, tx_under;
+ u32 rx_good, rx_over;
+ u32 def_tx, crc, code;
+ u32 multi_col, single_col;
+ u32 excess_col, late_col, loss;
+
+ outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ rx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
+ def_tx = inb( dev->base_addr + TLAN_DIO_DATA );
+ def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ code = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ multi_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
+
+ outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
+ loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+
+ if ( record ) {
+ priv->stats.rx_packets += rx_good;
+ priv->stats.rx_errors += rx_over + crc + code;
+ priv->stats.tx_packets += tx_good;
+ priv->stats.tx_errors += tx_under + loss;
+ priv->stats.collisions += multi_col + single_col + excess_col + late_col;
+
+ priv->stats.rx_over_errors += rx_over;
+ priv->stats.rx_crc_errors += crc;
+ priv->stats.rx_frame_errors += code;
+
+ priv->stats.tx_aborted_errors += tx_under;
+ priv->stats.tx_carrier_errors += loss;
+ }
+
+ } /* TLan_ReadAndClearStats */
+
+
+
+
+ /*************************************************************************
+ * TLan_Reset
+ *
+ * Returns: 0
+ * Parms: dev Pointer to device structure of adapter to be
+ * reset.
+ *
+ * This function resets the adapter and it's physical device. See
+ * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details.
+ * The routine tries to implement what is detailed there, though
+ * adjustments have been made.
+ *
+ ************************************************************************/
+
+ int TLan_Reset( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ u32 data;
+ u8 data8;
+
+ // 1. Assume Stat registers have been dealt with.
+ // 2. Assert reset bit.
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+ // 3. Turn off interrupts.
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+ // 4. Setup AREGs and HASHs.
+ for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 )
+ TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
+ // 5. Setup NetConfig register.
+ outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR);
+ outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA);
+ // 6. Setup BSIZE register.
+ // Accept defaults, 0x22, for now.
+ // 7. Setup TX commit in Acommit.
+ // Allow it to manage itself.
+ // 8. Load Ld_Tmr in HOST_CMD.
+ // I don't know what this value should be. I'll try 3.
+ outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
+ // 9. Load Ld_Thr in HOST_CMD.
+ // Try 1 for now.
+ outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
+ // 10. Unreset the MII by setting NMRST (in NetSio) to 1.
+ outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
+ TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO );
+ // 10a. Other.
+ // 12. Setup the NetMask register.
+ TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC );
+ //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP );
+ // 11. Initialize PHYs.
+ TLan_PhySelect( dev );
+ (*priv->phyCheck)( dev );
+ if ( debug >= 1 )
+ TLan_PhyPrint( dev );
+
+ //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7;
+ data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
+ TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 );
+ /*
+ * 13. Turn on interrupts to host.
+ * I don't want any interrupts, yet.
+ */
+ /*
+ data = inl(base_port + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_ON;
+ outl(data, base_port + TLAN_HOST_CMD);
+ */
+
+ return 0;
+
+ } /* TLan_Reset */
+
+
+
+
+ /*************************************************************************
+ * TLan_SetMac
+ *
+ * Returns: Nothing
+ * Parms: dev Pointer to device structure of adapter on which to
+ * change the AREG.
+ * areg The AREG to set the address in (0 - 3).
+ * mac A pointer to an array of chars. Each element
+ * stores one byte of the address. IE, it isn't
+ * in ascii.
+ *
+ * This function transfers a MAC address to one of the TLAN AREGs
+ * (address registers). The TLAN chip locks the register on writing to
+ * offset 0 and unlocks the register after writing to offset 5. If NULL
+ * is passed in mac, then the AREG is filled with 0's.
+ *
+ ************************************************************************/
+
+ void TLan_SetMac( struct device *dev, int areg, char *mac )
+ {
+ int i;
+
+ areg *= 6;
+
+ if ( mac != NULL ) {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+ } else {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+ }
+
+ } /* TLan_SetMac */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Eeprom routines
+
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM.
+ These functions are based on information in Microchip's data sheet. I
+ don't know how well this functions will work with other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+ static void TLan_EeSendStart( u16 );
+ static int TLan_EeSendByte( u16, u8, int );
+ static void TLan_EeReceiveByte( u16, u8 *, int );
+ static int TLan_EeReadByte( u16, u8, u8 * );
+
+
+
+
+ /*************************************************************************
+ * TLan_EeSendStart
+ *
+ * Returns: Nothing
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ *
+ * This function sends a start cycle to an EEPROM attached to a TLAN
+ * chip.
+ *
+ ************************************************************************/
+
+ void TLan_EeSendStart( u16 io_base )
+ {
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ //
+ // FIXME: Do I really need these SLOW_DOWN_IOs?
+ //
+ SLOW_DOWN_IO;
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ SLOW_DOWN_IO;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ SLOW_DOWN_IO;
+
+ } /* TLan_EeSendStart */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeSendByte
+ *
+ * Returns: If the correct ack was received, 0, otherwise 1
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * data The 8 bits of information to send to the
+ * EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
+ * sent after the byte is sent after the ack is
+ * read.
+ *
+ * This function sends a byte on the serial EEPROM line, driving the
+ * clock to send each bit. The function then reverses transmission
+ * direction and reads an acknowledge bit.
+ *
+ ************************************************************************/
+
+ int TLan_EeSendByte( u16 io_base, u8 data, int stop )
+ {
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ // Assume clock is low, tx is enabled;
+ for ( place = 0x80; place; place >>= 1 ) {
+ if ( place & data )
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+
+ if ( ( ! err ) && stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+ return ( err );
+
+ } /* TLan_EeSendByte */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeReceiveByte
+ *
+ * Returns: Nothing
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * data An address to a char to hold the data sent
+ * from the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
+ * sent after the byte is received, and no ack is
+ * sent.
+ *
+ * This function receives 8 bits of data from the EEPROM over the serial
+ * link. It then sends and ack bit, or no ack and a stop bit. This
+ * function is used to retrieve data after the address of a byte in the
+ * EEPROM has been sent.
+ *
+ ************************************************************************/
+
+ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
+ {
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ // Assume clock is low, tx is enabled;
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ for ( place = 0x80; place; place >>= 1 ) {
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
+ *data |= place;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ if ( ! stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ } else {
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?)
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+ } /* TLan_EeReceiveByte */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeReadByte
+ *
+ * Returns: No error = 0, else, the stage at which the error occured.
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * ee_addr The address of the byte in the EEPROM whose
+ * contents are to be retrieved.
+ * data An address to a char to hold the data obtained
+ * from the EEPROM.
+ *
+ * This function reads a byte of information from an byte cell in the
+ * EEPROM.
+ *
+ ************************************************************************/
+
+ int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
+ {
+ int err;
+
+ cli();
+
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
+ if (err)
+ return 1;
+ err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
+ if (err)
+ return 2;
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
+ if (err)
+ return 3;
+ TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
+
+ sti();
+
+ return 0;
+
+ } /* TLan_EeReadByte */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Interrupt Vectors and Table
+
+ Please see Chap. 4, "Interrupt Handling" of the
+ "ThunderLAN Programmer's Guide" for more informations on handling
+ interrupts generated by TLAN based adapters.
+
+******************************************************************************
+*****************************************************************************/
+
+ static u32 TLan_HandleInvalid( struct device *, u16 );
+ static u32 TLan_HandleTxEOF( struct device *, u16 );
+ static u32 TLan_HandleStatOverflow( struct device *, u16 );
+ static u32 TLan_HandleRxEOF( struct device *, u16 );
+ static u32 TLan_HandleDummy( struct device *, u16 );
+ static u32 TLan_HandleTxEOC( struct device *, u16 );
+ static u32 TLan_HandleStatusCheck( struct device *, u16 );
+ static u32 TLan_HandleRxEOC( struct device *, u16 );
+
+
+
+
+ typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleInvalid
+ *
+ * Returns: 0
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles invalid interrupts. This should never happen
+ * unless some other adapter is trying to use the IRQ line assigned to
+ * the device.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ // printk( "TLAN: Invalid interrupt on %s.\n", dev->name );
+ return 0;
+
+ } /* TLan_HandleInvalid */
+
+
+ /*************************************************************************
+ * TLan_HandleTxEOF
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles Tx EOF interrupts which are raised by the
+ * adapter when it has completed sending the contents of a buffer. If
+ * detemines which list/buffer was completed and resets it. If the
+ * buffer was the last in the channel (EOC), then the function checks
+ * to see if another buffer is ready to send, and if so, sends a Tx Go
+ * command. Finally, the driver activates/continues the activity LED.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int eoc = 0;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ // printk( "TLAN: Handling Tx EOF\n" );
+ host_int = 0;
+ head_list = priv->txList + priv->txHead;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
+ }
+ // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ dev->tbusy = 0;
+ priv->txHead++;
+ if ( priv->txHead >= TLAN_NUM_TX_LISTS )
+ priv->txHead = 0;
+ if ( eoc ) {
+ // printk( "TLAN: Handling Tx EOC\n" );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("TxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ priv->timerSetAt = jiffies;
+ // printk("TxEOF continuing timer...\n");
+ }
+ }
+
+ return ack;
+
+ } /* TLan_HandleTxEOF */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleStatOverflow
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Statistics Overflow interrupt which means
+ * that one or more of the TLAN statistics registers has reached 1/2
+ * capacity and needs to be read.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+ return 1;
+
+ } /* TLan_HandleStatOverflow */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleRxEOF
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Rx EOF interrupt which indicates a frame
+ * has been received by the adapter from the net and the frame has been
+ * transferred to memory. The function determines the bounce buffer
+ * the frame has been loaded into, creates a new sk_buff big enough to
+ * hold the frame, and sends it to protocol stack. It then resets the
+ * used buffer and appends it to the end of the list. If the frame was
+ * the last in the Rx channel (EOC), the function restarts the receive
+ * channel by sending an Rx Go command to the adapter. Then it activates/
+ * continues the the activity LED.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
+ {
+ u32 ack = 1;
+ int eoc = 0;
+ u8 *head_buffer;
+ TLanList *head_list;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ struct sk_buff *skb;
+ TLanList *tail_list;
+ void *t;
+
+ // printk( "TLAN: Handling Rx EOF Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ host_int = 0;
+ head_list = priv->rxList + priv->rxHead;
+ tail_list = priv->rxList + priv->rxTail;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
+ } else {
+ skb = dev_alloc_skb( head_list->frameSize + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ } else {
+ head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, head_list->frameSize );
+ // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
+ memcpy( t, head_buffer, head_list->frameSize );
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ }
+ }
+ head_list->forward = 0;
+ head_list->frameSize = TLAN_MAX_FRAME_SIZE;
+ head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ tail_list->forward = virt_to_bus( head_list );
+ priv->rxHead++;
+ if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
+ priv->rxHead = 0;
+ priv->rxTail++;
+ if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
+ priv->rxTail = 0;
+ if ( eoc ) {
+ // printk( "TLAN: Handling Rx EOC Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("RxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ // printk("RxEOF tarting continuing timer...\n");
+ priv->timerSetAt = jiffies;
+ }
+ }
+ dev->last_rx = jiffies;
+
+ return ack;
+
+ } /* TLan_HandleRxEOF */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleDummy
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Dummy interrupt, which is raised whenever
+ * a test interrupt is generated by setting the Req_Int bit of HOST_CMD
+ * to 1.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleDummy( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Dummy interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleDummy */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleTxEOC
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This driver is structured to determine EOC occurances by reading the
+ * CSTAT member of the list structure. Tx EOC interrupts are disabled
+ * via the DIO INTDIS register.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Tx EOC interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleTxEOC */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleStatusCheck
+ *
+ * Returns: 0 if Adapter check, 1 if Network Status check.
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles Adapter Check/Network Status interrupts
+ * generated by the adapter. It checks the vector in the HOST_INT
+ * register to determine if it is an Adapter Check interrupt. If so,
+ * it resets the adapter. Otherwise it clears the status registers
+ * and services the PHY.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
+ {
+ u32 ack;
+ u32 error;
+ u8 net_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ ack = 1;
+ if ( host_int & TLAN_HI_IV_MASK ) {
+ error = inl( dev->base_addr + TLAN_CH_PARM );
+ printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error );
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ TLan_ResetLists( dev );
+ TLan_Reset( dev );
+ dev->tbusy = 0;
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ if ( priv->timerType == 0 ) {
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ //printk( " RX GO---->\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+ }
+ ack = 0;
+ } else {
+ net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
+ if ( net_sts )
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
+ if ( net_sts & TLAN_NET_STS_MIRQ )
+ (*priv->phyService)( dev );
+
+ TLAN_DBG( 1, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
+ }
+
+ return ack;
+
+ } /* TLan_HandleStatusCheck */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleRxEOC
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This driver is structured to determine EOC occurances by reading the
+ * CSTAT member of the list structure. Rx EOC interrupts are disabled
+ * via the DIO INTDIS register.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Rx EOC interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleRxEOC */
+
+
+
+ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
+ TLan_HandleInvalid,
+ TLan_HandleTxEOF,
+ TLan_HandleStatOverflow,
+ TLan_HandleRxEOF,
+ TLan_HandleDummy,
+ TLan_HandleTxEOC,
+ TLan_HandleStatusCheck,
+ TLan_HandleRxEOC
+ };
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Timer Function
+
+******************************************************************************
+*****************************************************************************/
+
+ static void TLan_Timer( unsigned long );
+
+
+
+
+ /*************************************************************************
+ * TLan_Timer
+ *
+ * Returns: Nothing
+ * Parms: data A value given to add timer when add_timer was
+ * called.
+ *
+ * This function handles timed functionality for the TLAN driver. The
+ * two current timer uses are for delaying for autonegotionation and
+ * driving the ACT LED.
+ * - Autonegotiation requires being allowed about 2 1/2 seconds before
+ * attempting to transmit a packet. It would be a very bad thing
+ * to hang the kernel this long, so the driver doesn't allow
+ * transmission 'til after this time, for certain PHYs. It would
+ * be much nicer if all PHYs were interrupt-capable like the
+ * internal PHY.
+ * - The ACT LED, which shows adapter activity, is driven by the driver,
+ * and so must be left on for a short period to power up the LED so
+ * it can be seen. This delay can be changed by changing the
+ * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a
+ * slightly sluggish response.
+ *
+ ************************************************************************/
+
+ void TLan_Timer( unsigned long data )
+ {
+ struct device *dev = (struct device *) data;
+ u16 gen_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType );
+
+ switch ( priv->timerType ) {
+ case TLAN_TIMER_LINK:
+ TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
+ add_timer( &priv->timer );
+ }
+ break;
+ case TLAN_TIMER_ACT:
+ if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+ add_timer( &priv->timer );
+ }
+ break;
+ default:
+ break;
+ }
+
+ } /* TLan_Timer */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Primary Functions
+
+ These functions are more or less common to all Linux network drivers.
+
+******************************************************************************
+*****************************************************************************/
+
+ static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
+ static int TLan_Init( struct device * );
+ static int TLan_Open(struct device *dev);
+ static int TLan_StartTx(struct sk_buff *, struct device *);
+ static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
+ static int TLan_Close(struct device *);
+ static struct enet_statistics *TLan_GetStats( struct device * );
+ static void TLan_SetMulticastList( struct device * );
+
+
+#ifdef MODULE
+
+ /*************************************************************************
+ * init_module
+ *
+ * Returns: 0 if module installed ok, non-zero if not.
+ * Parms: None
+ *
+ * This function begins the setup of the driver creating a pad buffer,
+ * finding all TLAN devices (matching TLanDeviceList entries), and
+ * creating and initializing a device structure for each adapter.
+ *
+ ************************************************************************/
+
+ extern int init_module(void)
+ {
+ int failed, found;
+ size_t dev_size;
+ struct device *dev;
+ TLanPrivateInfo *priv;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, dl_ix;
+
+ printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
+ TLanVersionMajor,
+ TLanVersionMinor
+ );
+
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ return -ENOMEM;
+ }
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+
+ dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
+ while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
+ dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
+ if ( dev == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ continue;
+ }
+ memset( dev, 0, dev_size );
+
+ dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+ dev->base_addr = io_base;
+ dev->irq = irq;
+ dev->init = TLan_Init;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ ether_setup( dev );
+
+ failed = register_netdev( dev );
+
+ if ( failed ) {
+ printk( "TLAN: Could not register network device. Freeing struct.\n" );
+ kfree( dev );
+ } else {
+ priv->nextDevice = TLanDevices;
+ TLanDevices = dev;
+ TLanDevicesInstalled++;
+ printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
+ }
+ }
+
+ // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled );
+
+ return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+
+ } /* init_module */
+
+
+
+
+ /*************************************************************************
+ * cleanup_module
+ *
+ * Returns: Nothing
+ * Parms: None
+ *
+ * Goes through the TLanDevices list and frees the device structs and
+ * memory associated with each device (lists and buffers). It also
+ * ureserves the IO port regions associated with this device.
+ *
+ ************************************************************************/
+
+ extern void cleanup_module(void)
+ {
+ struct device *dev;
+ TLanPrivateInfo *priv;
+
+ while (TLanDevicesInstalled) {
+ dev = TLanDevices;
+ priv = (TLanPrivateInfo *) dev->priv;
+ if ( priv->dmaStorage )
+ kfree( priv->dmaStorage );
+ release_region( dev->base_addr, 0x10 );
+ unregister_netdev( dev );
+ TLanDevices = priv->nextDevice;
+ kfree( dev );
+ TLanDevicesInstalled--;
+ }
+ kfree( TLanPadBuffer );
+
+ } /* cleanup_module */
+
+
+#else /* MODULE */
+
+
+
+
+ /*************************************************************************
+ * tlan_probe
+ *
+ * Returns: 0 on success, error code on error
+ * Parms: dev device struct to use if adapter is found.
+ *
+ * The name is lower case to fit in with all the rest of the
+ * netcard_probe names. This function looks for a/another TLan based
+ * adapter, setting it up with the provided device struct if one is
+ * found.
+ *
+ ************************************************************************/
+
+ extern int tlan_probe( struct device *dev )
+ {
+ static int pad_allocated = 0;
+ int found;
+ TLanPrivateInfo *priv;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, dl_ix;
+
+ found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
+ TLAN_DBG( 1, "TLAN: Probing...\n" );
+ if ( found ) {
+ dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+ if ( dev->priv == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ }
+ memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+
+ dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+
+ dev->base_addr = io_base;
+ dev->irq = irq;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ if ( ! pad_allocated ) {
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ } else {
+ pad_allocated = 1;
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+ }
+ }
+ printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor,
+ TLanVersionMinor,
+ dev->name,
+ (int) irq,
+ io_base,
+ TLanDeviceList[dl_ix].deviceName );
+ TLan_Init( dev );
+ }
+
+ return ( ( found ) ? 0 : -ENODEV );
+
+ } /* tlan_probe */
+
+
+#endif /* MODULE */
+
+
+
+
+ /*************************************************************************
+ * TLan_PciProbe
+ *
+ * Returns: 1 if another TLAN card was found, 0 if not.
+ * Parms: pci_bus The PCI bus the card was found on.
+ * pci_dfn The PCI whatever the card was found at.
+ * pci_irq The IRQ of the found adapter.
+ * pci_rev The revision of the adapter.
+ * pci_io_base The first IO port used by the adapter.
+ * dl_ix The index in the device list of the adapter.
+ *
+ * This function searches for an adapter with PCI vendor and device
+ * IDs matching those in the TLanDeviceList. The function 'remembers'
+ * the last device it found, and so finds a new device (if anymore are
+ * to be found) each time the function is called. It then looks up
+ * pertinent PCI info and returns it to the caller.
+ *
+ ************************************************************************/
+
+ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix )
+ {
+ static int dl_index = 0;
+ static int pci_index = 0;
+
+ int not_found;
+ u8 pci_latency;
+ u16 pci_command;
+
+
+ if ( ! pcibios_present() ) {
+ printk( "TLAN: PCI Bios not present.\n" );
+ return 0;
+ }
+
+ for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
+ not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId,
+ pci_index,
+ pci_bus,
+ pci_dfn
+ );
+ if ( ! not_found ) {
+ TLAN_DBG( 1, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId
+ );
+
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
+ pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command);
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 0x10) {
+ pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
+ TLAN_DBG( 1, "TLAN: Setting latency timer to max.\n");
+ }
+
+ if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
+ *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
+ TLAN_DBG( 1, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
+ } else {
+ *pci_io_base = 0;
+ printk("TLAN: IO mapping not available, ignoring device.\n");
+ }
+
+ if (pci_command & PCI_COMMAND_MASTER) {
+ TLAN_DBG( 1, "TLAN: Bus mastering is active.\n");
+ }
+
+ pci_index++;
+
+ if ( *pci_io_base ) {
+ *dl_ix = dl_index;
+ return 1;
+ }
+
+ } else {
+ pci_index = 0;
+ }
+ }
+
+ return 0;
+
+ } /* TLan_PciProbe */
+
+
+
+
+ /*************************************************************************
+ * TLan_Init
+ *
+ * Returns: 0 on success, error code otherwise.
+ * Parms: dev The structure of the device to be init'ed.
+ *
+ * This function completes the initialization of the device structure
+ * and driver. It reserves the IO addresses, allocates memory for the
+ * lists and bounce buffers, retrieves the MAC address from the eeprom
+ * and assignes the device's methods.
+ *
+ ************************************************************************/
+
+ int TLan_Init( struct device *dev )
+ {
+ int dma_size;
+ int err;
+ int i;
+ TLanPrivateInfo *priv;
+
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ err = check_region( dev->base_addr, 0x10 );
+ if ( err ) {
+ printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 );
+ return -EIO;
+ }
+ request_region( dev->base_addr, 0x10, TLanSignature );
+
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+ priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
+ if ( priv->dmaStorage == NULL ) {
+ printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name );
+ return -ENOMEM;
+ }
+ memset( priv->dmaStorage, 0, dma_size );
+ priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+ priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+
+ err = 0;
+ for ( i = 0; i < 6 ; i++ )
+ err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] );
+ if ( err )
+ printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err );
+ dev->addr_len = 6;
+
+ dev->open = &TLan_Open;
+ dev->hard_start_xmit = &TLan_StartTx;
+ dev->stop = &TLan_Close;
+ dev->get_stats = &TLan_GetStats;
+ dev->set_multicast_list = &TLan_SetMulticastList;
+
+ return 0;
+
+ } /* TLan_Init */
+
+
+
+
+ /*************************************************************************
+ * TLan_Open
+ *
+ * Returns: 0 on success, error code otherwise.
+ * Parms: dev Structure of device to be opened.
+ *
+ * This routine puts the driver and TLAN adapter in a state where it is
+ * ready to send and receive packets. It allocates the IRQ, resets and
+ * brings the adapter out of reset, and allows interrupts. It also
+ * delays the startup for autonegotiation or sends a Rx GO command to
+ * the adapter, as appropriate.
+ *
+ ************************************************************************/
+
+ int TLan_Open( struct device *dev )
+ {
+ int err;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+ if ( err ) {
+ printk( "TLAN: %s: IRQ %d already in use.\n", dev->name, dev->irq );
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* NOTE: It might not be necessary to read the stats before a
+ reset if you don't care what the values are.
+ */
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_Reset( dev );
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ if ( debug >= 1 )
+ outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+
+ init_timer( &priv->timer );
+ priv->timer.data = (unsigned long) dev;
+ priv->timer.function = &TLan_Timer;
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ TLAN_DBG( 1, "TLAN: RX GO\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ TLAN_DBG( 1, "TLAN: Device %s opened.\n", dev->name );
+
+ return 0;
+
+ } /* TLan_Open */
+
+
+
+
+ /*************************************************************************
+ * TLan_StartTx
+ *
+ * Returns: 0 on success, non-zero on failure.
+ * Parms: skb A pointer to the sk_buff containing the frame to
+ * be sent.
+ * dev The device to send the data on.
+ *
+ * This function adds a frame to the Tx list to be sent ASAP. First it
+ * verifies that the adapter is ready and there is room in the queue.
+ * Then it sets up the next available list, copies the frame to the
+ * corresponding buffer. If the adapter Tx channel is idle, it gives
+ * adapter a Tx Go command on the list, otherwise it sets the forward
+ * address of the previous list to point to this one. Then it frees
+ * the sk_buff.
+ *
+ ************************************************************************/
+
+ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *tail_list;
+ u8 *tail_buffer;
+ int pad;
+
+ // printk( "Entering StartTx\n" );
+ if ( ! priv->phyOnline ) {
+ dev_kfree_skb( skb, FREE_WRITE );
+ return 0;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+ if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+ // printk( "TLAN: %s TX is busy, Head=%d Tail=%d\n", dev->name, priv->txHead, priv->txTail );
+ dev->tbusy = 1;
+ // printk( "TLAN: Tx is busy.\n");
+ priv->txBusyCount++;
+ return 1;
+ }
+ tail_list->forward = 0;
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
+ pad = TLAN_MIN_FRAME_SIZE - skb->len;
+ if ( pad > 0 ) {
+ tail_list->frameSize = (u16) skb->len + pad;
+ tail_list->buffer[0].count = (u32) skb->len;
+ tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
+ tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
+ } else {
+ tail_list->frameSize = (u16) skb->len;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+ }
+ // are we transferring?
+ cli();
+ tail_list->cStat = TLAN_CSTAT_READY;
+ if ( ! priv->txInProgress ) {
+ priv->txInProgress = 1;
+ outw( 0x4, dev->base_addr + TLAN_HOST_INT );
+ // printk("TLAN: Sending GO for 0%d\n", priv->txTail );
+ outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
+ } else {
+ // printk("TLAN: Adding 0%d\n", priv->txTail );
+ // Assign previous list to point to this one. If previous has
+ // already been read, the EOC check in the TX EOF interrupt handler
+ // will start another transfer.
+ if ( priv->txTail == 0 )
+ ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
+ else
+ ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+ }
+ sti();
+ priv->txTail++;
+ if ( priv->txTail >= TLAN_NUM_TX_LISTS )
+ priv->txTail = 0;
+
+ dev_kfree_skb( skb, FREE_WRITE );
+
+ dev->trans_start = jiffies;
+ // printk( "Leaving StartTx\n" );
+ return 0;
+
+ } /* TLan_StartTx */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleInterrupt
+ *
+ * Returns: Nothing
+ * Parms: irq The line on which the interrupt occurred.
+ * dev_id A pointer to the device assigned to this irq line.
+ * regs ???
+ *
+ * This function handles an interrupt generated by its assigned TLAN
+ * adapter. The function deactivates interrupts on its adapter, records
+ * the type of interrupt, executes the appropriate subhandler, and
+ * acknowdges the interrupt to the adapter (thus re-enabling adapter
+ * interrupts.
+ *
+ ************************************************************************/
+
+ void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
+ {
+ u32 ack;
+ struct device *dev;
+ u32 host_cmd;
+ u16 host_int;
+ int type;
+
+ dev = (struct device *) dev_id;
+
+ if ( dev->interrupt )
+ TLAN_DBG( 1, "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+ dev->interrupt++;
+
+ cli();
+
+ host_int = inw( dev->base_addr + TLAN_HOST_INT );
+ outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
+
+ type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
+
+ ack = TLanIntVector[type]( dev, host_int );
+
+ sti();
+
+ if ( ack ) {
+ host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
+ outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ dev->interrupt--;
+
+ } /* TLan_HandleInterrupts */
+
+
+
+
+ /*************************************************************************
+ * TLan_Close
+ *
+ * Returns: An error code.
+ * Parms: dev The device structure of the device to close.
+ *
+ * This function shuts down the adapter. It records any stats, puts
+ * the adapter into reset state, deactivates its time as needed, and
+ * frees the irq it is using.
+ *
+ ************************************************************************/
+
+ int TLan_Close(struct device *dev)
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ if ( priv->timerSetAt != 0 )
+ del_timer( &priv->timer );
+ free_irq( dev->irq, dev );
+ TLAN_DBG( 1, "TLAN: Device %s closed.\n", dev->name );
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+ } /* TLan_Close */
+
+
+
+
+ /*************************************************************************
+ * TLan_GetStats
+ *
+ * Returns: A pointer to the device's statistics structure.
+ * Parms: dev The device structure to return the stats for.
+ *
+ * This function updates the devices statistics by reading the TLAN
+ * chip's onboard registers. Then it returns the address of the
+ * statistics structure.
+ *
+ ************************************************************************/
+
+ struct enet_statistics *TLan_GetStats( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+
+ /* Should only read stats if open ? */
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ printk( "TLAN: %s Rx EOC Count: %d\n", dev->name, priv->rxEocCount );
+ printk( "TLAN: %s Tx Busy Count: %d\n", dev->name, priv->txBusyCount );
+ // printk( "TLAN: Got stats for %s.\n", dev->name );
+ TLan_PrintDio( dev->base_addr );
+ TLan_PhyPrint( dev );
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+ TLan_PrintList( priv->rxList + i, "RX", i );
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+ TLan_PrintList( priv->txList + i, "TX", i );
+
+ return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+
+ } /* TLan_GetStats */
+
+
+
+
+ /*************************************************************************
+ * TLan_SetMulticastList
+ *
+ * Returns: Nothing
+ * Parms: dev The device structure to set the multicast list for.
+ *
+ * This function sets the TLAN adaptor to various receive modes. If the
+ * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise,
+ * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then
+ * the hash table is set to receive all group addresses. Otherwise, the
+ * first three multicast addresses are stored in AREG_1-3, and the rest
+ * are selected via the hash table, as necessary.
+ *
+ ************************************************************************/
+
+ void TLan_SetMulticastList( struct device *dev )
+ {
+ struct dev_mc_list *dmi = dev->mc_list;
+ u32 hash1 = 0;
+ u32 hash2 = 0;
+ int i;
+ u32 offset;
+ u8 tmp;
+
+ if ( dev->flags & IFF_PROMISC ) {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
+ } else {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
+ if ( dev->flags & IFF_ALLMULTI ) {
+ for ( i = 0; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
+ } else {
+ for ( i = 0; i < dev->mc_count; i++ ) {
+ if ( i < 3 ) {
+ TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
+ } else {
+ offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
+ if ( offset < 32 )
+ hash1 |= ( 1 << offset );
+ else
+ hash2 |= ( 1 << ( offset - 32 ) );
+ }
+ dmi = dmi->next;
+ }
+ for ( ; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
+ }
+ }
+
+ } /* TLan_SetRxMode */
diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h
new file mode 100644
index 000000000..9919fd275
--- /dev/null
+++ b/drivers/net/tlan.h
@@ -0,0 +1,485 @@
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.h
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4, colums>=132
+ *
+ ********************************************************************/
+
+#include <asm/io.h>
+#include <asm/types.h>
+#include <linux/netdevice.h>
+
+
+
+ /*****************************************************************
+ * TLan Definitions
+ *
+ ****************************************************************/
+
+ #define FALSE 0
+ #define TRUE 1
+
+ #define TLAN_MIN_FRAME_SIZE 64
+ #define TLAN_MAX_FRAME_SIZE 1600
+
+ #define TLAN_NUM_RX_LISTS 4
+ #define TLAN_NUM_TX_LISTS 8
+
+ #define TLAN_IGNORE 0
+ #define TLAN_RECORD 1
+
+ #define TLAN_DBG(lvl, format, args...) if ( debug >= lvl ) printk( format, ##args );
+
+
+
+
+ /*****************************************************************
+ * Device Identification Definitions
+ *
+ ****************************************************************/
+
+ /* NOTE: These should be moved to pci.h someday */
+ #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
+ #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
+ #define PCI_DEVICE_ID_NETFLEX_3_INTEGRATED 0xAE35
+
+
+ typedef struct tlan_pci_id {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceName;
+ } TLanPciId;
+
+
+
+
+ /*****************************************************************
+ * Rx/Tx List Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_BUFFERS_PER_LIST 10
+ #define TLAN_LAST_BUFFER 0x80000000
+ #define TLAN_CSTAT_UNUSED 0x8000
+ #define TLAN_CSTAT_FRM_CMP 0x4000
+ #define TLAN_CSTAT_READY 0x3000
+ #define TLAN_CSTAT_EOC 0x0800
+ #define TLAN_CSTAT_RX_ERROR 0x0400
+ #define TLAN_CSTAT_PASS_CRC 0x0200
+ #define TLAN_CSTAT_DP_PR 0x0100
+
+
+ typedef struct tlan_buffer_ref_tag {
+ u32 count;
+ u32 address;
+ } TLanBufferRef;
+
+
+ typedef struct tlan_list_tag {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST];
+ } TLanList;
+
+
+ typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+
+
+
+ /*****************************************************************
+ * PHY definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_PHY_MAX_ADDR 0x1F
+ #define TLAN_PHY_INTERNAL 0x1F
+
+ #define TLAN_PHY_ACTIVITY 0x00000001
+ #define TLAN_PHY_AUTONEG 0x00000002
+
+
+ typedef int (TLanPhyFunc)( struct device * );
+
+
+ typedef struct tlan_phy_id_entry_tag {
+ u16 idHi;
+ u16 idLo;
+ TLanPhyFunc *check;
+ TLanPhyFunc *service;
+ u32 flags;
+ } TLanPhyIdEntry;
+
+
+
+
+ /*****************************************************************
+ * TLAN Private Information Structure
+ *
+ ****************************************************************/
+
+ typedef struct tlan_private_tag {
+ struct device *nextDevice;
+ void *dmaStorage;
+ u8 *padBuffer;
+ TLanList *rxList;
+ u8 *rxBuffer;
+ u32 rxHead;
+ u32 rxTail;
+ u32 rxEocCount;
+ TLanList *txList;
+ u8 *txBuffer;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ u32 txBusyCount;
+ u32 phyAddr;
+ u32 phyEntry;
+ u32 phyOnline;
+ u32 phyFlags;
+ TLanPhyFunc *phyCheck;
+ TLanPhyFunc *phyService;
+ u32 timerSetAt;
+ u32 timerType;
+ struct timer_list timer;
+ struct enet_statistics stats;
+ u32 pciEntry;
+ u8 pciRevision;
+ u8 pciBus;
+ u8 pciDeviceFn;
+ char devName[8];
+ } TLanPrivateInfo;
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Timer Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_TIMER_LINK 1
+ #define TLAN_TIMER_ACT 2
+
+ #define TLAN_TIMER_LINK_DELAY 230
+ #define TLAN_TIMER_ACT_DELAY 10
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Eeprom Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_EEPROM_ACK 0
+ #define TLAN_EEPROM_STOP 1
+
+
+
+
+ /*****************************************************************
+ * Host Register Offsets and Contents
+ *
+ ****************************************************************/
+
+ #define TLAN_HOST_CMD 0x00
+ #define TLAN_HC_GO 0x80000000
+ #define TLAN_HC_STOP 0x40000000
+ #define TLAN_HC_ACK 0x20000000
+ #define TLAN_HC_CS_MASK 0x1FE00000
+ #define TLAN_HC_EOC 0x00100000
+ #define TLAN_HC_RT 0x00080000
+ #define TLAN_HC_NES 0x00040000
+ #define TLAN_HC_AD_RST 0x00008000
+ #define TLAN_HC_LD_TMR 0x00004000
+ #define TLAN_HC_LD_THR 0x00002000
+ #define TLAN_HC_REQ_INT 0x00001000
+ #define TLAN_HC_INT_OFF 0x00000800
+ #define TLAN_HC_INT_ON 0x00000400
+ #define TLAN_HC_AC_MASK 0x000000FF
+ #define TLAN_CH_PARM 0x04
+ #define TLAN_DIO_ADR 0x08
+ #define TLAN_DA_ADR_INC 0x8000
+ #define TLAN_DA_RAM_ADR 0x4000
+ #define TLAN_HOST_INT 0x0A
+ #define TLAN_HI_IV_MASK 0x1FE0
+ #define TLAN_HI_IT_MASK 0x001C
+ #define TLAN_DIO_DATA 0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD 0x00
+#define TLAN_NET_CMD_NRESET 0x80
+#define TLAN_NET_CMD_NWRAP 0x40
+#define TLAN_NET_CMD_CSF 0x20
+#define TLAN_NET_CMD_CAF 0x10
+#define TLAN_NET_CMD_NOBRX 0x08
+#define TLAN_NET_CMD_DUPLEX 0x04
+#define TLAN_NET_CMD_TRFRAM 0x02
+#define TLAN_NET_CMD_TXPACE 0x01
+#define TLAN_NET_SIO 0x01
+#define TLAN_NET_SIO_MINTEN 0x80
+#define TLAN_NET_SIO_ECLOK 0x40
+#define TLAN_NET_SIO_ETXEN 0x20
+#define TLAN_NET_SIO_EDATA 0x10
+#define TLAN_NET_SIO_NMRST 0x08
+#define TLAN_NET_SIO_MCLK 0x04
+#define TLAN_NET_SIO_MTXEN 0x02
+#define TLAN_NET_SIO_MDATA 0x01
+#define TLAN_NET_STS 0x02
+#define TLAN_NET_STS_MIRQ 0x80
+#define TLAN_NET_STS_HBEAT 0x40
+#define TLAN_NET_STS_TXSTOP 0x20
+#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RSRVD 0x0F
+#define TLAN_NET_MASK 0x03
+#define TLAN_NET_MASK_MASK7 0x80
+#define TLAN_NET_MASK_MASK6 0x40
+#define TLAN_NET_MASK_MASK5 0x20
+#define TLAN_NET_MASK_MASK4 0x10
+#define TLAN_NET_MASK_RSRVD 0x0F
+#define TLAN_NET_CONFIG 0x04
+#define TLAN_NET_CFG_RCLK 0x8000
+#define TLAN_NET_CFG_TCLK 0x4000
+#define TLAN_NET_CFG_BIT 0x2000
+#define TLAN_NET_CFG_RXCRC 0x1000
+#define TLAN_NET_CFG_PEF 0x0800
+#define TLAN_NET_CFG_1FRAG 0x0400
+#define TLAN_NET_CFG_1CHAN 0x0200
+#define TLAN_NET_CFG_MTEST 0x0100
+#define TLAN_NET_CFG_PHY_EN 0x0080
+#define TLAN_NET_CFG_MSMASK 0x007F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_AREG_0 0x10
+#define TLAN_AREG_1 0x16
+#define TLAN_AREG_2 0x1C
+#define TLAN_AREG_3 0x22
+#define TLAN_HASH_1 0x28
+#define TLAN_HASH_2 0x2C
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
+#define TLAN_MAX_RX 0x46
+#define TLAN_INT_DIS 0x48
+#define TLAN_ID_TX_EOC 0x04
+#define TLAN_ID_RX_EOF 0x02
+#define TLAN_ID_RX_EOC 0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS 8
+
+#define TLAN_INT_NONE 0x0000
+#define TLAN_INT_TX_EOF 0x0001
+#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_RX_EOF 0x0003
+#define TLAN_INT_DUMMY 0x0004
+#define TLAN_INT_TX_EOC 0x0005
+#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_RX_EOC 0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL 0x00
+#define MII_GC_RESET 0x8000
+#define MII_GC_LOOPBK 0x4000
+#define MII_GC_SPEEDSEL 0x2000
+#define MII_GC_AUTOENB 0x1000
+#define MII_GC_PDOWN 0x0800
+#define MII_GC_ISOLATE 0x0400
+#define MII_GC_AUTORSRT 0x0200
+#define MII_GC_DUPLEX 0x0100
+#define MII_GC_COLTEST 0x0080
+#define MII_GC_RESERVED 0x007F
+#define MII_GEN_STS 0x01
+#define MII_GS_100BT4 0x8000
+#define MII_GS_100BTXFD 0x4000
+#define MII_GS_100BTXHD 0x2000
+#define MII_GS_10BTFD 0x1000
+#define MII_GS_10BTHD 0x0800
+#define MII_GS_RESERVED 0x07C0
+#define MII_GS_AUTOCMPLT 0x0020
+#define MII_GS_RFLT 0x0010
+#define MII_GS_AUTONEG 0x0008
+#define MII_GS_LINK 0x0004
+#define MII_GS_JABBER 0x0002
+#define MII_GS_EXTCAP 0x0001
+#define MII_GEN_ID_HI 0x02
+#define MII_GEN_ID_LO 0x03
+#define MII_GIL_OUI 0xFC00
+#define MII_GIL_MODEL 0x03F0
+#define MII_GIL_REVISION 0x000F
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID 0x10
+#define TLAN_TLPHY_CTL 0x11
+#define TLAN_TC_IGLINK 0x8000
+#define TLAN_TC_SWAPOL 0x4000
+#define TLAN_TC_AUISEL 0x2000
+#define TLAN_TC_SQEEN 0x1000
+#define TLAN_TC_MTEST 0x0800
+#define TLAN_TC_RESERVED 0x07F8
+#define TLAN_TC_NFEW 0x0004
+#define TLAN_TC_INTEN 0x0002
+#define TLAN_TC_TINT 0x0001
+#define TLAN_TLPHY_STS 0x12
+#define TLAN_TS_MINT 0x8000
+#define TLAN_TS_PHOK 0x4000
+#define TLAN_TS_POLOK 0x2000
+#define TLAN_TS_TPENERGY 0x1000
+#define TLAN_TS_RESERVED 0x0FFF
+
+
+
+/* Routines to access internal registers. */
+
+inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+
+} /* TLan_DioRead8 */
+
+
+
+
+inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_ClearBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+inline int TLan_GetBit(u8 bit, u16 port)
+{
+ return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+inline void TLan_SetBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) | bit, port);
+}
+
+
+inline u32 xor( u32 a, u32 b )
+{
+ return ( ( a && ! b ) || ( ! a && b ) );
+}
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+inline u32 TLan_HashFunc( u8 *a )
+{
+ u32 hash;
+
+ hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) );
+ hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1;
+ hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2;
+ hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3;
+ hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4;
+ hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5;
+
+ return hash;
+
+}
+
+
+
+
+#endif
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c
index f87beba00..03b7fc10c 100644
--- a/drivers/net/tulip.c
+++ b/drivers/net/tulip.c
@@ -1078,6 +1078,7 @@ tulip_rx(struct device *dev)
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
+ lp->stats.rx_bytes+=skb->len;
}
lp->rx_ring[entry].status = TRING_OWN;