📄 eepro.c
字号:
/* Get the current statistics. This may be called with the card open or closed. */static struct enet_statistics *eepro_get_stats(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; return &lp->stats;}/* Set or clear the multicast filter for this adaptor. */static voidset_multicast_list(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; short ioaddr = dev->base_addr; unsigned short mode; struct dev_mc_list *dmi=dev->mc_list; if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63) { /* * We must make the kernel realise we had to move * into promisc mode or we start all out war on * the cable. If it was a promisc request the * flag is already set. If not we assert it. */ dev->flags|=IFF_PROMISC; outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | PRMSC_Mode, ioaddr + REG2); mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ printk("%s: promiscuous mode enabled.\n", dev->name); } else if (dev->mc_count==0 ) { outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */ mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ } else { unsigned short status, *eaddrs; int i, boguscount = 0; /* Disable RX and TX interrupts. Necessary to avoid corruption of the HOST_ADDRESS_REG by interrupt service routines. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | Multi_IA, ioaddr + REG2); mode = inb(ioaddr + REG3); outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG); outw(MC_SETUP, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(6*(dev->mc_count + 1), ioaddr + IO_PORT); for (i = 0; i < dev->mc_count; i++) { eaddrs=(unsigned short *)dmi->dmi_addr; dmi=dmi->next; outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); } eaddrs = (unsigned short *) dev->dev_addr; outw(eaddrs[0], ioaddr + IO_PORT); outw(eaddrs[1], ioaddr + IO_PORT); outw(eaddrs[2], ioaddr + IO_PORT); outw(lp->tx_end, ioaddr + XMT_BAR); outb(MC_SETUP, ioaddr); /* Update the transmit queue */ i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1); if (lp->tx_start != lp->tx_end) { /* update the next address and the chain bit in the last packet */ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); outw(i, ioaddr + IO_PORT); outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); status = inw(ioaddr + IO_PORT); outw(status | CHAIN_BIT, ioaddr + IO_PORT); lp->tx_end = i ; } else { lp->tx_start = lp->tx_end = i ; } /* Acknowledge that the MC setup is done */ do { /* We should be doing this in the eepro_interrupt()! */ SLOW_DOWN_IO; SLOW_DOWN_IO; if (inb(ioaddr + STATUS_REG) & 0x08) { i = inb(ioaddr); outb(0x08, ioaddr + STATUS_REG); if (i & 0x20) { /* command ABORTed */ printk("%s: multicast setup failed.\n", dev->name); break; } else if ((i & 0x0f) == 0x03) { /* MC-Done */ printk("%s: set Rx mode to %d addresses.\n", dev->name, dev->mc_count); break; } } } while (++boguscount < 100); /* Re-enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); } outb(RCV_ENABLE_CMD, ioaddr);}/* The horrible routine to read a word from the serial EEPROM. *//* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read *//* The delay between EEPROM clock transitions. */#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}#define EE_READ_CMD (6 << 6)intread_eeprom(int ioaddr, int location){ int i; unsigned short retval = 0; short ee_addr = ioaddr + EEPROM_REG; int read_cmd = location | EE_READ_CMD; short ctrl_val = EECS ; outb(BANK2_SELECT, ioaddr); outb(ctrl_val, ee_addr); /* Shift the read command bits out. */ for (i = 8; i >= 0; i--) { short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; outb(outval, ee_addr); outb(outval | EESK, ee_addr); /* EEPROM clock tick. */ eeprom_delay(); outb(outval, ee_addr); /* Finish EEPROM a clock tick. */ eeprom_delay(); } outb(ctrl_val, ee_addr); for (i = 16; i > 0; i--) { outb(ctrl_val | EESK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); outb(ctrl_val, ee_addr); eeprom_delay(); } /* Terminate the EEPROM access. */ ctrl_val &= ~EECS; outb(ctrl_val | EESK, ee_addr); eeprom_delay(); outb(ctrl_val, ee_addr); eeprom_delay(); outb(BANK0_SELECT, ioaddr); return retval;}static voidhardware_send_packet(struct device *dev, void *buf, short length){ struct eepro_local *lp = (struct eepro_local *)dev->priv; short ioaddr = dev->base_addr; int rcv_ram = dev->mem_end; unsigned status, tx_available, last, end, boguscount = 100; if (net_debug > 5) printk("eepro: entering hardware_send_packet routine.\n"); while (boguscount-- > 0) { /* Disable RX and TX interrupts. Necessary to avoid corruption of the HOST_ADDRESS_REG by interrupt service routines. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); if (dev->interrupt == 1) { /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); continue; } /* determine how much of the transmit buffer space is available */ if (lp->tx_end > lp->tx_start) tx_available = XMT_RAM - (lp->tx_end - lp->tx_start); else if (lp->tx_end < lp->tx_start) tx_available = lp->tx_start - lp->tx_end; else tx_available = XMT_RAM; if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) >= tx_available) /* No space available ??? */ { eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */ /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); continue; } last = lp->tx_end; end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */ if ((RAM_SIZE - last) <= XMT_HEADER) { /* Arrrr!!!, must keep the xmt header together, several days were lost to chase this one down. */ last = rcv_ram; end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; } else end = rcv_ram + (end - RAM_SIZE); } outw(last, ioaddr + HOST_ADDRESS_REG); outw(XMT_CMD, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(end, ioaddr + IO_PORT); outw(length, ioaddr + IO_PORT); if (lp->version == LAN595) outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1); else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ unsigned short temp = inb(ioaddr + INT_MASK_REG); outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2); outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); } /* A dummy read to flush the DRAM write pipeline */ status = inw(ioaddr + IO_PORT); if (lp->tx_start == lp->tx_end) { outw(last, ioaddr + XMT_BAR); outb(XMT_CMD, ioaddr); lp->tx_start = last; /* I don't like to change tx_start here */ } else { /* update the next address and the chain bit in the last packet */ if (lp->tx_end != last) { outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); outw(last, ioaddr + IO_PORT); } outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); status = inw(ioaddr + IO_PORT); outw(status | CHAIN_BIT, ioaddr + IO_PORT); /* Continue the transmit command */ outb(RESUME_XMT_CMD, ioaddr); } lp->tx_last = last; lp->tx_end = end; /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); if (dev->tbusy) { dev->tbusy = 0; } if (net_debug > 5) printk("eepro: exiting hardware_send_packet routine.\n"); return; } dev->tbusy = 1; if (net_debug > 5) printk("eepro: exiting hardware_send_packet routine.\n");}static voideepro_rx(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; short ioaddr = dev->base_addr, rcv_ram = dev->mem_end; short boguscount = 20; short rcv_car = lp->rx_start; unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size; if (net_debug > 5) printk("eepro: entering eepro_rx routine.\n"); /* Set the read pointer to the start of the RCV */ outw(rcv_car, ioaddr + HOST_ADDRESS_REG); rcv_event = inw(ioaddr + IO_PORT); while (rcv_event == RCV_DONE) { rcv_status = inw(ioaddr + IO_PORT); rcv_next_frame = inw(ioaddr + IO_PORT); rcv_size = inw(ioaddr + IO_PORT); if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) { /* Malloc up new buffer. */ struct sk_buff *skb; rcv_size &= 0x3fff; skb = dev_alloc_skb(rcv_size+5); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb,2); if (lp->version == LAN595) insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1); else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ unsigned short temp = inb(ioaddr + INT_MASK_REG); outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2); outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); } skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; } else { /* Not sure will ever reach here, I set the 595 to discard bad received frames */ lp->stats.rx_errors++; if (rcv_status & 0x0100) lp->stats.rx_over_errors++; else if (rcv_status & 0x0400) lp->stats.rx_frame_errors++; else if (rcv_status & 0x0800) lp->stats.rx_crc_errors++; printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n", dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size); } if (rcv_status & 0x1000) lp->stats.rx_length_errors++; if (--boguscount == 0) break; rcv_car = lp->rx_start + RCV_HEADER + rcv_size; lp->rx_start = rcv_next_frame; outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG); rcv_event = inw(ioaddr + IO_PORT); } if (rcv_car == 0) rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff; outw(rcv_car - 1, ioaddr + RCV_STOP); if (net_debug > 5) printk("eepro: exiting eepro_rx routine.\n");}static voideepro_transmit_interrupt(struct device *dev){ struct eepro_local *lp = (struct eepro_local *)dev->priv; short ioaddr = dev->base_addr; short boguscount = 20; short xmt_status; while (lp->tx_start != lp->tx_end) { outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG); xmt_status = inw(ioaddr+IO_PORT); if ((xmt_status & TX_DONE_BIT) == 0) break; xmt_status = inw(ioaddr+IO_PORT); lp->tx_start = inw(ioaddr+IO_PORT); dev->tbusy = 0; mark_bh(NET_BH); if (xmt_status & 0x2000) lp->stats.tx_packets++; else { lp->stats.tx_errors++; if (xmt_status & 0x0400) lp->stats.tx_carrier_errors++; printk("%s: XMT status = %#x\n", dev->name, xmt_status); } if (xmt_status & 0x000f) { lp->stats.collisions += (xmt_status & 0x000f); } if ((xmt_status & 0x0040) == 0x0) { lp->stats.tx_heartbeat_errors++; } if (--boguscount == 0) break; }}#ifdef MODULEstatic char devicename[9] = { 0, };static struct device dev_eepro = { devicename, /* device name is inserted by linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eepro_probe };static int io = 0x200;static int irq = 0;static int mem = (RCV_RAM/1024); /* Size of the rx buffer in KB */int init_module(void){ if (io == 0) printk("eepro: You should not use auto-probing with insmod!\n"); dev_eepro.base_addr = io; dev_eepro.irq = irq; dev_eepro.mem_end = mem; if (register_netdev(&dev_eepro) != 0) return -EIO; return 0;}voidcleanup_module(void){ unregister_netdev(&dev_eepro); kfree_s(dev_eepro.priv,sizeof(struct eepro_local)); dev_eepro.priv=NULL; /* If we don't do this, we can't re-insmod it later. */ release_region(dev_eepro.base_addr, EEPRO_IO_EXTENT);}#endif /* MODULE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -