📄 eth16i.c
字号:
inb(ioaddr + 1), inb(ioaddr + 2), inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5), inb(ioaddr + 6), inb(ioaddr + 7)); printk(KERN_DEBUG "%s: transmit start reg: %02x. collision reg %02x\n", dev->name, inb(ioaddr + TRANSMIT_START_REG), inb(ioaddr + COL_16_REG)); printk(KERN_DEBUG "lp->tx_queue = %d\n", lp->tx_queue); printk(KERN_DEBUG "lp->tx_queue_len = %d\n", lp->tx_queue_len); printk(KERN_DEBUG "lp->tx_started = %d\n", lp->tx_started); } lp->stats.tx_errors++; eth16i_reset(dev); dev->trans_start = jiffies; outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); } /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() itself */ if(skb == NULL) {#if LINUX_VERSION_CODE < 0x020100 dev_tint(dev);#endif if(eth16i_debug > 0) printk(KERN_WARNING "%s: Missed tx-done interrupt.\n", dev->name); return 0; } /* Block a timer based transmitter from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ set_bit(0, (void *)&lp->tx_buf_busy); /* Turn off TX interrupts */ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); if(test_and_set_bit(0, (void *)&dev->tbusy) != 0) { printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); status = -1; } else { ushort length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; if( (length + 2) > (lp->tx_buf_size - lp->tx_queue_len)) { if(eth16i_debug > 0) printk(KERN_WARNING "%s: Transmit buffer full.\n", dev->name); } else { outw(length, ioaddr + DATAPORT); if( ioaddr < 0x1000 ) outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); else { unsigned char frag = length % 4; outsl(ioaddr + DATAPORT, buf, length >> 2); if( frag != 0 ) { outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1); if( frag == 3 ) outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC) + 2), 1); } } lp->tx_buffered_packets++; lp->tx_queue++; lp->tx_queue_len += length + 2; } lp->tx_buf_busy = 0; if(lp->tx_started == 0) { /* If the transmitter is idle..always trigger a transmit */ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; lp->tx_started = 1; dev->tbusy = 0; } else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { /* There is still more room for one more packet in tx buffer */ dev->tbusy = 0; } outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); /* Turn TX interrupts back on */ /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */ status = 0; } #if LINUX_VERSION_CODE >= 0x020100 dev_kfree_skb(skb);#else dev_kfree_skb(skb, FREE_WRITE);#endif return status;}static void eth16i_rx(struct device *dev){ struct eth16i_local *lp = (struct eth16i_local *)dev->priv; int ioaddr = dev->base_addr; int boguscount = MAX_RX_LOOP; /* Loop until all packets have been read */ while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) { /* Read status byte from receive buffer */ ushort status = inw(ioaddr + DATAPORT); /* Get the size of the packet from receive buffer */ ushort pkt_len = inw(ioaddr + DATAPORT); if(eth16i_debug > 4) printk(KERN_DEBUG "%s: Receiving packet mode %02x status %04x.\n", dev->name, inb(ioaddr + RECEIVE_MODE_REG), status); if( !(status & PKT_GOOD) ) { lp->stats.rx_errors++; if( (pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN) ) { lp->stats.rx_length_errors++; eth16i_reset(dev); return; } else { eth16i_skip_packet(dev); lp->stats.rx_dropped++; } } else { /* Ok so now we should have a good packet */ struct sk_buff *skb; skb = dev_alloc_skb(pkt_len + 3); if( skb == NULL ) { printk(KERN_WARNING "%s: Could'n allocate memory for packet (len %d)\n", dev->name, pkt_len); eth16i_skip_packet(dev); lp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb,2); /* Now let's get the packet out of buffer. size is (pkt_len + 1) >> 1, cause we are now reading words and it have to be even aligned. */ if(ioaddr < 0x1000) insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), (pkt_len + 1) >> 1); else { unsigned char *buf = skb_put(skb, pkt_len); unsigned char frag = pkt_len % 4; insl(ioaddr + DATAPORT, buf, pkt_len >> 2); if(frag != 0) { unsigned short rest[2]; rest[0] = inw( ioaddr + DATAPORT ); if(frag == 3) rest[1] = inw( ioaddr + DATAPORT ); memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag); } } skb->protocol=eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; if( eth16i_debug > 5 ) { int i; printk(KERN_DEBUG "%s: Received packet of length %d.\n", dev->name, pkt_len); for(i = 0; i < 14; i++) printk(KERN_DEBUG " %02x", skb->data[i]); printk(KERN_DEBUG ".\n"); } } /* else */ if(--boguscount <= 0) break; } /* while */#if 0 { int i; for(i = 0; i < 20; i++) { if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == RX_BUFFER_EMPTY) break; inw(ioaddr + DATAPORT); outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); } if(eth16i_debug > 1) printk(KERN_DEBUG "%s: Flushed receive buffer.\n", dev->name); }#endif return;}static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct device *dev = dev_id; struct eth16i_local *lp; int ioaddr = 0, status; if(dev == NULL) { printk(KERN_WARNING "eth16i_interrupt(): irq %d for unknown device. \n", irq); return; } /* Turn off all interrupts from adapter */ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); set_bit(0, (void *)&dev->tbusy); /* Set the device busy so that */ /* eth16i_tx wont be called */ if(dev->interrupt) printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name); dev->interrupt = 1; ioaddr = dev->base_addr; lp = (struct eth16i_local *)dev->priv; status = inw(ioaddr + TX_STATUS_REG); /* Get the status */ outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */ if(eth16i_debug > 3) printk(KERN_DEBUG "%s: Interrupt with status %04x.\n", dev->name, status); if( status & 0x7f00 ) { lp->stats.rx_errors++; if(status & (BUS_RD_ERR << 8) ) printk(KERN_WARNING "%s: Bus read error.\n",dev->name); if(status & (SHORT_PKT_ERR << 8) ) lp->stats.rx_length_errors++; if(status & (ALIGN_ERR << 8) ) lp->stats.rx_frame_errors++; if(status & (CRC_ERR << 8) ) lp->stats.rx_crc_errors++; if(status & (RX_BUF_OVERFLOW << 8) ) lp->stats.rx_over_errors++; } if( status & 0x001a) { lp->stats.tx_errors++; if(status & CR_LOST) lp->stats.tx_carrier_errors++; if(status & TX_JABBER_ERR) lp->stats.tx_window_errors++;#if 0 if(status & COLLISION) { lp->stats.collisions += ((inb(ioaddr+TRANSMIT_MODE_REG) & 0xF0) >> 4); }#endif if(status & COLLISIONS_16) { if(lp->col_16 < MAX_COL_16) { lp->col_16++; lp->stats.collisions++; /* Resume transmitting, skip failed packet */ outb(0x02, ioaddr + COL_16_REG); } else { printk(KERN_WARNING "%s: bailing out due to many consecutive 16-in-a-row collisions. Network cable problem?\n", dev->name); } } } if( status & 0x00ff ) { /* Let's check the transmit status reg */ if(status & TX_DONE) { /* The transmit has been done */ lp->stats.tx_packets = lp->tx_buffered_packets; lp->col_16 = 0; if(lp->tx_queue) { /* Is there still packets ? */ /* There was packet(s) so start transmitting and write also how many packets there is to be sended */ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); lp->tx_queue = 0; lp->tx_queue_len = 0; lp->tx_started = 1; dev->trans_start = jiffies; mark_bh(NET_BH); } else { lp->tx_started = 0; mark_bh(NET_BH); } } } if( ( status & 0x8000 ) || ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) { eth16i_rx(dev); /* We have packet in receive buffer */ } dev->interrupt = 0; /* Turn interrupts back on */ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { /* There is still more room for one more packet in tx buffer */ dev->tbusy = 0; } return;}static void eth16i_skip_packet(struct device *dev){ int ioaddr = dev->base_addr; inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); while( inb( ioaddr + FILTER_SELF_RX_REG ) != 0);}static void eth16i_reset(struct device *dev){ struct eth16i_local *lp = (struct eth16i_local *)dev->priv; int ioaddr = dev->base_addr; if(eth16i_debug > 1) printk(KERN_DEBUG "%s: Resetting device.\n", dev->name); BITSET(ioaddr + CONFIG_REG_0, DLC_EN); outw(0xffff, ioaddr + TX_STATUS_REG); eth16i_select_regbank(2, ioaddr); lp->tx_started = 0; lp->tx_buf_busy = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; dev->interrupt = 0; dev->start = 1; dev->tbusy = 0; BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);}static void eth16i_multicast(struct device *dev){ int ioaddr = dev->base_addr; if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) { dev->flags|=IFF_PROMISC; /* Must do this */ outb(3, ioaddr + RECEIVE_MODE_REG); } else { outb(2, ioaddr + RECEIVE_MODE_REG); }}static struct enet_statistics *eth16i_get_stats(struct device *dev){ struct eth16i_local *lp = (struct eth16i_local *)dev->priv; return &lp->stats;}static void eth16i_select_regbank(unsigned char banknbr, int ioaddr){ unsigned char data; data = inb(ioaddr + CONFIG_REG_1); outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); }#ifdef MODULEstatic ushort eth16i_parse_mediatype(const char* s){ if(!s) return E_PORT_FROM_EPROM; if (!strncmp(s, "bnc", 3)) return E_PORT_BNC; else if (!strncmp(s, "tp", 2)) return E_PORT_TP; else if (!strncmp(s, "dix", 3)) return E_PORT_DIX; else if (!strncmp(s, "auto", 4)) return E_PORT_AUTO; else return E_PORT_FROM_EPROM;}#define MAX_ETH16I_CARDS 4 /* Max number of Eth16i cards per module */#define NAMELEN 8 /* number of chars for storing dev->name */static char namelist[NAMELEN * MAX_ETH16I_CARDS] = { 0, };static struct device dev_eth16i[MAX_ETH16I_CARDS] = { { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL },};static int ioaddr[MAX_ETH16I_CARDS] = { 0, };#if 0static int irq[MAX_ETH16I_CARDS] = { 0, };#endifstatic char* mediatype[MAX_ETH16I_CARDS] = { 0, };static int debug = -1;#if (LINUX_VERSION_CODE >= 0x20115) MODULE_AUTHOR("Mika Kuoppala <miku@iki.fi>");MODULE_DESCRIPTION("ICL EtherTeam 16i/32 driver");MODULE_PARM(ioaddr, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i");MODULE_PARM_DESC(ioaddr, "eth16i io base address");#if 0MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i");MODULE_PARM_DESC(irq, "eth16i interrupt request number");#endifMODULE_PARM(mediatype, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "s");MODULE_PARM_DESC(mediatype, "eth16i interfaceport mediatype");MODULE_PARM(debug, "i");MODULE_PARM_DESC(debug, "eth16i debug level (0-4)");#endifint init_module(void){ int this_dev, found = 0; for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) { struct device *dev = &dev_eth16i[this_dev]; dev->name = namelist + (NAMELEN*this_dev); dev->irq = 0; /* irq[this_dev]; */ dev->base_addr = ioaddr[this_dev]; dev->init = eth16i_probe; if(debug != -1) eth16i_debug = debug; if(eth16i_debug > 1) printk(KERN_NOTICE "eth16i(%d): interface type %s\n", this_dev, mediatype[this_dev] ? mediatype[this_dev] : "none" ); dev->if_port = eth16i_parse_mediatype(mediatype[this_dev]); if(ioaddr[this_dev] == 0) { if(this_dev != 0) break; /* Only autoprobe 1st one */ printk(KERN_NOTICE "eth16i.c: Presently autoprobing (not recommended) for a single card.\n"); } if(register_netdev(dev) != 0) { printk(KERN_WARNING "eth16i.c No Eth16i card found (i/o = 0x%x).\n", ioaddr[this_dev]); if(found != 0) return 0; return -ENXIO; } found++; } return 0;} void cleanup_module(void){ int this_dev; for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) { struct device* dev = &dev_eth16i[this_dev]; if(dev->priv != NULL) { unregister_netdev(dev); kfree(dev->priv); dev->priv = NULL; free_irq(dev->irq, dev); release_region(dev->base_addr, ETH16I_IO_EXTENT); } }}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eth16i.c" * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict -prototypes -O6 -c eth16i.c" * tab-width: 8 * c-basic-offset: 8 * c-indent-level: 8 * End: *//* End of file eth16i.c */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -