diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
commit | e308faf24f68e262d92d294a01ddca7a17e76762 (patch) | |
tree | 22c47cb315811834861f013067878ff664e95abd /drivers/net | |
parent | 30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff) |
Sync with Linux 2.1.46.
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/8390.c | 1 | ||||
-rw-r--r-- | drivers/net/Config.in | 22 | ||||
-rw-r--r-- | drivers/net/Makefile | 18 | ||||
-rw-r--r-- | drivers/net/README.wanpipe | 12 | ||||
-rw-r--r-- | drivers/net/Space.c | 15 | ||||
-rw-r--r-- | drivers/net/cops.c | 1018 | ||||
-rw-r--r-- | drivers/net/cops.h | 60 | ||||
-rw-r--r-- | drivers/net/cops_ffdrv.h | 533 | ||||
-rw-r--r-- | drivers/net/cops_ltdrv.h | 242 | ||||
-rw-r--r-- | drivers/net/ibmtr.c | 11 | ||||
-rw-r--r-- | drivers/net/lapbether.c | 2 | ||||
-rw-r--r-- | drivers/net/sdla_fr.c | 339 | ||||
-rw-r--r-- | drivers/net/sdla_ppp.c | 142 | ||||
-rw-r--r-- | drivers/net/sdla_x25.c | 174 | ||||
-rw-r--r-- | drivers/net/sdlamain.c | 45 | ||||
-rw-r--r-- | drivers/net/shaper.c | 22 | ||||
-rw-r--r-- | drivers/net/shaper.h | 61 | ||||
-rw-r--r-- | drivers/net/strip.c | 1940 | ||||
-rw-r--r-- | drivers/net/sunhme.c | 17 | ||||
-rw-r--r-- | drivers/net/sunqe.c | 14 | ||||
-rw-r--r-- | drivers/net/tlan.c | 2309 | ||||
-rw-r--r-- | drivers/net/tlan.h | 485 | ||||
-rw-r--r-- | drivers/net/tulip.c | 1 |
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; |