diff options
Diffstat (limited to 'drivers/net/arcnet.c')
-rw-r--r-- | drivers/net/arcnet.c | 2175 |
1 files changed, 2175 insertions, 0 deletions
diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c new file mode 100644 index 000000000..1f0fad4bd --- /dev/null +++ b/drivers/net/arcnet.c @@ -0,0 +1,2175 @@ +/* arcnet.c + Written 1994-95 by Avery Pennarun, derived from skeleton.c by + Donald Becker. + + Contact Avery at: apenwarr@tourism.807-city.on.ca or + RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9 + + ********************** + + skeleton.c Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may only be used + and distributed according to the terms of the GNU Public License as + modified by SRC, incorporated herein by reference. + + ********************** + + v1.01 (95/03/24) + - Fixed some IPX-related bugs. (Thanks to Tomasz Motylewski + <motyl@tichy.ch.uj.edu.pl> for the patches to make arcnet work + with dosemu!) + v1.0 (95/02/15) + - Initial non-alpha release. + + + TO DO: + + - Test in systems with NON-ARCnet network cards, just to see if + autoprobe kills anything. With any luck, it won't. (It's pretty + careful.) + - Except some unfriendly NE2000's die. (as of 0.40-ALPHA) + - cards with shared memory that can be "turned off?" + - NFS mount freezes after several megabytes to SOSS for DOS. + unmount/remount works. Is this arcnet-specific? I don't know. + - Add support for the various stupid bugs ("I didn't read the RFC" + syndrome) in Windows for Workgroups and LanMan. + */ + +/**************************************************************************/ + +/* define this if you want to use the new but possibly dangerous ioprobe + * If you get lockups right after status5, you probably need + * to undefine this. It should make more cards probe correctly, + * I hope. + */ +#define DANGER_PROBE + +/* define this if you want to use the "extra delays" which were removed + * in 0.41 since they seemed needless. + */ +#undef EXTRA_DELAYS + +/* undefine this if you want to use the non-IRQ-driven transmitter. (possibly + * safer, although it takes more CPU time and IRQ_XMIT seems fine right now) + */ +#define IRQ_XMIT + +/* define this for "careful" transmitting. Try with and without if you have + * problems. If you use IRQ_XMIT, do NOT define this. + */ +#undef CAREFUL_XMIT + +/* define this for an extra-careful memory detect. This should work all + * the time now, but you never know. + */ +#define STRICT_MEM_DETECT + +/* define this to use the "old-style" limited MTU by default. It basically + * disables packet splitting. ifconfig can still be used to reset the MTU. + * + * leave this disabled if possible, so it will use ethernet defaults, + * which is our goal. + */ +#undef LIMIT_MTU + +/* define this if you have a problem with the card getting "stuck" now and + * then, which can only be fixed by a reboot or resetting the card manually + * via ifconfig up/down. ARCnet will set a timer function which is called + * 8 times every second. + * + * This should no longer be necessary. if you experience "stuck" ARCnet + * drivers, please email apenwarr@tourism.807-city.on.ca or I will remove + * this feature in a future release. + */ +#undef USE_TIMER_HANDLER + +/**************************************************************************/ + +static char *version = + "arcnet.c:v1.01 95/03/24 Avery Pennarun <apenwarr@tourism.807-city.on.ca>\n"; + +/* + Sources: + Crynwr arcnet.com/arcether.com packet drivers. + arcnet.c v0.00 dated 1/1/94 and apparently by + Donald Becker - it didn't work :) + skeleton.c v0.05 dated 11/16/93 by Donald Becker + (from Linux Kernel 1.1.45) + ...I sure wish I had the ARCnet data sheets right about now! + RFC's 1201 and 1051 (mostly 1201) - re: ARCnet IP packets + net/inet/eth.c (from kernel 1.1.50) for header-building info... + Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su> + Textual information and more alternate source from Joachim Koenig + <jojo@repas.de> +*/ + +#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#endif /* MODULE */ + +#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 <linux/timer.h> +#include <linux/errno.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <net/arp.h> + + +/* debug levels: + * D_OFF production + * D_NORMAL verification + * D_INIT show init/detect messages + * D_DURING show messages during normal use (ie interrupts) + * D_DATA show packets data from skb's, not on Arcnet card + * D_TX show tx packets + * D_RX show tx+rx packets + */ +#define D_OFF 0 +#define D_NORMAL 1 +#define D_INIT 2 +#define D_EXTRA 3 +#define D_DURING 4 +#define D_DATA 6 +#define D_TX 8 +#define D_RX 9 + +#ifndef NET_DEBUG +#define NET_DEBUG D_INIT +#endif +static unsigned int net_debug = NET_DEBUG; + +#ifndef HAVE_AUTOIRQ +/* From auto_irq.c, in ioport.h for later versions. */ +extern void autoirq_setup(int waittime); +extern int autoirq_report(int waittime); +/* The map from IRQ number (as passed to the interrupt handler) to + 'struct device'. */ +extern struct device *irq2dev_map[16]; +#endif + +#ifndef HAVE_PORTRESERVE +#define check_region(ioaddr, size) 0 +#define request_region(ioaddr, size) do ; while (0) +#endif + +/* macro to simplify debug checking */ +#define BUGLVL(x) if (net_debug>=x) + +/* The number of low I/O ports used by the ethercard. */ +#define ETHERCARD_TOTAL_SIZE 16 + + +/* Handy defines for ARCnet specific stuff */ + /* COM 9026 (?) --> ARCnet register addresses */ +#define INTMASK (ioaddr+0) /* writable */ +#define STATUS (ioaddr+0) /* readable */ +#define COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */ +#define RESET (ioaddr+8) /* software reset writable */ + + /* time needed for various things (in clock ticks, 1/100 sec) */ +#define RESETtime 40 /* reset */ +#define XMITtime 10 /* send (?) */ +#define ACKtime 10 /* acknowledge (?) */ + + /* these are the max/min lengths of packet data. (including + * ClientData header) + * note: packet sizes 250, 251, 252 are impossible (God knows why) + * so exception packets become necessary. + * + * These numbers are compared with the length of the full packet, + * including ClientData header. + */ +#define MTU (253+EXTRA_CLIENTDATA) /* normal packet max size */ +#define MinTU (257+EXTRA_CLIENTDATA) /* extended packet min size */ +#define XMTU (508+EXTRA_CLIENTDATA) /* extended packet max size */ + + /* status/interrupt mask bit fields */ +#define TXFREEflag 0x001 /* transmitter available */ +#define TXACKflag 0x002 /* transmitted msg. ackd */ +#define RECONflag 0x004 /* system reconfigured */ +#define TESTflag 0x008 /* test flag */ +#define RESETflag 0x010 /* power-on-reset */ +#define RES1flag 0x020 /* unused */ +#define RES2flag 0x040 /* unused */ +#define NORXflag 0x080 /* receiver inhibited */ + + /* in the command register, the following bits have these meanings: + * 0-2 command + * 3-4 page number (for enable rcv/xmt command) + * 7 receive broadcasts + */ +#define NOTXcmd 0x001 /* disable transmitter */ +#define NORXcmd 0x002 /* disable receiver */ +#define TXcmd 0x003 /* enable transmitter */ +#define RXcmd 0x004 /* enable receiver */ +#define CONFIGcmd 0x005 /* define configuration */ +#define CFLAGScmd 0x006 /* clear flags */ +#define TESTcmd 0x007 /* load test flags */ + + /* flags for "clear flags" command */ +#define RESETclear 0x008 /* power-on-reset */ +#define CONFIGclear 0x010 /* system reconfigured */ + + /* flags for "load test flags" command */ +#define TESTload 0x008 /* test flag (diagnostic) */ + + /* byte deposited into first address of buffers on reset */ +#define TESTvalue 0321 /* that's octal for 0xD1 :) */ + + /* for "enable receiver" command */ +#define RXbcasts 0x080 /* receive broadcasts */ + + /* flags for "define configuration" command */ +#define NORMALconf 0x000 /* 1-249 byte packets */ +#define EXTconf 0x008 /* 250-504 byte packets */ + + /* buffers (4 total) used for receive and xmit. + */ +#define EnableReceiver() outb(RXcmd|(recbuf<<3)|RXbcasts,COMMAND) +/*#define TXbuf 2 (Obsoleted by ping-pong xmits) */ + + /* Protocol ID's */ +#define ARC_P_IP 212 /* 0xD4 */ +#define ARC_P_ARP 213 /* 0xD5 */ +#define ARC_P_RARP 214 /* 0xD6 */ +#define ARC_P_IPX 250 /* 0xFA */ +#define ARC_P_LANSOFT 251 /* 0xFB */ +#define ARC_P_ATALK 0xDD + + /* Length of time between "stuck" checks */ +#define TIMERval (HZ/8) /* about 1/8 second */ + + /* these structures define the format of an arcnet packet. */ +#define NORMAL 0 +#define EXTENDED 1 +#define EXCEPTION 2 + + /* the header required by the card itself */ +struct HardHeader +{ + u_char source, /* source ARCnet - filled in automagically */ + destination, /* destination ARCnet - 0 for broadcast */ + offset1, /* offset of ClientData (256-byte packets) */ + offset2; /* offset of ClientData (512-byte packets) */ +}; + + /* a complete ARCnet packet */ +union ArcPacket +{ + struct HardHeader hardheader; /* the hardware header */ + u_char raw[512]; /* raw packet info, incl ClientData */ +}; + + /* the "client data" header - RFC-1201 information + * notice that this screws up if it's not an even number of bytes + * <sigh> + */ +struct ClientData +{ + /* data that's NOT part of real packet */ + u_char daddr; /* Destination address - stored here, + * but WE MUST GET RID OF IT BEFORE SENDING A + * PACKET!! + */ + u_char saddr; /* Source address - necessary for IPX protocol */ + + /* data that IS part of real packet */ + u_char protocol_id, /* ARC_P_IP, ARC_P_ARP, or ARC_P_RARP */ + split_flag; /* for use with split packets */ + u_short sequence; /* sequence number (?) */ +}; +#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4) + + +/* "Incoming" is information needed for each address that could be sending + * to us. Mostly for partially-received split packets. + */ +struct Incoming +{ + struct sk_buff *skb; /* packet data buffer */ + unsigned char lastpacket, /* number of last packet (from 1) */ + numpackets; /* number of packets in split */ + u_short sequence; /* sequence number of assembly */ +}; + +struct Outgoing +{ + struct sk_buff *skb; /* buffer from upper levels */ + struct ClientData *hdr; /* clientdata of last packet */ + u_char *data; /* pointer to data in packet */ + short length, /* bytes total */ + dataleft, /* bytes left */ + segnum, /* segment being sent */ + numsegs, /* number of segments */ + seglen; /* length of segment */ +}; + + +/* Information that needs to be kept for each board. */ +struct arcnet_local { + struct enet_statistics stats; + u_char arcnum; /* arcnet number - our 8-bit address */ + u_short sequence; /* sequence number (incs with each packet) */ + u_char recbuf, /* receive buffer # (0 or 1) */ + txbuf, /* transmit buffer # (2 or 3) */ + txready; /* buffer where a packet is ready to send */ + short intx, /* in TX routine? */ + in_txhandler, /* in TX_IRQ handler? */ + sending; /* transmit in progress? */ + short tx_left; /* segments of split packet left to TX */ + struct timer_list timer; /* the timer interrupt struct */ + struct Incoming incoming[256]; /* one from each address */ + struct Outgoing outgoing; /* packet currently being sent */ +}; + + +/* Index to functions, as function prototypes. */ +extern int arcnet_probe(struct device *dev); +#ifndef MODULE +static int arcnet_memprobe(struct device *dev,u_char *addr); +static int arcnet_ioprobe(struct device *dev, short ioaddr); +#endif + +static int arcnet_open(struct device *dev); +static int arcnet_close(struct device *dev); + +static int arcnet_send_packet(struct sk_buff *skb, struct device *dev); +#ifdef CAREFUL_XMIT + static void careful_xmit_wait(struct device *dev); +#else + #define careful_xmit_wait(dev) +#endif +static void arcnet_continue_tx(struct device *dev); +static void arcnet_prepare_tx(struct device *dev,struct ClientData *hdr, + short length,char *data); +static void arcnet_go_tx(struct device *dev); + +static void arcnet_interrupt(int irq,struct pt_regs *regs); +static void arcnet_inthandler(struct device *dev); +static void arcnet_rx(struct device *dev,int recbuf); + +#ifdef USE_TIMER_HANDLER +static void arcnet_timer(unsigned long arg); +#endif + +static struct enet_statistics *arcnet_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); + + /* annoying functions for header/arp/etc building */ +int arc_header(unsigned char *buff,struct device *dev,unsigned short type, + void *daddr,void *saddr,unsigned len,struct sk_buff *skb); +int arc_rebuild_header(void *eth,struct device *dev,unsigned long raddr, + struct sk_buff *skb); +unsigned short arc_type_trans(struct sk_buff *skb,struct device *dev); + +static int arcnet_reset(struct device *dev); + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +#define tx_done(dev) 1 + +/* +#define JIFFER(time) for (delayval=jiffies+(time); delayval>jiffies;); +*/ +#define JIFFER(time) for (delayval=0; delayval<(time*10); delayval++) \ + udelay(1000); + +#ifdef EXTRA_DELAYS + #define XJIFFER(time) JIFFER(time) +#else + #define XJIFFER(time) +#endif + + +/* Check for a network adaptor of this type, and return '0' if 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 +arcnet_probe(struct device *dev) +{ +#ifndef MODULE + /* I refuse to probe anything less than 0x200, because anyone using + * an address like that should probably be shot. + */ + int *port, ports[] = {/* first the suggested values! */ + 0x300,0x2E0,0x2F0,0x2D0, + /* ...now everything else possible. */ + 0x200,0x210,0x220,0x230,0x240,0x250,0x260,0x270, + 0x280,0x290,0x2a0,0x2b0,0x2c0, + 0x310,0x320,0x330,0x340,0x350,0x360,0x370, + 0x380,0x390,0x3a0,/* video ports, */0x3e0,0x3f0, + /* a null ends the list */ + 0}; + /* I'm not going to probe below 0xA0000 either, for similar reasons. + */ + unsigned long *addr, addrs[] = {0xD0000,0xE0000,0xA0000,0xB0000, + 0xC0000,0xF0000, + /* from <mdrejhon@magi.com> */ + 0xE1000, + 0xDD000,0xDC000, + 0xD9000,0xD8000,0xD5000,0xD4000,0xD1000, + 0xCD000,0xCC000, + 0xC9000,0xC8000,0xC5000,0xC4000, + /* terminator */ + 0}; + int base_addr=dev->base_addr, status=0; +#endif /* MODULE */ + int delayval; + struct arcnet_local *lp; + + if (net_debug) + { + printk(version); + printk("arcnet: ***\n"); + printk("arcnet: * Read linux/drivers/net/README.arcnet for important release notes!\n"); + printk("arcnet: *\n"); + printk("arcnet: * This version should be stable, but e-mail me if you have any\n"); + printk("arcnet: * questions, comments, or bug reports!\n"); + printk("arcnet: ***\n"); + } + + BUGLVL(D_INIT) + printk("arcnet: given: base %lXh, IRQ %Xh, shmem %lXh\n", + dev->base_addr,dev->irq,dev->mem_start); + +#ifndef MODULE + if (base_addr > 0x1ff) /* Check a single specified location. */ + status=arcnet_ioprobe(dev, base_addr); + else if (base_addr > 0) /* Don't probe at all. */ + return ENXIO; + else for (port = &ports[0]; *port; port++) + { + int ioaddr = *port; + if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE)) + { + BUGLVL(D_INIT) + printk("arcnet: Skipping %Xh because of check_region...\n", + ioaddr); + continue; + } + + status=arcnet_ioprobe(dev, ioaddr); + if (!status) break; + } + + if (status) return status; + + /* ioprobe turned out okay. Now give it a couple seconds to finish + * initializing... + */ + BUGLVL(D_INIT) + printk("arcnet: ioprobe okay! Waiting for reset...\n"); + JIFFER(100); + + /* okay, now we have to find the shared memory area. */ + BUGLVL(D_INIT) + printk("arcnet: starting memory probe, given %lXh\n", + dev->mem_start); + if (dev->mem_start) /* value given - probe just that one */ + { + status=arcnet_memprobe(dev,(u_char *)dev->mem_start); + if (status) return status; + } + else /* no value given - probe everything */ + { + for (addr = &addrs[0]; *addr; addr++) { + status=arcnet_memprobe(dev,(u_char *)(*addr)); + if (!status) break; + } + + if (status) return status; + } +#else /* MODULE */ + if (!dev->base_addr || !dev->irq || !dev->mem_start + || !dev->rmem_start) + { + printk("arcnet: loadable modules can't autoprobe!\n"); + printk("arcnet: try using io=, irqnum=, and shmem= on the insmod line.\n"); + printk("arcnet: you may also need num= to change the device name. (ie. num=1 for arc1)\n"); + return ENODEV; + } +#endif + /* now reserve the irq... */ + { + int irqval = request_irq(dev->irq, &arcnet_interrupt, 0, + "arcnet"); + if (irqval) { + printk("%s: unable to get IRQ %d (irqval=%d).\n", + dev->name,dev->irq, irqval); + return EAGAIN; + } + } + + /* Grab the region so we can find another board if autoIRQ fails. */ + request_region(dev->base_addr, ETHERCARD_TOTAL_SIZE,"arcnet"); + + printk("%s: ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n", + dev->name, dev->base_addr, dev->irq, dev->mem_start); + + /* Initialize the device structure. */ + dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct arcnet_local)); + lp=(struct arcnet_local *)(dev->priv); + + dev->open = arcnet_open; + dev->stop = arcnet_close; + dev->hard_start_xmit = arcnet_send_packet; + dev->get_stats = arcnet_get_stats; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_multicast_list; +#endif + + /* Fill in the fields of the device structure with ethernet-generic values. */ + ether_setup(dev); + + /* And now fill particular ones with arcnet values :) */ + + dev->type=ARPHRD_ARCNET; + dev->hard_header_len=sizeof(struct ClientData); + BUGLVL(D_EXTRA) + printk("arcnet: ClientData header size is %d.\narcnet: HardHeader size is %d.\n", + sizeof(struct ClientData),sizeof(struct HardHeader)); +#if LIMIT_MTU /* the old way - normally, now use ethernet default */ + dev->mtu=512-sizeof(struct HardHeader)+EXTRA_CLIENTDATA; +#endif + /* since we strip EXTRA_CLIENTDATA bytes off before sending, + * we let Linux add that many bytes to the packet data... + */ + dev->addr_len=1; + dev->broadcast[0]=0x00; + + BUGLVL(D_INIT) printk("arcnet: arcnet_probe: resetting card.\n"); + arcnet_reset(dev); + JIFFER(50); + BUGLVL(D_NORMAL) + printk("arcnet: We appear to be station %d (%02Xh)\n", + lp->arcnum,lp->arcnum); + if (lp->arcnum==0) + printk("arcnet: WARNING! Station address 0 is reserved for broadcasts!\n"); + if (lp->arcnum==255) + printk("arcnet: WARNING! Station address 255 may confuse DOS networking programs!\n"); + dev->dev_addr[0]=lp->arcnum; + lp->sequence=1; + lp->recbuf=0; + + dev->hard_header = arc_header; + dev->rebuild_header = arc_rebuild_header; + + return 0; +} + +#ifndef MODULE + +int arcnet_ioprobe(struct device *dev, short ioaddr) +{ + int delayval,airq; + + BUGLVL(D_INIT) + printk("arcnet: probing address %Xh\n",ioaddr); + + BUGLVL(D_INIT) + printk("arcnet: status1=%Xh\n",inb(STATUS)); + + + /* very simple - all we have to do is reset the card, and if there's + * no irq, it's not an ARCnet. We can also kill two birds with + * one stone because we detect the IRQ at the same time :) + */ + + /* reset the card by reading the reset port */ + inb(RESET); + JIFFER(RESETtime); + + /* if status port is FF, there's certainly no arcnet... give up. */ + if (inb(STATUS)==0xFF) + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. Status port empty.\n"); + return ENODEV; + } + + /* we'll try to be reasonably sure it's an arcnet by making sure + * the value of the COMMAND port changes automatically once in a + * while. I have no idea what those values ARE, but at least + * they work. + */ + { + int initval,curval; + + curval=initval=inb(COMMAND); + delayval=jiffies+5; + while (delayval>=jiffies && curval==initval) + curval=inb(COMMAND); + + if (curval==initval) + { + printk("arcnet: probe failed. never-changing command port (%02Xh).\n", + initval); + return ENODEV; + } + } + + BUGLVL(D_INIT) + printk("arcnet: status2=%Xh\n",inb(STATUS)); + + /* now we turn the reset bit off so we can IRQ next reset... */ + outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND); + XJIFFER(ACKtime); + if (inb(STATUS) & RESETflag) /* reset flag STILL on */ + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. eternal reset flag1...(status=%Xh)\n", + inb(STATUS)); + return ENODEV; + } + + /* set up automatic IRQ detection */ + autoirq_setup(0); + + /* enable reset IRQ's (shouldn't be necessary, but worth a try) */ + outb(RESETflag,INTMASK); + + /* now reset it again to generate an IRQ */ + inb(RESET); + JIFFER(RESETtime); + + BUGLVL(D_INIT) + printk("arcnet: status3=%Xh\n",inb(STATUS)); + + /* and turn the reset flag back off */ + outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND); + XJIFFER(ACKtime); + + BUGLVL(D_INIT) + printk("arcnet: status4=%Xh\n",inb(STATUS)); + + /* enable reset IRQ's again */ + outb(RESETflag,INTMASK); + + /* now reset it again to generate an IRQ */ + inb(RESET); + JIFFER(RESETtime); + + BUGLVL(D_INIT) + printk("arcnet: status5=%Xh\n",inb(STATUS)); + + /* if we do this, we're sure to get an IRQ since the card has + * just reset and the NORXflag is on until we tell it to start + * receiving. + * + * However, this could, theoretically, cause a lockup. Maybe I'm just + * not very good at theory! :) + */ +#ifdef DANGER_PROBE + outb(NORXflag,INTMASK); + JIFFER(RESETtime); + outb(0,INTMASK); +#endif + + /* and turn the reset flag back off */ + outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND); + XJIFFER(ACKtime); + + airq = autoirq_report(0); + if (net_debug>=D_INIT && airq) + printk("arcnet: autoirq is %d\n", airq); + + /* if there was no autoirq AND the user hasn't set any defaults, + * give up. + */ + if (!airq && !(dev->base_addr && dev->irq)) + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. no autoirq...\n"); + return ENODEV; + } + + /* otherwise we probably have a card. Let's make sure. */ + + if (inb(STATUS) & RESETflag) /* reset flag on */ + { + /* now we turn the reset bit off */ + outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND); + XJIFFER(ACKtime); + } + + if (inb(STATUS) & RESETflag) /* reset flag STILL on */ + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. eternal reset flag...(status=%Xh)\n", + inb(STATUS)); + return ENODEV; + } + + /* okay, we've got a real, live ARCnet on our hands. */ + if (!dev->base_addr) dev->base_addr=ioaddr; + + if (dev->irq < 2) /* "Auto-IRQ" */ + { + /* we already did the autoirq above, so store the values */ + dev->irq=airq; + } + else if (dev->irq == 2) + { + if (net_debug) + printk("arcnet: IRQ2 == IRQ9, don't worry.\n"); + dev->irq = 9; + } + + BUGLVL(D_INIT) + printk("arcnet: irq and base address seem okay. (%lXh, IRQ %d)\n", + dev->base_addr,dev->irq); + return 0; +} + + +/* A memory probe that is called after the card is reset. + * It checks for the official TESTvalue in byte 0 and makes sure the buffer + * has certain characteristics of an ARCnet... + */ +int arcnet_memprobe(struct device *dev,u_char *addr) +{ + BUGLVL(D_INIT) + printk("arcnet: probing memory at %lXh\n",(u_long)addr); + + dev->mem_start=0; + +#ifdef STRICT_MEM_DETECT /* probably better. */ + /* ARCnet memory byte 0 is TESTvalue */ + if (addr[0]!=TESTvalue) + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not %Xh)\n", + (unsigned long)addr,addr[0],TESTvalue); + return ENODEV; + } + + /* now verify the shared memory writability */ + addr[0]=0x42; + if (addr[0]!=0x42) + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not 42h)\n", + (unsigned long)addr,addr[0]); + return ENODEV; + } +#else + if (addr[0]!=TESTvalue) + { + BUGLVL(D_INIT) + printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not %Xh)\n", + (unsigned long)addr,addr[0],TESTvalue); + return ENODEV; + } +#endif + + /* got it! fill in dev */ + dev->mem_start=(unsigned long)addr; + dev->mem_end=dev->mem_start+512*4-1; + dev->rmem_start=dev->mem_start+512*0; + dev->rmem_end=dev->mem_start+512*2-1; + + return 0; +} + +#endif /* MODULE */ + + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ +static int +arcnet_open(struct device *dev) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; +/* int ioaddr = dev->base_addr;*/ + + if (dev->metric>=10) + { + net_debug=dev->metric-10; + dev->metric=1; + } + + if (net_debug) printk(version); + +#if 0 /* Yup, they're hardwired in arcnets */ + /* This is used if the interrupt line can turned off (shared). + See 3c503.c for an example of selecting the IRQ at config-time. */ + if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet")) { + return -EAGAIN; + } +#endif + + irq2dev_map[dev->irq] = dev; + + /* Reset the hardware here. */ + BUGLVL(D_EXTRA) printk("arcnet: arcnet_open: resetting card.\n"); + + /* try to reset - twice if it fails the first time */ + if (arcnet_reset(dev) && arcnet_reset(dev)) + return -ENODEV; + +/* chipset_init(dev, 1);*/ +/* outb(0x00, ioaddr);*/ + +/* lp->open_time = jiffies;*/ + + dev->tbusy=0; + dev->interrupt=0; + dev->start=1; + lp->intx=0; + lp->in_txhandler=0; + +#ifdef USE_TIMER_HANDLER + /* grab a timer handler to recover from any missed IRQ's */ + init_timer(&lp->timer); + lp->timer.expires = TIMERval; /* length of time */ + lp->timer.data = (unsigned long)dev; /* pointer to "dev" structure */ + lp->timer.function = &arcnet_timer; /* timer handler */ + add_timer(&lp->timer); +#endif + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + + +/* The inverse routine to arcnet_open(). */ +static int +arcnet_close(struct device *dev) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + int ioaddr = dev->base_addr; +#ifdef EXTRA_DELAYS + int delayval; +#endif + +/* lp->open_time = 0;*/ + + dev->tbusy = 1; + dev->start = 0; + + /* release the timer */ + del_timer(&lp->timer); + + /* Flush the Tx and disable Rx here. */ + /* resetting the card should do the job. */ + /*inb(RESET);*/ + + outb(0,INTMASK); /* no IRQ's */ + outb(NOTXcmd,COMMAND); /* disable transmit */ + XJIFFER(ACKtime); + outb(NORXcmd,COMMAND); /* disable receive */ + + /* Update the statistics here. */ + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + + +static int +arcnet_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + int ioaddr=dev->base_addr; +/* short daddr;*/ + + lp->intx++; + + BUGLVL(D_DURING) + printk("arcnet: transmit requested (status=%Xh, inTX=%d)\n", + inb(STATUS),lp->intx); + + if (dev->tbusy || lp->in_txhandler) + { + /* 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; + int recbuf=lp->recbuf; + int status=inb(STATUS); + + /* resume any stopped tx's */ +#if 0 + if (lp->txready && (inb(STATUS)&TXFREEflag)) + { + printk("arcnet: kickme: starting a TX (status=%Xh)\n", + inb(STATUS)); + arcnet_go_tx(dev); + lp->intx--; + return 1; + } +#endif + + if (tickssofar < 5) + { + BUGLVL(D_DURING) + printk("arcnet: premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n", + status,tickssofar,lp->outgoing.skb, + lp->outgoing.numsegs, + lp->outgoing.segnum); + lp->intx--; + return 1; + } + + BUGLVL(D_INIT) + printk("arcnet: transmit timed out (status=%Xh, inTX=%d, tickssofar=%d)\n", + status,lp->intx,tickssofar); + + /* Try to restart the adaptor. */ + /*arcnet_reset(dev);*/ + + if (status&NORXflag) EnableReceiver(); + if (!(status&TXFREEflag)) outb(NOTXcmd,COMMAND); + dev->trans_start = jiffies; + + if (lp->outgoing.skb) + dev_kfree_skb(lp->outgoing.skb,FREE_WRITE); + lp->outgoing.skb=NULL; + + dev->tbusy=0; + mark_bh(NET_BH); + lp->intx=0; + lp->in_txhandler=0; + lp->txready=0; + lp->sending=0; + + return 1; + } + + /* If some higher layer thinks we've missed a tx-done interrupt + we are passed NULL. Caution: dev_tint() handles the cli()/sti() + itself. */ + if (skb == NULL) { + BUGLVL(D_INIT) + printk("arcnet: tx passed null skb (status=%Xh, inTX=%d, tickssofar=%ld)\n", + inb(STATUS),lp->intx,jiffies-dev->trans_start); + dev_tint(dev); + lp->intx--; + return 0; + } + + if (lp->txready) /* transmit already in progress! */ + { + printk("arcnet: trying to start new packet while busy!\n"); + printk("arcnet: marking as not ready.\n"); + lp->txready=0; + return 1; + } + + /* 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 (set_bit(0, (void*)&dev->tbusy) != 0) + { + printk("arcnet: transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n", + inb(STATUS),lp->intx,jiffies-dev->trans_start); + lp->intx--; + return -EBUSY; + } + else { + struct Outgoing *out=&(lp->outgoing); + out->length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + out->hdr=(struct ClientData*)skb->data; + out->skb=skb; + BUGLVL( D_DATA ) { + short i; + for( i=0; i< skb->len; i++) + { + if( i%16 == 0 ) printk("\n[%04hX] ",i); + printk("%02hX ",((unsigned char*)skb->data)[i]); + } + printk("\n"); + } + +#ifdef IRQ_XMIT + if (lp->txready && inb(STATUS)&TXFREEflag) + arcnet_go_tx(dev); +#endif + + + if (out->length<=XMTU) /* fits in one packet? */ + { + BUGLVL(D_TX) printk("arcnet: not splitting %d-byte packet. (split_flag=%d)\n", + out->length,out->hdr->split_flag); + BUGLVL(D_INIT) if (out->hdr->split_flag) + printk("arcnet: short packet has split_flag set?! (split_flag=%d)\n", + out->hdr->split_flag); + out->numsegs=1; + out->segnum=1; + arcnet_prepare_tx(dev,out->hdr, + out->length-sizeof(struct ClientData), + ((char *)skb->data)+sizeof(struct ClientData)); + careful_xmit_wait(dev); + + /* done right away */ + dev_kfree_skb(out->skb,FREE_WRITE); + out->skb=NULL; + + if (!lp->sending) + { + arcnet_go_tx(dev); + + /* inform upper layers */ + dev->tbusy=0; + mark_bh(NET_BH); + } + } + else /* too big for one - split it */ + { + int maxsegsize=XMTU-sizeof(struct ClientData); + + out->data=(u_char *)skb->data + + sizeof(struct ClientData); + out->dataleft=out->length-sizeof(struct ClientData); + out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize; + + out->segnum=0; + + BUGLVL(D_TX) printk("arcnet: packet (%d bytes) split into %d fragments:\n", + out->length,out->numsegs); + +#ifdef IRQ_XMIT + /* if a packet waiting, launch it */ + if (lp->txready && inb(STATUS)&TXFREEflag) + arcnet_go_tx(dev); + + if (!lp->txready) + { + /* prepare a packet, launch it and prepare + * another. + */ + arcnet_continue_tx(dev); + if (!lp->sending) + { + arcnet_go_tx(dev); + arcnet_continue_tx(dev); + if (!lp->sending) + arcnet_go_tx(dev); + } + } + + /* if segnum==numsegs, the transmission is finished; + * free the skb right away. + */ + if (out->segnum==out->numsegs) + { + /* transmit completed */ + out->segnum++; + if (out->skb) + dev_kfree_skb(out->skb,FREE_WRITE); + out->skb=NULL; +#if 0 + /* inform upper layers */ + dev->tbusy=0; + mark_bh(NET_BH); +#endif + } + +#else /* non-irq xmit */ + while (out->segnum<out->numsegs) + { + arcnet_continue_tx(dev); + careful_xmit_wait(dev); + arcnet_go_tx(dev); + dev->trans_start=jiffies; + } + + dev_kfree_skb(out->skb,FREE_WRITE); + out->skb=NULL; + + /* inform upper layers */ + dev->tbusy = 0; + mark_bh(NET_BH); +#endif + } + } + + lp->intx--; + lp->stats.tx_packets++; + dev->trans_start=jiffies; + return 0; +} + +static void arcnet_continue_tx(struct device *dev) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + int maxsegsize=XMTU-sizeof(struct ClientData); + struct Outgoing *out=&(lp->outgoing); + + if (lp->txready) + { + printk("arcnet: continue_tx: called with packet in buffer!\n"); + return; + } + + if (out->segnum>=out->numsegs) + { + printk("arcnet: continue_tx: building segment %d of %d!\n", + out->segnum+1,out->numsegs); + } + + if (!out->segnum) /* first packet */ + out->hdr->split_flag=((out->numsegs-2)<<1)+1; + else + out->hdr->split_flag=out->segnum<<1; + + out->seglen=maxsegsize; + if (out->seglen>out->dataleft) out->seglen=out->dataleft; + + BUGLVL(D_TX) printk("arcnet: building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n", + out->segnum+1,out->seglen,out->numsegs, + out->length,out->hdr->split_flag); + + arcnet_prepare_tx(dev,out->hdr,out->seglen,out->data); + + out->dataleft-=out->seglen; + out->data+=out->seglen; + out->segnum++; +} + +#ifdef CAREFUL_XMIT +static void careful_xmit_wait(struct device *dev) +{ + int ioaddr=dev->base_addr; + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + + /* wait patiently for tx to become available again */ + while ( !(inb(STATUS)&TXFREEflag) ) + { + if (jiffies-dev->trans_start > 20 || !dev->tbusy) + { + BUGLVL(D_INIT) + printk("arcnet: CAREFUL_XMIT timeout. (busy=%d, status=%Xh)\n", + dev->tbusy,inb(STATUS)); + lp->stats.tx_errors++; + + outb(NOTXcmd,COMMAND); + return; + } + } + BUGLVL(D_TX) printk("arcnet: transmit completed successfully. (status=%Xh)\n", + inb(STATUS)); +} +#endif + +static void +arcnet_prepare_tx(struct device *dev,struct ClientData *hdr,short length, + char *data) +{ +/* int ioaddr = dev->base_addr;*/ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + struct ClientData *arcsoft; + union ArcPacket *arcpacket = + (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1)); + u_char pkttype; + int offset; + short daddr; + + lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */ + + length+=sizeof(struct ClientData); + + BUGLVL(D_TX) + printk("arcnet: arcnet_prep_tx: hdr:%ph, length:%d, data:%ph\n", + hdr,length,data); + + /* clean out the page to make debugging make more sense :) */ + BUGLVL(D_DURING) + memset((void *)dev->mem_start+lp->txbuf*512,0x42,512); + + daddr=arcpacket->hardheader.destination=hdr->daddr; + + /* load packet into shared memory */ + if (length<=MTU) /* Normal (256-byte) Packet */ + { + pkttype=NORMAL; + + arcpacket->hardheader.offset1=offset=256-length + + EXTRA_CLIENTDATA; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset-EXTRA_CLIENTDATA]); + } + else if (length>=MinTU) /* Extended (512-byte) Packet */ + { + pkttype=EXTENDED; + + arcpacket->hardheader.offset1=0; + arcpacket->hardheader.offset2=offset=512-length + + EXTRA_CLIENTDATA; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset-EXTRA_CLIENTDATA]); + } + else /* Exception Packet */ + { + pkttype=EXCEPTION; + + arcpacket->hardheader.offset1=0; + arcpacket->hardheader.offset2=offset=512-length-4 + + EXTRA_CLIENTDATA; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset+4-EXTRA_CLIENTDATA]); + + /* exception-specific stuff - these four bytes + * make the packet long enough to fit in a 512-byte + * frame. + */ + arcpacket->raw[offset+0]=arcsoft->protocol_id; + arcpacket->raw[offset+1]=0xFF; /* FF flag */ + arcpacket->raw[offset+2]=0xFF; /* FF padding */ + arcpacket->raw[offset+3]=0xFF; /* FF padding */ + } + + + /* copy the packet into ARCnet shmem + * - the first bytes of ClientData header are skipped + */ + memcpy((u_char*)arcsoft+EXTRA_CLIENTDATA, + (u_char*)hdr+EXTRA_CLIENTDATA, + sizeof(struct ClientData)-EXTRA_CLIENTDATA); + memcpy((u_char*)arcsoft+sizeof(struct ClientData), + data, + length-sizeof(struct ClientData)); + + BUGLVL(D_DURING) printk("arcnet: transmitting packet to station %02Xh (%d bytes, type=%d)\n", + daddr,length,pkttype); + + BUGLVL(D_TX) + { + int countx,county; + + printk("arcnet: packet dump [tx] follows:"); + + for (county=0; county<16+(pkttype!=NORMAL)*16; county++) + { + printk("\n[%04X] ",county*16); + for (countx=0; countx<16; countx++) + printk("%02X ", + arcpacket->raw[county*16+countx]); + } + + printk("\n"); + } +#ifdef CAREFUL_XMIT + #if 0 + careful_xmit_wait(dev); + + /* if we're not broadcasting, make sure the xmit was ack'd. + * if it wasn't, there is probably no card with that + * address... or else it missed our tx somehow. + */ + if (daddr && !(inb(STATUS)&TXACKflag)) + { + BUGLVL(D_INIT) + printk("arcnet: transmit not acknowledged. (status=%Xh, daddr=%02Xh)\n", + inb(STATUS),daddr); + lp->stats.tx_errors++; + return -ENONET; /* "machine is not on the network" */ + } + #endif +#endif + lp->txready=lp->txbuf; /* packet is ready for sending */ + +#if 0 +#ifdef IRQ_XMIT + if (inb(STATUS)&TXFREEflag) arcnet_go_tx(dev); +#endif +#endif +} + +static void +arcnet_go_tx(struct device *dev) +{ + struct arcnet_local *lp=(struct arcnet_local *)dev->priv; + int ioaddr=dev->base_addr; + + BUGLVL(D_DURING) + printk("arcnet: go_tx: status=%Xh\n", + inb(STATUS)); + + if (!(inb(STATUS)&TXFREEflag) || !lp->txready) return; + + /* start sending */ + outb(TXcmd|(lp->txready<<3),COMMAND); + +#ifdef IRQ_XMIT + outb(TXFREEflag|NORXflag,INTMASK); +#endif + + dev->trans_start = jiffies; + lp->txready=0; + lp->sending++; +} + + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static void +arcnet_interrupt(int irq,struct pt_regs *regs) +{ + struct device *dev = (struct device *)(irq2dev_map[irq]); + + if (dev == NULL) { + if (net_debug >= D_DURING) + printk("arcnet: irq %d for unknown device.\n", irq); + return; + } + + arcnet_inthandler(dev); +} + +static void +arcnet_inthandler(struct device *dev) +{ + struct arcnet_local *lp; + int ioaddr, status, boguscount = 3, didsomething; + + dev->interrupt = 1; + sti(); + + ioaddr = dev->base_addr; + lp = (struct arcnet_local *)dev->priv; + +#ifdef IRQ_XMIT + outb(0,INTMASK); +#endif + + BUGLVL(D_DURING) + printk("arcnet: in net_interrupt (status=%Xh)\n",inb(STATUS)); + + do + { + status = inb(STATUS); + didsomething=0; + + if (!dev->start) + { + BUGLVL(D_EXTRA) + printk("arcnet: ARCnet not yet initialized. irq ignored. (status=%Xh)\n", + status); +#ifdef IRQ_XMIT + if (!(status&NORXflag)) + outb(NORXflag,INTMASK); +#endif + dev->interrupt=0; + return; + } + + /* RESET flag was enabled - card is resetting and if RX + * is disabled, it's NOT because we just got a packet. + */ + if (status & RESETflag) + { + BUGLVL(D_INIT) + printk("arcnet: reset irq (status=%Xh)\n", + status); + dev->interrupt=0; + return; + } + +#if 1 /* yes, it's silly to disable this part but it makes good testing */ + /* RX is inhibited - we must have received something. */ + if (status & NORXflag) + { + int recbuf=lp->recbuf=!lp->recbuf; + + BUGLVL(D_DURING) + printk("arcnet: receive irq (status=%Xh)\n", + status); + + /* enable receive of our next packet */ + EnableReceiver(); + + /* Got a packet. */ + arcnet_rx(dev,!recbuf); + + didsomething++; + } +#endif +#ifdef IRQ_XMIT + /* it can only be an xmit-done irq if we're xmitting :) */ + if (status&TXFREEflag && !lp->in_txhandler && lp->sending) + { + struct Outgoing *out=&(lp->outgoing); + + lp->in_txhandler++; + lp->sending--; + + BUGLVL(D_DURING) + printk("arcnet: TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n", + status,out->numsegs,out->segnum,out->skb); + + /* send packet if there is one */ + if (lp->txready) + { + arcnet_go_tx(dev); + didsomething++; + } + + if (lp->intx) + { + lp->in_txhandler--; + continue; + } + + if (!lp->outgoing.skb) + { + BUGLVL(D_DURING) + printk("arcnet: TX IRQ done: no split to continue.\n"); + + /* inform upper layers */ + if (!lp->txready && dev->tbusy) + { + dev->tbusy=0; + mark_bh(NET_BH); + } + + lp->in_txhandler--; + continue; + } + + /*lp->stats.tx_packets++;*/ + + /* if more than one segment, and not all segments + * are done, then continue xmit. + */ + if (out->segnum<out->numsegs) + arcnet_continue_tx(dev); + if (lp->txready && !lp->sending) + arcnet_go_tx(dev); + + /* if segnum==numsegs, the transmission is finished; + * free the skb. + */ + if (out->segnum>=out->numsegs) + { + /* transmit completed */ + out->segnum++; + if (out->skb) + dev_kfree_skb(out->skb,FREE_WRITE); + out->skb=NULL; + + /* inform upper layers */ + if (!lp->txready && dev->tbusy) + { + dev->tbusy=0; + mark_bh(NET_BH); + } + } + didsomething++; + + lp->in_txhandler--; + } +#endif /* IRQ_XMIT */ + } while (--boguscount && didsomething); + + BUGLVL(D_DURING) + printk("arcnet: net_interrupt complete (status=%Xh)\n", + inb(STATUS)); + +#ifdef IRQ_XMIT + if (dev->start && lp->sending ) + outb(NORXflag|TXFREEflag,INTMASK); + else + outb(NORXflag,INTMASK); +#endif + + dev->interrupt=0; +} + +/* A packet has arrived; grab it from the buffers and possibly unsplit it. + */ +static void +arcnet_rx(struct device *dev,int recbuf) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + int ioaddr = dev->base_addr; +/* int status = inb(STATUS);*/ + + struct sk_buff *skb; + + union ArcPacket *arcpacket= + (union ArcPacket *)(dev->mem_start+recbuf*512); + struct ClientData *soft,*arcsoft; + short length,offset; + u_char pkttype,daddr,saddr; + + daddr=arcpacket->hardheader.destination; + saddr=arcpacket->hardheader.source; + + /* if source is 0, it's not a "used" packet! */ + if (saddr==0) + { + /*BUGLVL(D_DURING)*/ + printk("arcnet: discarding old packet. (status=%Xh)\n", + inb(STATUS)); + lp->stats.rx_errors++; + return; + } + arcpacket->hardheader.source=0; + + if (arcpacket->hardheader.offset1) /* Normal Packet */ + { + offset=arcpacket->hardheader.offset1; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset-EXTRA_CLIENTDATA]); + length=256-offset+EXTRA_CLIENTDATA; + pkttype=NORMAL; + } + else /* ExtendedPacket or ExceptionPacket */ + { + offset=arcpacket->hardheader.offset2; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset-EXTRA_CLIENTDATA]); + + if (arcsoft->split_flag!=0xFF) /* Extended Packet */ + { + length=512-offset+EXTRA_CLIENTDATA; + pkttype=EXTENDED; + } + else /* Exception Packet */ + { + /* skip over 4-byte junkola */ + arcsoft=(struct ClientData *) + ((u_char *)arcsoft + 4); + length=512-offset+EXTRA_CLIENTDATA-4; + pkttype=EXCEPTION; + } + } + + if (!arcsoft->split_flag) /* not split */ + { + struct Incoming *in=&lp->incoming[saddr]; + + BUGLVL(D_RX) printk("arcnet: incoming is not split (splitflag=%d)\n", + arcsoft->split_flag); + + if (in->skb) /* already assembling one! */ + { + BUGLVL(D_INIT) printk("arcnet: aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n", + in->sequence,arcsoft->split_flag, + arcsoft->sequence); + kfree_skb(in->skb,FREE_WRITE); + in->skb=NULL; + } + + in->sequence=arcsoft->sequence; + + skb = alloc_skb(length, GFP_ATOMIC); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", + dev->name); + lp->stats.rx_dropped++; + return; + } + soft=(struct ClientData *)skb->data; + + skb->len = length; + skb->dev = dev; + + memcpy((u_char *)soft+EXTRA_CLIENTDATA, + (u_char *)arcsoft+EXTRA_CLIENTDATA, + length-EXTRA_CLIENTDATA); + soft->daddr=daddr; + soft->saddr=saddr; + + BUGLVL(D_DURING) + printk("arcnet: received packet from %02Xh to %02Xh (%d bytes, type=%d)\n", + saddr,daddr,length,pkttype); + BUGLVL(D_RX) + { + int countx,county; + + printk("arcnet: packet dump [rx-unsplit] follows:"); + + for (county=0; county<16+(pkttype!=NORMAL)*16; county++) + { + printk("\n[%04X] ",county*16); + for (countx=0; countx<16; countx++) + printk("%02X ", + arcpacket->raw[county*16+countx]); + } + + printk("\n"); + } + + /* ARP packets have problems when sent from DOS. + * source address is always 0! So we take the hardware + * source addr (which is impossible to fumble) and insert + * it ourselves. + */ + if (soft->protocol_id == ARC_P_ARP) + { + struct arphdr *arp=(struct arphdr *) + ((char *)soft+sizeof(struct ClientData)); + + /* make sure addresses are the right length */ + if (arp->ar_hln==1 && arp->ar_pln==4) + { + char *cptr=(char *)(arp)+sizeof(struct arphdr); + + if (!*cptr) /* is saddr = 00? */ + { + BUGLVL(D_DURING) + printk("arcnet: ARP source address was 00h, set to %02Xh.\n", + saddr); + *cptr=saddr; + } + else BUGLVL(D_DURING) + { + printk("arcnet: ARP source address (%Xh) is fine.\n", + *cptr); + } + } + else + { + printk("arcnet: funny-shaped ARP packet. (%Xh, %Xh)\n", + arp->ar_hln,arp->ar_pln); + } + } + skb->protocol=arc_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + } + else /* split packet */ + { + /* NOTE: MSDOS ARP packet correction should only need to + * apply to unsplit packets, since ARP packets are so short. + * + * My interpretation of the RFC1201 (ARCnet) document is that + * if a packet is received out of order, the entire assembly + * process should be aborted. + * + * The RFC also mentions "it is possible for successfully + * received packets to be retransmitted." As of 0.40 all + * previously received packets are allowed, not just the + * most recent one. + * + * We allow multiple assembly processes, one for each + * ARCnet card possible on the network. Seems rather like + * a waste of memory. Necessary? + */ + + struct Incoming *in=&lp->incoming[saddr]; + + BUGLVL(D_RX) printk("arcnet: packet is split (splitflag=%d, seq=%d)\n", + arcsoft->split_flag,in->sequence); + + if (in->skb && in->sequence!=arcsoft->sequence) + { + BUGLVL(D_INIT) printk("arcnet: wrong seq number, aborting assembly (expected=%d, seq=%d, splitflag=%d)\n", + in->sequence,arcsoft->sequence, + arcsoft->split_flag); + kfree_skb(in->skb,FREE_WRITE); + in->skb=NULL; + in->lastpacket=in->numpackets=0; + } + + if (arcsoft->split_flag & 1) /* first packet in split */ + { + BUGLVL(D_RX) printk("arcnet: brand new splitpacket (splitflag=%d)\n", + arcsoft->split_flag); + if (in->skb) /* already assembling one! */ + { + BUGLVL(D_INIT) printk("arcnet: aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n", + in->sequence,arcsoft->split_flag, + arcsoft->sequence); + kfree_skb(in->skb,FREE_WRITE); + } + + in->sequence=arcsoft->sequence; + in->numpackets=((unsigned)arcsoft->split_flag>>1)+2; + in->lastpacket=1; + + if (in->numpackets>16) + { + printk("arcnet: incoming packet more than 16 segments; dropping. (splitflag=%d)\n", + arcsoft->split_flag); + lp->stats.rx_dropped++; + return; + } + + in->skb=skb=alloc_skb(508*in->numpackets + + sizeof(struct ClientData), + GFP_ATOMIC); + if (skb == NULL) { + printk("%s: (split) memory squeeze, dropping packet.\n", + dev->name); + lp->stats.rx_dropped++; + return; + } + + /* I don't know what this is for, but it DOES avoid + * warnings... + */ + skb->free=1; + + soft=(struct ClientData *)skb->data; + + skb->len=sizeof(struct ClientData); + skb->dev=dev; + + memcpy((u_char *)soft+EXTRA_CLIENTDATA, + (u_char *)arcsoft+EXTRA_CLIENTDATA, + sizeof(struct ClientData)-EXTRA_CLIENTDATA); + soft->split_flag=0; /* final packet won't be split */ + } + else /* not first packet */ + { + int packetnum=((unsigned)arcsoft->split_flag>>1) + 1; + + /* if we're not assembling, there's no point + * trying to continue. + */ + if (!in->skb) + { + BUGLVL(D_INIT) printk("arcnet: can't continue split without starting first! (splitflag=%d, seq=%d)\n", + arcsoft->split_flag,arcsoft->sequence); + return; + } + + in->lastpacket++; + if (packetnum!=in->lastpacket) /* not the right flag! */ + { + /* harmless duplicate? ignore. */ + if (packetnum<=in->lastpacket-1) + { + BUGLVL(D_INIT) printk("arcnet: duplicate splitpacket ignored! (splitflag=%d)\n", + arcsoft->split_flag); + return; + } + + /* "bad" duplicate, kill reassembly */ + BUGLVL(D_INIT) printk("arcnet: out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n", + in->sequence,arcsoft->split_flag, + arcsoft->sequence); + kfree_skb(in->skb,FREE_WRITE); + in->skb=NULL; + in->lastpacket=in->numpackets=0; + return; + } + + soft=(struct ClientData *)in->skb->data; + } + + skb=in->skb; + + memcpy(skb->data+skb->len, + (u_char *)arcsoft+sizeof(struct ClientData), + length-sizeof(struct ClientData)); + + skb->len+=length-sizeof(struct ClientData); + + soft->daddr=daddr; + soft->saddr=saddr; + + BUGLVL(D_DURING) + printk("arcnet: received packet from %02Xh to %02Xh (%d bytes, type=%d)\n", + saddr,daddr,length,pkttype); + BUGLVL(D_RX) + { + int countx,county; + + printk("arcnet: packet dump [rx-split] follows:"); + + for (county=0; county<16+(pkttype!=NORMAL)*16; county++) + { + printk("\n[%04X] ",county*16); + for (countx=0; countx<16; countx++) + printk("%02X ", + arcpacket->raw[county*16+countx]); + } + + printk("\n"); + } + + /* are we done? */ + if (in->lastpacket == in->numpackets) + { + if (!skb || !in->skb) + printk("arcnet: ?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n", + skb,in->skb); + in->skb=NULL; + in->lastpacket=in->numpackets=0; + skb->protocol=arc_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + } + } + + /* If any worth-while packets have been received, netif_rx() + has done a mark_bh(NET_BH) for us and will work on them + when we get to the bottom-half routine. */ +} + + +#ifdef USE_TIMER_HANDLER +/* this function is called every once in a while to make sure the ARCnet + * isn't stuck. + * + * If we miss a receive IRQ, the receiver (and IRQ) is permanently disabled + * and we might never receive a packet again! This will check if this + * is the case, and if so, re-enable the receiver. + */ +static void +arcnet_timer(unsigned long arg) +{ + struct device *dev=(struct device *)arg; + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; + short ioaddr=dev->base_addr; + int status=inb(STATUS); + + /* if we didn't interrupt the IRQ handler, and RX's are still + * disabled, and we're not resetting the card... then we're stuck! + */ + if (!dev->interrupt && dev->start + && status&NORXflag && !status&RESETflag) + { + BUGLVL(D_INIT) + printk("arcnet: timer: ARCnet was stuck! (status=%Xh)\n", + status); + + arcnet_inthandler(dev); + } + + /* requeue ourselves */ + init_timer(&lp->timer); + lp->timer.expires=TIMERval; + add_timer(&lp->timer); +} +#endif + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct enet_statistics * +arcnet_get_stats(struct device *dev) +{ + struct arcnet_local *lp = (struct arcnet_local *)dev->priv; +/* short ioaddr = dev->base_addr;*/ + + return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ +static void +set_multicast_list(struct device *dev, int num_addrs, void *addrs) +{ +#if 0 /* no promiscuous mode at all */ + struct arcnet_local *lp=(struct arcnet_local *)(dev->priv); + + short ioaddr = dev->base_addr; + if (num_addrs) { + outw(69, ioaddr); /* Enable promiscuous mode */ + } else + outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */ +#endif +} + +int arcnet_reset(struct device *dev) +{ + struct arcnet_local *lp=(struct arcnet_local *)dev->priv; + short ioaddr=dev->base_addr; + int delayval,recbuf=lp->recbuf; + + outb(0,INTMASK); /* no IRQ's, please! */ + + BUGLVL(D_INIT) + printk("arcnet: Resetting %s (status=%Xh)\n", + dev->name,inb(STATUS)); + + inb(RESET); /* Reset by reading this port */ + JIFFER(RESETtime); + + outb(CFLAGScmd|RESETclear, COMMAND); /* clear flags & end reset */ + outb(CFLAGScmd|CONFIGclear,COMMAND); + + /* after a reset, the first byte of shared mem is TESTvalue and the + * second byte is our 8-bit ARCnet address + */ + { + u_char *cardmem = (u_char *) dev->mem_start; + if (cardmem[0] != TESTvalue) + { + BUGLVL(D_INIT) + printk("arcnet: reset failed: TESTvalue not present.\n"); + return 1; + } + lp->arcnum=cardmem[1]; /* save address for later use */ + } + + /* clear out status variables */ + recbuf=lp->recbuf=0; + lp->txbuf=2; + /*dev->tbusy=0;*/ + + /* enable extended (512-byte) packets */ + outb(CONFIGcmd|EXTconf,COMMAND); + XJIFFER(ACKtime); + + /* clean out all the memory to make debugging make more sense :) */ + BUGLVL(D_DURING) + memset((void *)dev->mem_start,0x42,2048); + + /* and enable receive of our first packet to the first buffer */ + EnableReceiver(); + + /* re-enable interrupts */ + outb(NORXflag,INTMASK); + + /* done! return success. */ + return 0; +} + + +/* + * Create the ARCnet ClientData header for an arbitrary protocol layer + * + * saddr=NULL means use device source address (always will anyway) + * daddr=NULL means leave destination address (eg unresolved arp) + */ +int arc_header(unsigned char *buff,struct device *dev,unsigned short type, + void *daddr,void *saddr,unsigned len,struct sk_buff *skb) +{ + struct ClientData *head = (struct ClientData *)buff; + struct arcnet_local *lp=(struct arcnet_local *)(dev->priv); + + /* set the protocol ID according to RFC-1201 */ + switch(type) + { + case ETH_P_IP: + head->protocol_id=ARC_P_IP; + break; + case ETH_P_ARP: + head->protocol_id=ARC_P_ARP; + break; + case ETH_P_RARP: + head->protocol_id=ARC_P_RARP; + break; + case ETH_P_IPX: + head->protocol_id=ARC_P_IPX; + break; + case ETH_P_ATALK: + head->protocol_id=ARC_P_ATALK; + break; + default: + printk("arcnet: I don't understand protocol %d (%Xh)\n", + type,type); + return 0; + } + +#if 1 + /* + * Set the source hardware address. + * AVE: we can't do this, so we don't. Code below is directly + * stolen from eth.c driver and won't work. + ** TM: but for debugging I would like to have saddr in the header + */ + if(saddr) + head->saddr=((u_char*)saddr)[0]; + else + head->saddr=((u_char*)(dev->dev_addr))[0]; +#endif + +#if 0 + /* + * Anyway, the loopback-device should never use this function... + * + * And the chances of it using the ARCnet version of it are so + * tiny that I don't think we have to worry :) + */ + if (dev->flags & IFF_LOOPBACK) + { + head->daddr=0; + return(dev->hard_header_len); + } +#endif + + head->split_flag=0; /* split packets are done elsewhere */ + head->sequence=(lp->sequence++); + + /* supposedly if daddr is NULL, we should ignore it... */ + if(daddr) + { + head->daddr=((u_char*)daddr)[0]; + return dev->hard_header_len; + } + else + head->daddr=0; /* better fill one in anyway */ + + return -dev->hard_header_len; +} + + +/* + * Rebuild the ARCnet ClientData header. This is called after an ARP + * (or in future other address resolution) has completed on this + * sk_buff. We now let ARP fill in the other fields. + */ +int arc_rebuild_header(void *buff,struct device *dev,unsigned long dst, + struct sk_buff *skb) +{ + struct ClientData *head = (struct ClientData *)buff; + + /* + * Only ARP/IP is currently supported + */ + + if(head->protocol_id != ARC_P_IP) + { + printk("arcnet: I don't understand resolve type %d (%Xh) addresses!\n", + head->protocol_id,head->protocol_id); + head->daddr=0; + /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/ + return 0; + } + + /* + * Try and get ARP to resolve the header. + */ +#ifdef CONFIG_INET + return arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0; +#else + return 0; +#endif +} + +/* + * Determine the packet's protocol ID. + * + * With ARCnet we have to convert everything to Ethernet-style stuff. + */ +unsigned short arc_type_trans(struct sk_buff *skb,struct device *dev) +{ + struct ClientData *head = (struct ClientData *) skb->data; + + if (head->daddr==0) + skb->pkt_type=PACKET_BROADCAST; + else if(dev->flags&IFF_PROMISC) + { + /* if we're not sending to ourselves :) */ + if (head->daddr != dev->dev_addr[0]) + skb->pkt_type=PACKET_OTHERHOST; + } + + /* now return the protocol number */ + switch (head->protocol_id) + { + case ARC_P_IP: return htons(ETH_P_IP); + case ARC_P_ARP: return htons(ETH_P_ARP); + case ARC_P_RARP: return htons(ETH_P_RARP); + case ARC_P_IPX: return htons(ETH_P_IPX); + case ARC_P_ATALK: return htons(ETH_P_ATALK); /* Doesn't work yet */ + case ARC_P_LANSOFT: /* don't understand. fall through. */ + default: + BUGLVL(D_DURING) + printk("arcnet: received packet of unknown protocol id %d (%Xh)\n", + head->protocol_id,head->protocol_id); + return 0; + } + return htons(ETH_P_IP); +} + +#ifdef MODULE +char kernel_version[] = UTS_RELEASE; +static struct device thisARCnet = { + " ",/* if blank, device name inserted by /linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + 0, 0, /* I/O address, IRQ */ + 0, 0, 0, NULL, arcnet_probe }; + + +int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ +int irqnum=0; /* or use the insmod io= irq= shmem= options */ +int shmem=0; +int num=0; /* number of device (ie for arc0, arc1, arc2...) */ + +int +init_module(void) +{ + sprintf(thisARCnet.name,"arc%d",num); + + thisARCnet.base_addr=io; + + thisARCnet.irq=irqnum; + if (thisARCnet.irq==2) thisARCnet.irq=9; + + if (shmem) + { + thisARCnet.mem_start=shmem; + thisARCnet.mem_end=thisARCnet.mem_start+512*4-1; + thisARCnet.rmem_start=thisARCnet.mem_start+512*0; + thisARCnet.rmem_end=thisARCnet.mem_start+512*2-1; + } + + if (register_netdev(&thisARCnet) != 0) + return -EIO; + return 0; +} + +void +cleanup_module(void) +{ + if (MOD_IN_USE) { + printk("%s: device busy, remove delayed\n",thisARCnet.name); + } else { + if (thisARCnet.start) arcnet_close(&thisARCnet); + if (thisARCnet.irq) free_irq(thisARCnet.irq); + if (thisARCnet.base_addr) release_region(thisARCnet.base_addr, + ETHERCARD_TOTAL_SIZE); + unregister_netdev(&thisARCnet); + } +} + +#endif /* MODULE */ + + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c" + * version-control: t + * kept-new-versions: 5 + * tab-width: 4 + * End: + */ + |