| /* |
| * d_link.c |
| * |
| * Portions (C) Copyright 1993 by Bjorn Ekwall |
| * The Author may be reached as bj0rn@blox.se |
| * |
| * Linux driver for the D-Link Ethernet pocket adapter. |
| * Based on sources from linux 0.99pl5 |
| * and on a sample network driver core for linux. |
| * |
| * Sample driver written 1993 by Donald Becker <becker@super.org> |
| * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 |
| * |
| * compile-command: |
| * "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -c d_link.c" |
| */ |
| /************************************************************* |
| * If you have trouble reading/writing to the adapter, |
| * uncomment the following "#define": |
| #define REALLY_SLOW_IO |
| */ |
| |
| /* $Id: d_link.c,v 0.21 1993/06/02 19:41:00 waltje Exp $ */ |
| /* $Log: d_link.c,v $ |
| * Revision 0.21 1993/06/02 19:41:00 waltje |
| * Applied multi-IRQ fix from Bjorn Ekwall and increaded version |
| * number to 0.21. |
| * |
| * Revision 0.20 1993/03/26 11:43:53 root |
| * Changed version number to indicate "alpha+" (almost beta :-) |
| * |
| * Revision 0.16 1993/03/26 11:26:46 root |
| * Last ALPHA-minus version. |
| * REALLY_SLOW_IO choice included (at line 20) |
| * SLOW_DOWN_IO added anyway in convenience macros/functions |
| * Test of D_LINK_FIFO included (not completely debugged) |
| * |
| * Revision 0.15 1993/03/24 14:00:49 root |
| * Modified the interrupt handling considerably. |
| * (The .asm source had me fooled in how it _really_ works :-) |
| * |
| * Revision 0.14 1993/03/21 01:57:25 root |
| * Modified the interrupthandler for more robustness (hopefully :-) |
| * |
| * Revision 0.13 1993/03/19 11:45:09 root |
| * Re-write of ALPHA release using Don Beckers skeleton (still works, kind of ...:-) |
| * |
| * Revision 0.12 1993/03/16 13:22:21 root |
| * working ALPHA-release |
| * |
| */ |
| |
| static char *version = |
| "d_link.c: $Revision: 0.21 $, Bjorn Ekwall (bj0rn@blox.se)\n"; |
| |
| /* |
| * Based on adapter information gathered from DE600.ASM by D-Link Inc., |
| * as included on disk C in the v.2.11 of PC/TCP from FTP Software. |
| * |
| * For DE600.asm: |
| * Portions (C) Copyright 1990 D-Link, Inc. |
| * Copyright, 1988-1992, Russell Nelson, Crynwr Software |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| * |
| */ |
| |
| #include <linux/config.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/types.h> |
| #include <linux/fcntl.h> |
| #include <linux/string.h> |
| #include <linux/interrupt.h> |
| #include <asm/io.h> |
| #include <netinet/in.h> |
| #include <linux/ptrace.h> |
| #include <asm/system.h> |
| #include <errno.h> |
| |
| #include "inet.h" |
| #include "dev.h" |
| #include "eth.h" |
| #include "timer.h" |
| #include "ip.h" |
| #include "route.h" |
| #include "protocol.h" |
| #include "tcp.h" |
| #include "skbuff.h" |
| #include "sock.h" |
| #include "arp.h" |
| |
| |
| /* use 0 for production, 1 for verification, >2 for debug */ |
| #ifndef D_LINK_DEBUG |
| #define D_LINK_DEBUG 0 |
| #endif |
| static unsigned int d_link_debug = D_LINK_DEBUG; |
| |
| #ifdef D_LINK_DEBUG |
| #define PRINTK(x) if (d_link_debug >= 2) printk x |
| #else |
| #define PRINTK(x) /**/ |
| #endif |
| |
| /************************************************** |
| * * |
| * Definition of D-Link Ethernet Pocket adapter * |
| * * |
| **************************************************/ |
| /* |
| * D-Link Ethernet pocket adapter ports |
| */ |
| #define DATA_PORT (dev->base_addr + 0) |
| #define STATUS_PORT (dev->base_addr + 1) |
| #define COMMAND_PORT (dev->base_addr + 2) |
| |
| /* |
| * D-Link COMMAND_PORT commands |
| */ |
| #define SELECT_NIC 0x04 /* select Network Interface Card */ |
| #define SELECT_PRN 0x1c /* select Printer */ |
| #define NML_PRN 0xec /* normal Printer situation */ |
| #define IRQEN 0x10 /* enable IRQ line */ |
| |
| /* |
| * D-Link STATUS_PORT |
| */ |
| #define TX_INTR 0x88 |
| #define RX_INTR 0x40 |
| |
| #define OTHER_INTR 0x00 /* dummy, always false */ |
| |
| /* |
| * D-Link DATA_PORT commands |
| * command in low 4 bits |
| * data in high 4 bits |
| * select current data nibble with HI_NIBBLE bit |
| */ |
| #define WRITE_DATA 0x00 /* write memory */ |
| #define READ_DATA 0x01 /* read memory */ |
| #define STATUS 0x02 /* read status register */ |
| #define COMMAND 0x03 /* write command register (see COMMAND below) */ |
| #define NULL_COMMAND 0x04 /* null command */ |
| #define RX_LEN 0x05 /* read received packet length */ |
| #define TX_ADDR 0x06 /* set adapter transmit memory address */ |
| #define RW_ADDR 0x07 /* set adapter read/write memory address */ |
| #define HI_NIBBLE 0x08 /* read/write the high nibble of data, |
| or-ed with rest of command */ |
| |
| /* |
| * command register, (I don't know all about these bits...) |
| * accessed through DATA_PORT with low bits = COMMAND |
| */ |
| #define RX_ALL 0x01 /* bit 0,1 = 01 */ |
| #define RX_BP 0x02 /* bit 0,1 = 10 */ |
| #define RX_MBP 0x03 /* bit 0,1 = 11 */ |
| |
| #define TX_ENABLE 0x04 /* bit 2 */ |
| #define RX_ENABLE 0x08 /* bit 3 */ |
| |
| #define RESET 0x80 /* set bit 7 high */ |
| #define STOP_RESET 0x00 /* set bit 7 low */ |
| |
| /* |
| * data to command register |
| * (high 4 bits in write to DATA_PORT) |
| */ |
| #define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */ |
| #define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */ |
| #define FLIP_IRQ 0x40 /* bit 6 */ |
| |
| /* Convenience definition, transmitter page 2 */ |
| #define TX_PAGE2_SELECT 0x02 |
| |
| /* |
| * D-Link adapter internal memory: |
| * |
| * 0-2K 1:st transmit page (send from pointer up to 2K) |
| * 2-4K 2:nd transmit page (send from pointer up to 4K) |
| * |
| * 4-6K 1:st receive page (data from 4K upwards) |
| * 6-8K 2:nd receive page (data from 6K upwards) |
| * |
| * 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address) |
| */ |
| #define MEM_2K 0x0800 /* 2048 */ |
| #define MEM_4K 0x1000 /* 4096 */ |
| #define NODE_ADDRESS 0x2000 /* 8192 */ |
| |
| #define RUNT 64 /*56*/ /* Too small Ethernet packet */ |
| |
| /************************************************** |
| * * |
| * End of definition * |
| * * |
| **************************************************/ |
| |
| /* Common network statistics -- these will be in *.h someday. */ |
| struct netstats { |
| int tx_packets; |
| int rx_packets; |
| int tx_errors; |
| int rx_errors; |
| int missed_packets; |
| int soft_tx_errors; |
| int soft_rx_errors; |
| int soft_trx_err_bits; |
| }; |
| static struct netstats *localstats; |
| |
| /* |
| * Index to functions, as function prototypes. |
| */ |
| |
| /* Routines used internally. (See "convenience macros" also) */ |
| static int d_link_read_status(struct device *dev); |
| static unsigned char d_link_read_byte(int type, struct device *dev); |
| |
| /* Put in the device structure. */ |
| static int d_link_open(struct device *dev); |
| static int d_link_close(struct device *dev); |
| static int d_link_start_xmit(struct sk_buff *skb, struct device *dev); |
| |
| /* Dispatch from interrupts. */ |
| static void d_link_interrupt(int reg_ptr); |
| static void d_link_tx_intr(struct device *dev); |
| static void d_link_rx_intr(struct device *dev); |
| |
| /* Initialization */ |
| int d_link_init(struct device *dev); |
| static void adapter_init(struct device *dev, int startp); |
| |
| /* Passed to sigaction() to register the interrupt handler. */ |
| static struct sigaction d_link_sigaction = { |
| &d_link_interrupt, |
| 0, |
| 0, |
| NULL, |
| }; |
| |
| /* |
| * D-Link driver variables: |
| */ |
| static volatile int rx_page = 0; |
| static struct device *realdev; |
| #ifdef D_LINK_FIFO |
| static volatile int free_tx_page = 0x03; /* 2 pages = 0000 0011 */ |
| static volatile unsigned int busy_tx_page = 0x00; /* 2 pages = 0000 0000 */ |
| static volatile int transmit_next_from; |
| #endif |
| |
| /* |
| * Convenience macros/functions for D-Link adapter |
| * |
| * If you are having trouble reading/writing correctly, |
| * try to uncomment the line "#define REALLY_SLOW_IO" (near line 20) |
| */ |
| |
| #define select_prn() \ |
| outb_p(SELECT_PRN, COMMAND_PORT) |
| |
| #define select_nic() \ |
| outb_p(SELECT_NIC, COMMAND_PORT) |
| |
| #define d_link_put_byte(data) \ |
| outb_p(((data) << 4) | WRITE_DATA , DATA_PORT); \ |
| outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT) |
| |
| /* |
| * The first two outb_p()'s below could perhaps be deleted if there |
| * would be more delay in the last two. Not certain about it yet... |
| */ |
| #define d_link_put_command(cmd) \ |
| outb_p(( rx_page << 4) | COMMAND , DATA_PORT); \ |
| outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT); \ |
| outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT); \ |
| outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT) |
| |
| #define d_link_setup_address(addr,type) \ |
| outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT); \ |
| outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT); \ |
| outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT); \ |
| outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT) |
| |
| static int |
| d_link_read_status(struct device *dev) |
| { |
| int status; |
| |
| select_nic(); |
| outb_p(STATUS, DATA_PORT); |
| SLOW_DOWN_IO; /* See comment line 20 */ |
| status = inb_p(STATUS_PORT); |
| outb_p(NULL_COMMAND, DATA_PORT); |
| outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT); |
| |
| return status; |
| } |
| |
| static unsigned char |
| d_link_read_byte(int type, struct device *dev) { /* dev used by macros */ |
| unsigned char lo; |
| |
| outb_p((type), DATA_PORT); |
| SLOW_DOWN_IO; /* See comment line 20 */ |
| lo = inb_p(STATUS_PORT); |
| outb_p((type) | HI_NIBBLE, DATA_PORT); |
| SLOW_DOWN_IO; /* See comment line 20 */ |
| return ((lo & 0xf0) >> 4) | (inb_p(STATUS_PORT) & 0xf0); |
| } |
| |
| #ifdef D_LINK_FIFO |
| /* Handle a "fifo" in an int (= busy_tx_page) */ |
| # define AT_FIFO_OUTPUT (busy_tx_page & 0x0f) |
| # define ANY_QUEUED_IN_FIFO (busy_tx_page & 0xf0) |
| |
| # define PULL_FROM_FIFO { busy_tx_page >>= 4;} |
| # define PUSH_INTO_FIFO(page) { \ |
| if (busy_tx_page) /* there already is a transmit in progress */ \ |
| busy_tx_page |= (page << 4); \ |
| else \ |
| busy_tx_page = page; \ |
| } |
| #endif |
| |
| /* |
| * Open/initialize the board. This is called (in the current kernel) |
| * sometime after booting when the 'config <dev->name>' 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 a non-reboot way to recover if something goes wrong. |
| */ |
| static int |
| d_link_open(struct device *dev) |
| { |
| adapter_init(dev, 1); |
| dev->tbusy = 0; /* Transmit busy... */ |
| dev->interrupt = 0; |
| dev->start = 1; |
| return 0; |
| } |
| |
| /* |
| * The inverse routine to d_link_open(). |
| */ |
| static int |
| d_link_close(struct device *dev) |
| { |
| dev->start = 0; |
| |
| adapter_init(dev, 0); |
| |
| irqaction(dev->irq, NULL); |
| |
| return 0; |
| } |
| |
| /* |
| * Copy a buffer to the adapter transmit page memory. |
| * Start sending. |
| */ |
| static int |
| d_link_start_xmit(struct sk_buff *skb, struct device *dev) |
| { |
| static int tx_page = 0; |
| int transmit_from; |
| int len; |
| int tickssofar; |
| unsigned char *buffer = (unsigned char *)(skb + 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) { |
| dev_tint(dev); |
| return 0; |
| } |
| |
| /* For ethernet, fill in the header (hardware addresses) with an arp. */ |
| if (!skb->arp && dev->rebuild_header(skb + 1, dev)) { |
| skb->dev = dev; |
| arp_queue (skb); |
| return 0; |
| } |
| |
| #ifdef D_LINK_FIFO |
| if (free_tx_page == 0) { /* Do timeouts, to avoid hangs. */ |
| #else |
| if (dev->tbusy) { /* Do timeouts, to avoid hangs. */ |
| #endif |
| tickssofar = jiffies - dev->trans_start; |
| |
| if (tickssofar < 5) { |
| return 1; |
| } |
| |
| /* else */ |
| printk("%s: transmit timed out (%d), %s?\n", |
| dev->name, |
| tickssofar, |
| "network cable problem" |
| ); |
| /* Try to restart the adapter. */ |
| /* Maybe in next release... :-) |
| adapter_init(dev, 1); |
| */ |
| } |
| |
| /* Start real output */ |
| PRINTK(("d_link_start_xmit:len=%d\n", skb->len)); |
| cli(); |
| select_nic(); |
| |
| #ifdef D_LINK_FIFO |
| /* magic code selects the least significant bit in free_tx_page */ |
| tx_page = free_tx_page & (-free_tx_page); |
| |
| free_tx_page &= ~tx_page; |
| dev->tbusy = !free_tx_page; /* any more free pages? */ |
| |
| PUSH_INTO_FIFO(tx_page); |
| #else |
| tx_page ^= TX_PAGE2_SELECT; /* Flip page, only 2 pages */ |
| #endif |
| |
| if ((len = skb->len) < RUNT) /*&& Hmm...? */ |
| len = RUNT; |
| |
| if (tx_page & TX_PAGE2_SELECT) |
| transmit_from = MEM_4K - len; |
| else |
| transmit_from = MEM_2K - len; |
| |
| d_link_setup_address(transmit_from, RW_ADDR); |
| |
| for ( ; len > 0; --len, ++buffer) { |
| d_link_put_byte(*buffer); /* macro! watch out for side effects! */ |
| } |
| |
| #ifdef D_LINK_FIFO |
| if (ANY_QUEUED_IN_FIFO == 0) { /* there is no transmit in progress */ |
| d_link_setup_address(transmit_from, TX_ADDR); |
| d_link_put_command(TX_ENABLE); |
| dev->trans_start = jiffies; |
| } else { |
| transmit_next_from = transmit_from; |
| } |
| #else |
| d_link_setup_address(transmit_from, TX_ADDR); |
| d_link_put_command(TX_ENABLE); |
| dev->trans_start = jiffies; |
| #endif |
| |
| sti(); /* interrupts back on */ |
| |
| if (skb->free) { |
| kfree_skb (skb, FREE_WRITE); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * The typical workload of the driver: |
| * Handle the network interface interrupts. |
| */ |
| static void |
| d_link_interrupt(int reg_ptr) |
| { |
| int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); |
| struct device *dev = realdev; |
| int interrupts; |
| |
| /* Get corresponding device */ |
| /* |
| for (dev = dev_base; dev != NULL; dev = dev->next) { |
| if (dev->irq == irq) |
| break; |
| } |
| */ |
| if (dev == NULL) { |
| printk ("d_link_interrupt(): irq %d for unknown device.\n", irq); |
| return; |
| } |
| |
| if (dev->start == 0) { |
| return; /*&& bogus interrupt at boot!?!?!? */ |
| } |
| |
| cli(); |
| dev->interrupt = 1; |
| sti(); /* Allow other interrupts. */ |
| |
| localstats = (struct netstats*) dev->private; |
| |
| interrupts = d_link_read_status(dev); |
| |
| PRINTK(("d_link_interrupt (%2.2X)\n", interrupts)); |
| |
| /* |
| * Interrupts have been observed to be: |
| * |
| * Value My interpretation |
| * ----- ----------------- |
| * 0x47 Normal receive interrupt |
| * 0x4F Receive AND transmit interrupt simultaneously |
| * 0x87 Normal transmit interrupt (? I treat it as such...) |
| * 0x8F Normal transmit interrupt |
| */ |
| |
| /* |
| * Take care of TX interrupts first, in case there is an extra |
| * page to transmit (keep the adapter busy while we work). |
| */ |
| if (interrupts & TX_INTR) { /* 1xxx 1xxx */ |
| d_link_tx_intr(dev); |
| } |
| |
| if (interrupts & RX_INTR) { /* x1xx xxxx */ |
| d_link_rx_intr(dev); |
| } |
| |
| /* I'm not sure if there are any other interrupts from D-Link... */ |
| if (d_link_debug && (interrupts & OTHER_INTR)) { |
| printk("%s: unknown interrupt %#2x\n", dev->name, interrupts); |
| } |
| |
| /* Check comment near line 20! */ |
| if ( (interrupts != 0x47) && |
| (interrupts != 0x87) && |
| (interrupts != 0x4F) && |
| (interrupts != 0x8F) |
| ) |
| printk("Strange d_link_interrupt: <%2.2X>\n", interrupts); |
| |
| cli(); |
| dev->interrupt = 0; |
| |
| /* Enable adapter interrupts */ |
| select_prn(); |
| return; |
| } |
| |
| /* |
| * Do internal handshake: Transmitter done (of this page). |
| * Also handle the case of a pending transmit page. |
| */ |
| static void |
| d_link_tx_intr(struct device *dev) |
| { |
| localstats->tx_packets++; |
| |
| cli(); |
| dev->tbusy = 0; |
| |
| #ifdef D_LINK_FIFO |
| free_tx_page |= AT_FIFO_OUTPUT; |
| PULL_FROM_FIFO; |
| |
| if (AT_FIFO_OUTPUT != 0) { /* more in queue! */ |
| d_link_setup_address(transmit_next_from, TX_ADDR); |
| d_link_put_command(TX_ENABLE); |
| dev->trans_start = jiffies; |
| } |
| #endif |
| |
| mark_bh(INET_BH); |
| sti(); |
| } |
| |
| /* |
| * We have a good packet(s), get it/them out of the buffers. |
| */ |
| static void |
| d_link_rx_intr(struct device *dev) |
| { |
| struct sk_buff *skb; |
| int i; |
| int size; |
| int sksize; |
| unsigned char *buffer; |
| |
| /* Get size of received packet */ |
| /* Ignore trailing 4 CRC-bytes */ |
| size = d_link_read_byte(RX_LEN, dev); /* low byte */ |
| size = size + (d_link_read_byte(RX_LEN, dev) << 8) - 4; |
| |
| /* Tell adapter where to store next incoming packet, enable receiver */ |
| rx_page ^= RX_PAGE2_SELECT; /* Flip bit, only 2 pages */ |
| d_link_put_command(RX_ENABLE); |
| |
| if (size == 0) /* Read all the frames? */ |
| return; /* Done for now */ |
| |
| if ((size < 32 || size > 1535) && d_link_debug) |
| printk("%s: Bogus packet size %d.\n", dev->name, size); |
| |
| sksize = sizeof(struct sk_buff) + size; |
| if ((skb = kmalloc(sksize, GFP_ATOMIC)) == NULL) { |
| if (d_link_debug) { |
| printk("%s: Couldn't allocate a sk_buff of size %d.\n", |
| dev->name, sksize); |
| } |
| return; |
| } |
| /* else */ |
| |
| skb->lock = 0; |
| skb->mem_len = sksize; |
| skb->mem_addr = skb; |
| /* 'skb + 1' points to the start of sk_buff data area. */ |
| buffer = (unsigned char *)(skb + 1); |
| |
| /* Get packet */ |
| |
| /* Tell adapter from where we want to read this packet */ |
| if (rx_page & RX_PAGE2_SELECT) { |
| d_link_setup_address(MEM_4K, RW_ADDR); |
| } else { |
| d_link_setup_address(MEM_4K + MEM_2K, RW_ADDR); |
| } |
| |
| /* copy the packet into the buffer */ |
| for (i = size; i > 0; --i) { |
| *buffer++ = d_link_read_byte(READ_DATA, dev); |
| } |
| |
| localstats->rx_packets++; /* count all receives */ |
| |
| if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) { |
| printk("%s: receive buffers full.\n", dev->name); |
| return; |
| } |
| |
| /* |
| * 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. |
| */ |
| return; |
| } |
| |
| int |
| d_link_init(struct device *dev) |
| { |
| int i; |
| |
| printk("%s: D-Link pocket adapter", dev->name); |
| /* Alpha testers must have the version number to report bugs. */ |
| if (d_link_debug > 1) |
| printk(version); |
| |
| /* probe for adapter */ |
| rx_page = 0; |
| (void)d_link_read_status(dev); |
| d_link_put_command(RESET); |
| d_link_put_command(STOP_RESET); |
| if (d_link_read_status(dev) & 0xf0) { |
| printk(": probe failed at %#3x.\n", dev->base_addr); |
| return ENODEV; |
| } |
| |
| /* |
| * Maybe we found one, |
| * have to check if it is a D-Link adapter... |
| */ |
| |
| /* Get the adapter ethernet address from the ROM */ |
| d_link_setup_address(NODE_ADDRESS, RW_ADDR); |
| for (i = 0; i < ETH_ALEN; i++) { |
| dev->dev_addr[i] = d_link_read_byte(READ_DATA, dev); |
| dev->broadcast[i] = 0xff; |
| } |
| |
| /* Check magic code */ |
| if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) { |
| /* OK, install real address */ |
| dev->dev_addr[0] = 0x00; |
| dev->dev_addr[1] = 0x80; |
| dev->dev_addr[2] = 0xc8; |
| dev->dev_addr[3] &= 0x0f; |
| dev->dev_addr[3] |= 0x70; |
| } else { |
| printk(", not found in printer port!\n"); |
| return ENODEV; |
| } |
| |
| /* Initialize the device structure. */ |
| dev->private = kmalloc(sizeof(struct netstats), GFP_KERNEL); |
| memset(dev->private, 0, sizeof(struct netstats)); |
| |
| for (i = 0; i < DEV_NUMBUFFS; i++) |
| dev->buffs[i] = NULL; |
| |
| dev->hard_header = eth_header; |
| dev->add_arp = eth_add_arp; |
| dev->queue_xmit = dev_queue_xmit; |
| dev->rebuild_header = eth_rebuild_header; |
| dev->type_trans = eth_type_trans; |
| |
| dev->open = &d_link_open; |
| dev->stop = &d_link_close; |
| dev->hard_start_xmit = &d_link_start_xmit; |
| |
| /* These are ethernet specific. */ |
| dev->type = ARPHRD_ETHER; |
| dev->hard_header_len = ETH_HLEN; |
| dev->mtu = 1500; /* eth_mtu */ |
| dev->addr_len = ETH_ALEN; |
| |
| /* New-style flags. */ |
| dev->flags = IFF_BROADCAST; |
| dev->family = AF_INET; |
| dev->pa_addr = 0; |
| dev->pa_brdaddr = 0; |
| dev->pa_mask = 0; |
| dev->pa_alen = sizeof(unsigned long); |
| |
| if (irqaction (dev->irq, &d_link_sigaction)) { |
| printk (": unable to get IRQ %d\n", dev->irq); |
| return 0; |
| } |
| |
| printk(", Ethernet Address: %2.2X", dev->dev_addr[0]); |
| for (i = 1; i < ETH_ALEN; i++) |
| printk(":%2.2X",dev->dev_addr[i]); |
| printk("\n"); |
| |
| return 0; |
| } |
| |
| static void |
| adapter_init(struct device *dev, int startp) |
| { |
| int i; |
| |
| cli(); /* no interrupts yet, please */ |
| |
| select_nic(); |
| rx_page = 0; |
| d_link_put_command(RESET); |
| d_link_put_command(STOP_RESET); |
| |
| if (startp) { |
| irqaction (dev->irq, &d_link_sigaction); |
| realdev = dev; |
| #ifdef D_LINK_FIFO |
| free_tx_page = 0x03; /* 2 pages = 0000 0011 */ |
| busy_tx_page = 0x00; /* 2 pages = 0000 0000 */ |
| #endif |
| /* set the ether address. */ |
| d_link_setup_address(NODE_ADDRESS, RW_ADDR); |
| for (i = 0; i < ETH_ALEN; i++) { |
| d_link_put_byte(dev->dev_addr[i]); |
| } |
| |
| /* where to start saving incoming packets */ |
| rx_page = 0 | RX_BP | RX_BASE_PAGE; |
| d_link_setup_address(MEM_4K, RW_ADDR); |
| /* Enable receiver */ |
| d_link_put_command(RX_ENABLE); |
| } |
| else |
| d_link_put_command(0); |
| |
| select_prn(); |
| |
| sti(); |
| } |