|  | /* bionet.c     BioNet-100 device driver for linux68k. | 
|  | * | 
|  | * Version:	@(#)bionet.c	1.0	02/06/96 | 
|  | * | 
|  | * Author:	Hartmut Laue <laue@ifk-mp.uni-kiel.de> | 
|  | * and		Torsten Narjes <narjes@ifk-mp.uni-kiel.de> | 
|  | * | 
|  | * Little adaptions for integration into pl7 by Roman Hodek | 
|  | * | 
|  | * Some changes in bionet_poll_rx by Karl-Heinz Lohner | 
|  | * | 
|  | What is it ? | 
|  | ------------ | 
|  | This driver controls the BIONET-100 LAN-Adapter which connects | 
|  | an ATARI ST/TT via the ACSI-port to an Ethernet-based network. | 
|  |  | 
|  | This version can be compiled as a loadable module (See the | 
|  | compile command at the bottom of this file). | 
|  | At load time, you can optionally set the debugging level and the | 
|  | fastest response time on the command line of 'insmod'. | 
|  |  | 
|  | 'bionet_debug' | 
|  | controls the amount of diagnostic messages: | 
|  | 0  : no messages | 
|  | >0 : see code for meaning of printed messages | 
|  |  | 
|  | 'bionet_min_poll_time' (always >=1) | 
|  | gives the time (in jiffies) between polls. Low values | 
|  | increase the system load (beware!) | 
|  |  | 
|  | When loaded, a net device with the name 'bio0' becomes available, | 
|  | which can be controlled with the usual 'ifconfig' command. | 
|  |  | 
|  | It is possible to compile this driver into the kernel like other | 
|  | (net) drivers. For this purpose, some source files (e.g. config-files | 
|  | makefiles, Space.c) must be changed accordingly. (You may refer to | 
|  | other drivers how to do it.) In this case, the device will be detected | 
|  | at boot time and (probably) appear as 'eth0'. | 
|  |  | 
|  | This code is based on several sources: | 
|  | - The driver code for a parallel port ethernet adapter by | 
|  | Donald Becker (see file 'atp.c' from the PC linux distribution) | 
|  | - The ACSI code by Roman Hodek for the ATARI-ACSI harddisk support | 
|  | and DMA handling. | 
|  | - Very limited information about moving packets in and out of the | 
|  | BIONET-adapter from the TCP package for TOS by BioData GmbH. | 
|  |  | 
|  | Theory of Operation | 
|  | ------------------- | 
|  | Because the ATARI DMA port is usually shared between several | 
|  | devices (eg. harddisk, floppy) we cannot block the ACSI bus | 
|  | while waiting for interrupts. Therefore we use a polling mechanism | 
|  | to fetch packets from the adapter. For the same reason, we send | 
|  | packets without checking that the previous packet has been sent to | 
|  | the LAN. We rely on the higher levels of the networking code to detect | 
|  | missing packets and resend them. | 
|  |  | 
|  | Before we access the ATARI DMA controller, we check if another | 
|  | process is using the DMA. If not, we lock the DMA, perform one or | 
|  | more packet transfers and unlock the DMA before returning. | 
|  | We do not use 'stdma_lock' unconditionally because it is unclear | 
|  | if the networking code can be set to sleep, which will happen if | 
|  | another (possibly slow) device is using the DMA controller. | 
|  |  | 
|  | The polling is done via timer interrupts which periodically | 
|  | 'simulate' an interrupt from the Ethernet adapter. The time (in jiffies) | 
|  | between polls varies depending on an estimate of the net activity. | 
|  | The allowed range is given by the variable 'bionet_min_poll_time' | 
|  | for the lower (fastest) limit and the constant 'MAX_POLL_TIME' | 
|  | for the higher (slowest) limit. | 
|  |  | 
|  | Whenever a packet arrives, we switch to fastest response by setting | 
|  | the polling time to its lowest limit. If the following poll fails, | 
|  | because no packets have arrived, we increase the time for the next | 
|  | poll. When the net activity is low, the polling time effectively | 
|  | stays at its maximum value, resulting in the lowest load for the | 
|  | machine. | 
|  | */ | 
|  |  | 
|  | #define MAX_POLL_TIME	10 | 
|  |  | 
|  | static char version[] = | 
|  | "bionet.c:v1.0 06-feb-96 (c) Hartmut Laue.\n"; | 
|  |  | 
|  | #include <linux/module.h> | 
|  |  | 
|  | #include <linux/errno.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/jiffies.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/fcntl.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/in.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/string.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/timer.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/bitops.h> | 
|  |  | 
|  | #include <linux/netdevice.h> | 
|  | #include <linux/etherdevice.h> | 
|  | #include <linux/skbuff.h> | 
|  |  | 
|  | #include <asm/setup.h> | 
|  | #include <asm/pgtable.h> | 
|  | #include <asm/system.h> | 
|  | #include <asm/io.h> | 
|  | #include <asm/dma.h> | 
|  | #include <asm/atarihw.h> | 
|  | #include <asm/atariints.h> | 
|  | #include <asm/atari_acsi.h> | 
|  | #include <asm/atari_stdma.h> | 
|  |  | 
|  |  | 
|  | /* use 0 for production, 1 for verification, >2 for debug | 
|  | */ | 
|  | #ifndef NET_DEBUG | 
|  | #define NET_DEBUG 0 | 
|  | #endif | 
|  | /* | 
|  | * Global variable 'bionet_debug'. Can be set at load time by 'insmod' | 
|  | */ | 
|  | unsigned int bionet_debug = NET_DEBUG; | 
|  | MODULE_PARM(bionet_debug, "i"); | 
|  | MODULE_PARM_DESC(bionet_debug, "bionet debug level (0-2)"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | static unsigned int bionet_min_poll_time = 2; | 
|  |  | 
|  |  | 
|  | /* Information that need to be kept for each board. | 
|  | */ | 
|  | struct net_local { | 
|  | struct net_device_stats stats; | 
|  | long open_time;			/* for debugging */ | 
|  | int  poll_time;			/* polling time varies with net load */ | 
|  | }; | 
|  |  | 
|  | static struct nic_pkt_s {		/* packet format */ | 
|  | unsigned char	status; | 
|  | unsigned char	dummy; | 
|  | unsigned char	l_lo, l_hi; | 
|  | unsigned char	buffer[3000]; | 
|  | } *nic_packet; | 
|  | unsigned char *phys_nic_packet; | 
|  |  | 
|  | /* Index to functions, as function prototypes. | 
|  | */ | 
|  | static int bionet_open(struct net_device *dev); | 
|  | static int bionet_send_packet(struct sk_buff *skb, struct net_device *dev); | 
|  | static void bionet_poll_rx(struct net_device *); | 
|  | static int bionet_close(struct net_device *dev); | 
|  | static struct net_device_stats *net_get_stats(struct net_device *dev); | 
|  | static void bionet_tick(unsigned long); | 
|  |  | 
|  | static struct timer_list bionet_timer = TIMER_INITIALIZER(bionet_tick, 0, 0); | 
|  |  | 
|  | #define STRAM_ADDR(a)	(((a) & 0xff000000) == 0) | 
|  |  | 
|  | /* The following routines access the ethernet board connected to the | 
|  | * ACSI port via the st_dma chip. | 
|  | */ | 
|  | #define NODE_ADR 0x60 | 
|  |  | 
|  | #define C_READ 8 | 
|  | #define C_WRITE 0x0a | 
|  | #define C_GETEA 0x0f | 
|  | #define C_SETCR 0x0e | 
|  |  | 
|  | static int | 
|  | sendcmd(unsigned int a0, unsigned int mod, unsigned int cmd) { | 
|  | unsigned int c; | 
|  |  | 
|  | dma_wd.dma_mode_status = (mod | ((a0) ? 2 : 0) | 0x88); | 
|  | dma_wd.fdc_acces_seccount = cmd; | 
|  | dma_wd.dma_mode_status = (mod | 0x8a); | 
|  |  | 
|  | if( !acsi_wait_for_IRQ(HZ/2) )	/* wait for cmd ack */ | 
|  | return -1;		/* timeout */ | 
|  |  | 
|  | c = dma_wd.fdc_acces_seccount; | 
|  | return (c & 0xff); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | set_status(int cr) { | 
|  | sendcmd(0,0x100,NODE_ADR | C_SETCR);    /* CMD: SET CR */ | 
|  | sendcmd(1,0x100,cr); | 
|  |  | 
|  | dma_wd.dma_mode_status = 0x80; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get_status(unsigned char *adr) { | 
|  | int i,c; | 
|  |  | 
|  | DISABLE_IRQ(); | 
|  | c = sendcmd(0,0x00,NODE_ADR | C_GETEA);  /* CMD: GET ETH ADR*/ | 
|  | if( c < 0 ) goto gsend; | 
|  |  | 
|  | /* now read status bytes */ | 
|  |  | 
|  | for (i=0; i<6; i++) { | 
|  | dma_wd.fdc_acces_seccount = 0;	/* request next byte */ | 
|  |  | 
|  | if( !acsi_wait_for_IRQ(HZ/2) ) {	/* wait for cmd ack */ | 
|  | c = -1; | 
|  | goto gsend;		/* timeout */ | 
|  | } | 
|  | c = dma_wd.fdc_acces_seccount; | 
|  | *adr++ = (unsigned char)c; | 
|  | } | 
|  | c = 1; | 
|  | gsend: | 
|  | dma_wd.dma_mode_status = 0x80; | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static irqreturn_t | 
|  | bionet_intr(int irq, void *data, struct pt_regs *fp) { | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | get_frame(unsigned long paddr, int odd) { | 
|  | int c; | 
|  | unsigned long flags; | 
|  |  | 
|  | DISABLE_IRQ(); | 
|  | local_irq_save(flags); | 
|  |  | 
|  | dma_wd.dma_mode_status		= 0x9a; | 
|  | dma_wd.dma_mode_status		= 0x19a; | 
|  | dma_wd.dma_mode_status		= 0x9a; | 
|  | dma_wd.fdc_acces_seccount	= 0x04;		/* sector count (was 5) */ | 
|  | dma_wd.dma_lo			= (unsigned char)paddr; | 
|  | paddr >>= 8; | 
|  | dma_wd.dma_md			= (unsigned char)paddr; | 
|  | paddr >>= 8; | 
|  | dma_wd.dma_hi			= (unsigned char)paddr; | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | c = sendcmd(0,0x00,NODE_ADR | C_READ);	/* CMD: READ */ | 
|  | if( c < 128 ) goto rend; | 
|  |  | 
|  | /* now read block */ | 
|  |  | 
|  | c = sendcmd(1,0x00,odd);	/* odd flag for address shift */ | 
|  | dma_wd.dma_mode_status	= 0x0a; | 
|  |  | 
|  | if( !acsi_wait_for_IRQ(100) ) {	/* wait for DMA to complete */ | 
|  | c = -1; | 
|  | goto rend; | 
|  | } | 
|  | dma_wd.dma_mode_status	= 0x8a; | 
|  | dma_wd.dma_mode_status	= 0x18a; | 
|  | dma_wd.dma_mode_status	= 0x8a; | 
|  | c = dma_wd.fdc_acces_seccount; | 
|  |  | 
|  | dma_wd.dma_mode_status	= 0x88; | 
|  | c = dma_wd.fdc_acces_seccount; | 
|  | c = 1; | 
|  |  | 
|  | rend: | 
|  | dma_wd.dma_mode_status	= 0x80; | 
|  | udelay(40); | 
|  | acsi_wait_for_noIRQ(20); | 
|  | return c; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | hardware_send_packet(unsigned long paddr, int cnt) { | 
|  | unsigned int c; | 
|  | unsigned long flags; | 
|  |  | 
|  | DISABLE_IRQ(); | 
|  | local_irq_save(flags); | 
|  |  | 
|  | dma_wd.dma_mode_status	= 0x19a; | 
|  | dma_wd.dma_mode_status	= 0x9a; | 
|  | dma_wd.dma_mode_status	= 0x19a; | 
|  | dma_wd.dma_lo		= (unsigned char)paddr; | 
|  | paddr >>= 8; | 
|  | dma_wd.dma_md		= (unsigned char)paddr; | 
|  | paddr >>= 8; | 
|  | dma_wd.dma_hi		= (unsigned char)paddr; | 
|  |  | 
|  | dma_wd.fdc_acces_seccount	= 0x4;		/* sector count */ | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | c = sendcmd(0,0x100,NODE_ADR | C_WRITE);	/* CMD: WRITE */ | 
|  | c = sendcmd(1,0x100,cnt&0xff); | 
|  | c = sendcmd(1,0x100,cnt>>8); | 
|  |  | 
|  | /* now write block */ | 
|  |  | 
|  | dma_wd.dma_mode_status	= 0x10a;	/* DMA enable */ | 
|  | if( !acsi_wait_for_IRQ(100) )		/* wait for DMA to complete */ | 
|  | goto end; | 
|  |  | 
|  | dma_wd.dma_mode_status	= 0x19a;	/* DMA disable ! */ | 
|  | c = dma_wd.fdc_acces_seccount; | 
|  |  | 
|  | end: | 
|  | c = sendcmd(1,0x100,0); | 
|  | c = sendcmd(1,0x100,0); | 
|  |  | 
|  | dma_wd.dma_mode_status	= 0x180; | 
|  | udelay(40); | 
|  | acsi_wait_for_noIRQ(20); | 
|  | return( c & 0x02); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Check for a network adaptor of this type, and return '0' if one exists. | 
|  | */ | 
|  | struct net_device * __init bionet_probe(int unit) | 
|  | { | 
|  | struct net_device *dev; | 
|  | unsigned char station_addr[6]; | 
|  | static unsigned version_printed; | 
|  | static int no_more_found;	/* avoid "Probing for..." printed 4 times */ | 
|  | int i; | 
|  | int err; | 
|  |  | 
|  | if (!MACH_IS_ATARI || no_more_found) | 
|  | return ERR_PTR(-ENODEV); | 
|  |  | 
|  | dev = alloc_etherdev(sizeof(struct net_local)); | 
|  | if (!dev) | 
|  | return ERR_PTR(-ENOMEM); | 
|  | if (unit >= 0) { | 
|  | sprintf(dev->name, "eth%d", unit); | 
|  | netdev_boot_setup_check(dev); | 
|  | } | 
|  | SET_MODULE_OWNER(dev); | 
|  |  | 
|  | printk("Probing for BioNet 100 Adapter...\n"); | 
|  |  | 
|  | stdma_lock(bionet_intr, NULL); | 
|  | i = get_status(station_addr);	/* Read the station address PROM.  */ | 
|  | ENABLE_IRQ(); | 
|  | stdma_release(); | 
|  |  | 
|  | /* Check the first three octets of the S.A. for the manufactor's code. | 
|  | */ | 
|  |  | 
|  | if( i < 0 | 
|  | ||  station_addr[0] != 'B' | 
|  | ||  station_addr[1] != 'I' | 
|  | ||  station_addr[2] != 'O' ) { | 
|  | no_more_found = 1; | 
|  | printk( "No BioNet 100 found.\n" ); | 
|  | free_netdev(dev); | 
|  | return ERR_PTR(-ENODEV); | 
|  | } | 
|  |  | 
|  | if (bionet_debug > 0 && version_printed++ == 0) | 
|  | printk(version); | 
|  |  | 
|  | printk("%s: %s found, eth-addr: %02x-%02x-%02x:%02x-%02x-%02x.\n", | 
|  | dev->name, "BioNet 100", | 
|  | station_addr[0], station_addr[1], station_addr[2], | 
|  | station_addr[3], station_addr[4], station_addr[5]); | 
|  |  | 
|  | /* Initialize the device structure. */ | 
|  |  | 
|  | nic_packet = (struct nic_pkt_s *)acsi_buffer; | 
|  | phys_nic_packet = (unsigned char *)phys_acsi_buffer; | 
|  | if (bionet_debug > 0) { | 
|  | printk("nic_packet at 0x%p, phys at 0x%p\n", | 
|  | nic_packet, phys_nic_packet ); | 
|  | } | 
|  |  | 
|  | dev->open		= bionet_open; | 
|  | dev->stop		= bionet_close; | 
|  | dev->hard_start_xmit	= bionet_send_packet; | 
|  | dev->get_stats		= net_get_stats; | 
|  |  | 
|  | /* Fill in the fields of the device structure with ethernet-generic | 
|  | * values. This should be in a common file instead of per-driver. | 
|  | */ | 
|  |  | 
|  | for (i = 0; i < ETH_ALEN; i++) { | 
|  | #if 0 | 
|  | dev->broadcast[i] = 0xff; | 
|  | #endif | 
|  | dev->dev_addr[i]  = station_addr[i]; | 
|  | } | 
|  | err = register_netdev(dev); | 
|  | if (!err) | 
|  | return dev; | 
|  | free_netdev(dev); | 
|  | return ERR_PTR(err); | 
|  | } | 
|  |  | 
|  | /* 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 | 
|  | bionet_open(struct net_device *dev) { | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  |  | 
|  | if (bionet_debug > 0) | 
|  | printk("bionet_open\n"); | 
|  | stdma_lock(bionet_intr, NULL); | 
|  |  | 
|  | /* Reset the hardware here. | 
|  | */ | 
|  | set_status(4); | 
|  | lp->open_time = 0;	/*jiffies*/ | 
|  | lp->poll_time = MAX_POLL_TIME; | 
|  |  | 
|  | dev->tbusy = 0; | 
|  | dev->interrupt = 0; | 
|  | dev->start = 1; | 
|  |  | 
|  | stdma_release(); | 
|  | bionet_timer.data = (long)dev; | 
|  | bionet_timer.expires = jiffies + lp->poll_time; | 
|  | add_timer(&bionet_timer); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | bionet_send_packet(struct sk_buff *skb, struct net_device *dev) { | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  | unsigned long flags; | 
|  |  | 
|  | /* Block a timer-based transmit from overlapping.  This could better be | 
|  | * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. | 
|  | */ | 
|  | local_irq_save(flags); | 
|  |  | 
|  | if (stdma_islocked()) { | 
|  | local_irq_restore(flags); | 
|  | lp->stats.tx_errors++; | 
|  | } | 
|  | else { | 
|  | int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | 
|  | unsigned long buf = virt_to_phys(skb->data); | 
|  | int stat; | 
|  |  | 
|  | stdma_lock(bionet_intr, NULL); | 
|  | local_irq_restore(flags); | 
|  | if( !STRAM_ADDR(buf+length-1) ) { | 
|  | memcpy(nic_packet->buffer, skb->data, length); | 
|  | buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer; | 
|  | } | 
|  |  | 
|  | if (bionet_debug >1) { | 
|  | u_char *data = nic_packet->buffer, *p; | 
|  | int i; | 
|  |  | 
|  | printk( "%s: TX pkt type 0x%4x from ", dev->name, | 
|  | ((u_short *)data)[6]); | 
|  |  | 
|  | for( p = &data[6], i = 0; i < 6; i++ ) | 
|  | printk("%02x%s", *p++,i != 5 ? ":" : "" ); | 
|  | printk(" to "); | 
|  |  | 
|  | for( p = data, i = 0; i < 6; i++ ) | 
|  | printk("%02x%s", *p++,i != 5 ? ":" : "" "\n" ); | 
|  |  | 
|  | printk( "%s: ", dev->name ); | 
|  | printk(" data %02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x" | 
|  | " %02x%02x%02x%02x len %d\n", | 
|  | data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19], | 
|  | data[20], data[21], data[22], data[23], data[24], data[25], data[26], data[27], | 
|  | data[28], data[29], data[30], data[31], data[32], data[33], | 
|  | length ); | 
|  | } | 
|  | dma_cache_maintenance(buf, length, 1); | 
|  |  | 
|  | stat = hardware_send_packet(buf, length); | 
|  | ENABLE_IRQ(); | 
|  | stdma_release(); | 
|  |  | 
|  | dev->trans_start = jiffies; | 
|  | dev->tbusy	 = 0; | 
|  | lp->stats.tx_packets++; | 
|  | lp->stats.tx_bytes+=length; | 
|  | } | 
|  | dev_kfree_skb(skb); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* We have a good packet(s), get it/them out of the buffers. | 
|  | */ | 
|  | static void | 
|  | bionet_poll_rx(struct net_device *dev) { | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  | int boguscount = 10; | 
|  | int pkt_len, status; | 
|  | unsigned long flags; | 
|  |  | 
|  | local_irq_save(flags); | 
|  | /* ++roman: Take care at locking the ST-DMA... This must be done with ints | 
|  | * off, since otherwise an int could slip in between the question and the | 
|  | * locking itself, and then we'd go to sleep... And locking itself is | 
|  | * necessary to keep the floppy_change timer from working with ST-DMA | 
|  | * registers. */ | 
|  | if (stdma_islocked()) { | 
|  | local_irq_restore(flags); | 
|  | return; | 
|  | } | 
|  | stdma_lock(bionet_intr, NULL); | 
|  | DISABLE_IRQ(); | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | if( lp->poll_time < MAX_POLL_TIME ) lp->poll_time++; | 
|  |  | 
|  | while(boguscount--) { | 
|  | status = get_frame((unsigned long)phys_nic_packet, 0); | 
|  |  | 
|  | if( status == 0 ) break; | 
|  |  | 
|  | /* Good packet... */ | 
|  |  | 
|  | dma_cache_maintenance((unsigned long)phys_nic_packet, 1520, 0); | 
|  |  | 
|  | pkt_len = (nic_packet->l_hi << 8) | nic_packet->l_lo; | 
|  |  | 
|  | lp->poll_time = bionet_min_poll_time;    /* fast poll */ | 
|  | if( pkt_len >= 60 && pkt_len <= 1520 ) { | 
|  | /*	^^^^ war 1514  KHL */ | 
|  | /* Malloc up new buffer. | 
|  | */ | 
|  | struct sk_buff *skb = dev_alloc_skb( pkt_len + 2 ); | 
|  | if (skb == NULL) { | 
|  | printk("%s: Memory squeeze, dropping packet.\n", | 
|  | dev->name); | 
|  | lp->stats.rx_dropped++; | 
|  | break; | 
|  | } | 
|  |  | 
|  | skb->dev = dev; | 
|  | skb_reserve( skb, 2 );		/* 16 Byte align  */ | 
|  | skb_put( skb, pkt_len );	/* make room */ | 
|  |  | 
|  | /* 'skb->data' points to the start of sk_buff data area. | 
|  | */ | 
|  | memcpy(skb->data, nic_packet->buffer, pkt_len); | 
|  | skb->protocol = eth_type_trans( skb, dev ); | 
|  | netif_rx(skb); | 
|  | dev->last_rx = jiffies; | 
|  | lp->stats.rx_packets++; | 
|  | lp->stats.rx_bytes+=pkt_len; | 
|  |  | 
|  | /* If any worth-while packets have been received, dev_rint() | 
|  | has done a mark_bh(INET_BH) for us and will work on them | 
|  | when we get to the bottom-half routine. | 
|  | */ | 
|  |  | 
|  | if (bionet_debug >1) { | 
|  | u_char *data = nic_packet->buffer, *p; | 
|  | int i; | 
|  |  | 
|  | printk( "%s: RX pkt type 0x%4x from ", dev->name, | 
|  | ((u_short *)data)[6]); | 
|  |  | 
|  |  | 
|  | for( p = &data[6], i = 0; i < 6; i++ ) | 
|  | printk("%02x%s", *p++,i != 5 ? ":" : "" ); | 
|  | printk(" to "); | 
|  | for( p = data, i = 0; i < 6; i++ ) | 
|  | printk("%02x%s", *p++,i != 5 ? ":" : "" "\n" ); | 
|  |  | 
|  | printk( "%s: ", dev->name ); | 
|  | printk(" data %02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x" | 
|  | " %02x%02x%02x%02x len %d\n", | 
|  | data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19], | 
|  | data[20], data[21], data[22], data[23], data[24], data[25], data[26], data[27], | 
|  | data[28], data[29], data[30], data[31], data[32], data[33], | 
|  | pkt_len ); | 
|  | } | 
|  | } | 
|  | else { | 
|  | printk(" Packet has wrong length: %04d bytes\n", pkt_len); | 
|  | lp->stats.rx_errors++; | 
|  | } | 
|  | } | 
|  | stdma_release(); | 
|  | ENABLE_IRQ(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* bionet_tick: called by bionet_timer. Reads packets from the adapter, | 
|  | * passes them to the higher layers and restarts the timer. | 
|  | */ | 
|  | static void | 
|  | bionet_tick(unsigned long data) { | 
|  | struct net_device	 *dev = (struct net_device *)data; | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  |  | 
|  | if( bionet_debug > 0 && (lp->open_time++ & 7) == 8 ) | 
|  | printk("bionet_tick: %ld\n", lp->open_time); | 
|  |  | 
|  | if( !stdma_islocked() ) bionet_poll_rx(dev); | 
|  |  | 
|  | bionet_timer.expires = jiffies + lp->poll_time; | 
|  | add_timer(&bionet_timer); | 
|  | } | 
|  |  | 
|  | /* The inverse routine to bionet_open(). | 
|  | */ | 
|  | static int | 
|  | bionet_close(struct net_device *dev) { | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  |  | 
|  | if (bionet_debug > 0) | 
|  | printk("bionet_close, open_time=%ld\n", lp->open_time); | 
|  | del_timer(&bionet_timer); | 
|  | stdma_lock(bionet_intr, NULL); | 
|  |  | 
|  | set_status(0); | 
|  | lp->open_time = 0; | 
|  |  | 
|  | dev->tbusy = 1; | 
|  | dev->start = 0; | 
|  |  | 
|  | stdma_release(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Get the current statistics. | 
|  | This may be called with the card open or closed. | 
|  | */ | 
|  | static struct net_device_stats *net_get_stats(struct net_device *dev) | 
|  | { | 
|  | struct net_local *lp = netdev_priv(dev); | 
|  | return &lp->stats; | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef MODULE | 
|  |  | 
|  | static struct net_device *bio_dev; | 
|  |  | 
|  | int init_module(void) | 
|  | { | 
|  | bio_dev = bionet_probe(-1); | 
|  | if (IS_ERR(bio_dev)) | 
|  | return PTR_ERR(bio_dev); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void cleanup_module(void) | 
|  | { | 
|  | unregister_netdev(bio_dev); | 
|  | free_netdev(bio_dev); | 
|  | } | 
|  |  | 
|  | #endif /* MODULE */ | 
|  |  | 
|  | /* Local variables: | 
|  | *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include | 
|  | -b m68k-linuxaout -Wall -Wstrict-prototypes -O2 | 
|  | -fomit-frame-pointer -pipe -DMODULE -I../../net/inet -c bionet.c" | 
|  | *  version-control: t | 
|  | *  kept-new-versions: 5 | 
|  | *  tab-width: 8 | 
|  | * End: | 
|  | */ |