📄 fmv18x.c
字号:
dev->name, htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)), htons(inw(ioaddr +10)), htons(inw(ioaddr +12)), htons(inw(ioaddr +14))); printk("eth card: %04x %04x\n", htons(inw(ioaddr+FJ_STATUS0)), htons(inw(ioaddr+FJ_CONFIG0))); lp->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ cli(); /* Initialize LAN Controller and LAN Card */ outb(0xda, ioaddr + CONFIG_0); /* Initialize LAN Controller */ outb(0x00, ioaddr + CONFIG_1); /* Stand by mode */ outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */ outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure */ net_open(dev); sti(); } /* 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 (test_and_set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; if (length > ETH_FRAME_LEN) { if (net_debug) printk("%s: Attempting to send a large packet (%d bytes).\n", dev->name, length); return 1; } if (net_debug > 4) printk("%s: Transmitting a packet of length %lu.\n", dev->name, (unsigned long)skb->len); /* We may not start transmitting unless we finish transferring a packet into the Tx queue. During executing the following codes we possibly catch a Tx interrupt. Thus we flag off tx_queue_ready, so that we prevent the interrupt routine (net_interrupt) to start transmitting. */ lp->tx_queue_ready = 0; { outw(length, ioaddr + DATAPORT); outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); lp->tx_queue++; lp->tx_queue_len += length + 2; } lp->tx_queue_ready = 1; if (lp->tx_started == 0) { /* If the Tx is idle, always trigger a transmit. */ outb(0x80 | lp->tx_queue, ioaddr + TX_START); 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 < 4096 - 1502) /* Yes, there is room for one more packet. */ dev->tbusy = 0; } dev_kfree_skb (skb); return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static voidnet_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct device *dev = dev_id; struct net_local *lp; int ioaddr, status; if (dev == NULL) { printk ("fmv18x_interrupt(): irq %d for unknown device.\n", irq); return; } dev->interrupt = 1; ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; status = inw(ioaddr + TX_STATUS); outw(status, ioaddr + TX_STATUS); if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); if (lp->rx_started == 0 && (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { /* Got a packet(s). We cannot execute net_rx more than once at the same time for the same device. During executing net_rx, we possibly catch a Tx interrupt. Thus we flag on rx_started, so that we prevent the interrupt routine (net_interrupt) to dive into net_rx again. */ lp->rx_started = 1; outb(0x00, ioaddr + RX_INTR); /* Disable RX intr. */ net_rx(dev); outb(0x81, ioaddr + RX_INTR); /* Enable RX intr. */ lp->rx_started = 0; } if (status & 0x00ff) { if (status & 0x02) { /* More than 16 collisions occurred */ if (net_debug > 4) printk("%s: 16 Collision occur during Txing.\n", dev->name); /* Cancel sending a packet. */ outb(0x03, ioaddr + COL16CNTL); lp->stats.collisions++; } if (status & 0x82) { lp->stats.tx_packets++; if (lp->tx_queue && lp->tx_queue_ready) { outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; dev->trans_start = jiffies; dev->tbusy = 0; mark_bh(NET_BH); /* Inform upper layers. */ } else { lp->tx_started = 0; dev->tbusy = 0; mark_bh(NET_BH); /* Inform upper layers. */ } } } dev->interrupt = 0; return;}/* We have a good packet(s), get it/them out of the buffers. */static voidnet_rx(struct device *dev){ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; int boguscount = 5; while ((inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Clear PKT_RDY bit: by agy 19940922 */ /* outb(0x80, ioaddr + RX_STATUS); */ ushort status = inw(ioaddr + DATAPORT); if (net_debug > 4) printk("%s: Rxing packet mode %02x status %04x.\n", dev->name, inb(ioaddr + RX_MODE), status);#ifndef final_version if (status == 0) { outb(0x05, ioaddr + 14); break; }#endif if ((status & 0xF0) != 0x20) { /* There was an error. */ lp->stats.rx_errors++; if (status & 0x08) lp->stats.rx_length_errors++; if (status & 0x04) lp->stats.rx_frame_errors++; if (status & 0x02) lp->stats.rx_crc_errors++; if (status & 0x01) lp->stats.rx_over_errors++; } else { ushort pkt_len = inw(ioaddr + DATAPORT); /* Malloc up new buffer. */ struct sk_buff *skb; if (pkt_len > 1550) { printk("%s: The FMV-18x claimed a very large packet, size %d.\n", dev->name, pkt_len); outb(0x05, ioaddr + 14); lp->stats.rx_errors++; break; } skb = dev_alloc_skb(pkt_len+3); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet (len %d).\n", dev->name, pkt_len); outb(0x05, ioaddr + 14); lp->stats.rx_dropped++; break; } skb->dev = dev; skb_reserve(skb,2); insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); if (net_debug > 5) { int i; printk("%s: Rxed packet of length %d: ", dev->name, pkt_len); for (i = 0; i < 14; i++) printk(" %02x", skb->data[i]); printk(".\n"); } skb->protocol=eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; } if (--boguscount <= 0) break; } /* If any worth-while packets have been received, dev_rint() has done a mark_bh(NET_BH) for us and will work on them when we get to the bottom-half routine. */ { int i; for (i = 0; i < 20; i++) { if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40) break; (void)inw(ioaddr + DATAPORT); /* dummy status read */ outb(0x05, ioaddr + 14); } if (net_debug > 5 && i > 0) printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); } return;}/* The inverse routine to net_open(). */static int net_close(struct device *dev){ int ioaddr = dev->base_addr; ((struct net_local *)dev->priv)->open_time = 0; dev->tbusy = 1; dev->start = 0; /* Set configuration register 0 to disable Tx and Rx. */ outb(0xda, ioaddr + CONFIG_0); /* Update the statistics -- ToDo. */ /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); MOD_DEC_USE_COUNT; /* Set the ethernet adaptor disable IRQ */ outb(0x00, ioaddr + FJ_CONFIG1); 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 device *dev){ struct net_local *lp = (struct net_local *)dev->priv; cli(); /* ToDo: Update the statistics from the device registers. */ sti(); 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){ short ioaddr = dev->base_addr; if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) { /* * We must make the kernel realise we had to move * into promisc mode or we start all out war on * the cable. - AC */ dev->flags|=IFF_PROMISC; outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ } else outb(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */}#ifdef MODULEstatic char devicename[9] = { 0, };static struct device dev_fmv18x = { devicename, /* device name is inserted by linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, fmv18x_probe };static int io = 0x220;static int irq = 0;MODULE_PARM(io, "i");MODULE_PARM(irq, "i");MODULE_PARM(net_debug, "i");int init_module(void){ if (io == 0) printk("fmv18x: You should not use auto-probing with insmod!\n"); dev_fmv18x.base_addr = io; dev_fmv18x.irq = irq; if (register_netdev(&dev_fmv18x) != 0) { printk("fmv18x: register_netdev() returned non-zero.\n"); return -EIO; } return 0;}voidcleanup_module(void){ unregister_netdev(&dev_fmv18x); kfree(dev_fmv18x.priv); dev_fmv18x.priv = NULL; /* If we don't do this, we can't re-insmod it later. */ free_irq(dev_fmv18x.irq, &dev_fmv18x); release_region(dev_fmv18x.base_addr, FMV18X_IO_EXTENT);}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c fmv18x.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 * c-indent-level: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -