at1700.c
来自「linux 内核源代码」· C语言 代码 · 共 914 行 · 第 1/2 页
C
914 行
dev->tx_timeout = net_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; spin_lock_init(&lp->lock); lp->jumpered = is_fmv18x; lp->mca_slot = slot; /* Snarf the interrupt vector now. */ ret = request_irq(irq, &net_interrupt, 0, DRV_NAME, dev); if (ret) { printk (" AT1700 at %#3x is unusable due to a conflict on" "IRQ %d.\n", ioaddr, irq); goto err_mca; } return 0;err_mca:#ifdef CONFIG_MCA_LEGACY if (slot >= 0) mca_mark_as_unused(slot);#endiferr_out: release_region(ioaddr, AT1700_IO_EXTENT); return ret;}/* EEPROM_Ctrl bits. */#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. *//* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD (5 << 6)#define EE_READ_CMD (6 << 6)#define EE_ERASE_CMD (7 << 6)static int __init read_eeprom(long ioaddr, int location){ int i; unsigned short retval = 0; long ee_addr = ioaddr + EEPROM_Ctrl; long ee_daddr = ioaddr + EEPROM_Data; int read_cmd = location | EE_READ_CMD; /* Shift the read command bits out. */ for (i = 9; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(EE_CS, ee_addr); outb(dataval, ee_daddr); outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */ } outb(EE_DATA_WRITE, ee_daddr); for (i = 16; i > 0; i--) { outb(EE_CS, ee_addr); outb(EE_CS | EE_SHIFT_CLK, ee_addr); retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0); } /* Terminate the EEPROM access. */ outb(EE_CS, ee_addr); outb(EE_SHIFT_CLK, ee_addr); outb(0, ee_addr); return retval;}static int net_open(struct net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit bus access, and two 4K Tx queues. */ outb(0x5a, ioaddr + CONFIG_0); /* Powerup, switch to register bank 2, and enable the Rx and Tx. */ outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; /* Turn on hardware Tx and Rx interrupts. */ outb(0x82, ioaddr + TX_INTR); outb(0x81, ioaddr + RX_INTR); /* Enable the IRQ on boards of fmv18x it is feasible. */ if (lp->jumpered) { outb(0x80, ioaddr + IOCONFIG1); } netif_start_queue(dev); return 0;}static void net_tx_timeout (struct net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; printk ("%s: transmit timed out with status %04x, %s?\n", dev->name, inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80 ? "IRQ conflict" : "network cable problem"); printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", dev->name, inw(ioaddr + TX_STATUS), inw(ioaddr + TX_INTR), inw(ioaddr + TX_MODE), inw(ioaddr + CONFIG_0), inw(ioaddr + DATAPORT), inw(ioaddr + TX_START), inw(ioaddr + MODE13 - 1), inw(ioaddr + RX_CTRL)); dev->stats.tx_errors++; /* ToDo: We should try to restart the adaptor... */ outw(0xffff, ioaddr + MODE24); outw (0xffff, ioaddr + TX_STATUS); outb (0x5a, ioaddr + CONFIG_0); outb (0xe8, ioaddr + CONFIG_1); outw (0x8182, ioaddr + TX_INTR); outb (0x00, ioaddr + TX_START); outb (0x03, ioaddr + COL16CNTL); dev->trans_start = jiffies; lp->tx_started = 0; lp->tx_queue_ready = 1; lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; netif_wake_queue(dev);}static int net_send_packet (struct sk_buff *skb, struct net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; short len = skb->len; unsigned char *buf = skb->data; static u8 pad[ETH_ZLEN]; netif_stop_queue (dev); /* 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); /* Packet data */ outsw (ioaddr + DATAPORT, buf, len >> 1); /* Check for dribble byte */ if (len & 1) { outw(skb->data[skb->len-1], ioaddr + DATAPORT); len++; } /* Check for packet padding */ if (length != skb->len) outsw(ioaddr + DATAPORT, pad, (length - len + 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; netif_start_queue (dev); } else if (lp->tx_queue_len < 4096 - 1502) /* Yes, there is room for one more packet. */ netif_start_queue (dev); dev_kfree_skb (skb); return 0;}/* The typical workload of the driver: Handle the network interface interrupts. */static irqreturn_t net_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; struct net_local *lp; int ioaddr, status; int handled = 0; if (dev == NULL) { printk ("at1700_interrupt(): irq %d for unknown device.\n", irq); return IRQ_NONE; } ioaddr = dev->base_addr; lp = netdev_priv(dev); spin_lock (&lp->lock); 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. */ handled = 1; 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) { handled = 1; 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); dev->stats.collisions++; } if (status & 0x82) { dev->stats.tx_packets++; /* The Tx queue has any packets and is not being transferred a packet from the host, start transmitting. */ 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; netif_wake_queue (dev); } else { lp->tx_started = 0; netif_wake_queue (dev); } } } spin_unlock (&lp->lock); return IRQ_RETVAL(handled);}/* We have a good packet(s), get it/them out of the buffers. */static voidnet_rx(struct net_device *dev){ int ioaddr = dev->base_addr; int boguscount = 5; while ((inb(ioaddr + RX_MODE) & 0x40) == 0) { ushort status = inw(ioaddr + DATAPORT); ushort pkt_len = 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 + RX_CTRL); break; }#endif if ((status & 0xF0) != 0x20) { /* There was an error. */ dev->stats.rx_errors++; if (status & 0x08) dev->stats.rx_length_errors++; if (status & 0x04) dev->stats.rx_frame_errors++; if (status & 0x02) dev->stats.rx_crc_errors++; if (status & 0x01) dev->stats.rx_over_errors++; } else { /* Malloc up new buffer. */ struct sk_buff *skb; if (pkt_len > 1550) { printk("%s: The AT1700 claimed a very large packet, size %d.\n", dev->name, pkt_len); /* Prime the FIFO and then flush the packet. */ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); outb(0x05, ioaddr + RX_CTRL); dev->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); /* Prime the FIFO and then flush the packet. */ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); outb(0x05, ioaddr + RX_CTRL); dev->stats.rx_dropped++; break; } skb_reserve(skb,2); insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); skb->protocol=eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; } 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; inw(ioaddr + DATAPORT); /* dummy status read */ outb(0x05, ioaddr + RX_CTRL); } if (net_debug > 5) 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 net_device *dev){ struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; netif_stop_queue(dev); /* Set configuration register 0 to disable Tx and Rx. */ outb(0xda, ioaddr + CONFIG_0); /* No statistic counters on the chip to update. */ /* Disable the IRQ on boards of fmv18x where it is feasible. */ if (lp->jumpered) { outb(0x00, ioaddr + IOCONFIG1); free_irq(dev->irq, dev); } /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); return 0;}/* Set the multicast/promiscuous mode for this adaptor.*/static voidset_rx_mode(struct net_device *dev){ int ioaddr = dev->base_addr; struct net_local *lp = netdev_priv(dev); unsigned char mc_filter[8]; /* Multicast hash filter */ unsigned long flags; int i; if (dev->flags & IFF_PROMISC) { memset(mc_filter, 0xff, sizeof(mc_filter)); outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ } else if (dev->mc_count > MC_FILTERBREAK || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); outb(2, ioaddr + RX_MODE); /* Use normal mode. */ } else if (dev->mc_count == 0) { memset(mc_filter, 0x00, sizeof(mc_filter)); outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ } else { struct dev_mc_list *mclist; int i; memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { unsigned int bit = ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26; mc_filter[bit >> 3] |= (1 << bit); } outb(0x02, ioaddr + RX_MODE); /* Use normal mode. */ } spin_lock_irqsave (&lp->lock, flags); if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) { int saved_bank = inw(ioaddr + CONFIG_0); /* Switch to bank 1 and set the multicast table. */ outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0); for (i = 0; i < 8; i++) outb(mc_filter[i], ioaddr + PORT_OFFSET(8 + i)); memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter)); outw(saved_bank, ioaddr + CONFIG_0); } spin_unlock_irqrestore (&lp->lock, flags); return;}#ifdef MODULEstatic struct net_device *dev_at1700;module_param(io, int, 0);module_param(irq, int, 0);module_param(net_debug, int, 0);MODULE_PARM_DESC(io, "AT1700/FMV18X I/O base address");MODULE_PARM_DESC(irq, "AT1700/FMV18X IRQ number");MODULE_PARM_DESC(net_debug, "AT1700/FMV18X debug level (0-6)");int __init init_module(void){ if (io == 0) printk("at1700: You should not use auto-probing with insmod!\n"); dev_at1700 = at1700_probe(-1); if (IS_ERR(dev_at1700)) return PTR_ERR(dev_at1700); return 0;}void __exitcleanup_module(void){ unregister_netdev(dev_at1700); cleanup_card(dev_at1700); free_netdev(dev_at1700);}#endif /* MODULE */MODULE_LICENSE("GPL");/* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" * tab-width: 4 * c-basic-offset: 4 * c-indent-level: 4 * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?