3c515.c
来自「linux 内核源代码」· C语言 代码 · 共 1,591 行 · 第 1/4 页
C
1,591 行
/* Note: this is the only limit on the number of cards supported!! */static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };#ifdef MODULEstatic int debug = -1;module_param(debug, int, 0);module_param_array(options, int, NULL, 0);module_param(rx_copybreak, int, 0);module_param(max_interrupt_work, int, 0);MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");/* A list of all installed Vortex devices, for removing the driver module. *//* we will need locking (and refcounting) if we ever use it for more */static LIST_HEAD(root_corkscrew_dev);int init_module(void){ int found = 0; if (debug >= 0) corkscrew_debug = debug; if (corkscrew_debug) printk(version); while (corkscrew_scan(-1)) found++; return found ? 0 : -ENODEV;}#elsestruct net_device *tc515_probe(int unit){ struct net_device *dev = corkscrew_scan(unit); static int printed; if (!dev) return ERR_PTR(-ENODEV); if (corkscrew_debug > 0 && !printed) { printed = 1; printk(version); } return dev;}#endif /* not MODULE */static int check_device(unsigned ioaddr){ int timer; if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) return 0; /* Check the resource configuration for a matching ioaddr. */ if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) { release_region(ioaddr, CORKSCREW_TOTAL_SIZE); return 0; } /* Verify by reading the device ID from the EEPROM. */ outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); /* Pause for at least 162 us. for the read to take place. */ for (timer = 4; timer >= 0; timer--) { udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) break; } if (inw(ioaddr + Wn0EepromData) != 0x6d50) { release_region(ioaddr, CORKSCREW_TOTAL_SIZE); return 0; } return 1;}static void cleanup_card(struct net_device *dev){ struct corkscrew_private *vp = netdev_priv(dev); list_del_init(&vp->list); if (dev->dma) free_dma(dev->dma); outw(TotalReset, dev->base_addr + EL3_CMD); release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE); if (vp->dev) pnp_device_detach(to_pnp_dev(vp->dev));}static struct net_device *corkscrew_scan(int unit){ struct net_device *dev; static int cards_found = 0; static int ioaddr; int err;#ifdef __ISAPNP__ short i; static int pnp_cards;#endif dev = alloc_etherdev(sizeof(struct corkscrew_private)); if (!dev) return ERR_PTR(-ENOMEM); if (unit >= 0) { sprintf(dev->name, "eth%d", unit); netdev_boot_setup_check(dev); }#ifdef __ISAPNP__ if(nopnp == 1) goto no_pnp; for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) { struct pnp_dev *idev = NULL; int irq; while((idev = pnp_find_dev(NULL, corkscrew_isapnp_adapters[i].vendor, corkscrew_isapnp_adapters[i].function, idev))) { if (pnp_device_attach(idev) < 0) continue; if (pnp_activate_dev(idev) < 0) { printk("pnp activate failed (out of resources?)\n"); pnp_device_detach(idev); continue; } if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { pnp_device_detach(idev); continue; } ioaddr = pnp_port_start(idev, 0); irq = pnp_irq(idev, 0); if (!check_device(ioaddr)) { pnp_device_detach(idev); continue; } if(corkscrew_debug) printk ("ISAPNP reports %s at i/o 0x%x, irq %d\n", (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq); printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */ SET_NETDEV_DEV(dev, &idev->dev); pnp_cards++; err = corkscrew_setup(dev, ioaddr, idev, cards_found++); if (!err) return dev; cleanup_card(dev); } }no_pnp:#endif /* __ISAPNP__ */ /* Check all locations on the ISA bus -- evil! */ for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { if (!check_device(ioaddr)) continue; printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); err = corkscrew_setup(dev, ioaddr, NULL, cards_found++); if (!err) return dev; cleanup_card(dev); } free_netdev(dev); return NULL;}static int corkscrew_setup(struct net_device *dev, int ioaddr, struct pnp_dev *idev, int card_number){ struct corkscrew_private *vp = netdev_priv(dev); unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ int i; int irq; DECLARE_MAC_BUF(mac); if (idev) { irq = pnp_irq(idev, 0); vp->dev = &idev->dev; } else { irq = inw(ioaddr + 0x2002) & 15; } dev->base_addr = ioaddr; dev->irq = irq; dev->dma = inw(ioaddr + 0x2000) & 7; vp->product_name = "3c515"; vp->options = dev->mem_start; vp->our_dev = dev; if (!vp->options) { if (card_number >= MAX_UNITS) vp->options = -1; else vp->options = options[card_number]; } if (vp->options >= 0) { vp->media_override = vp->options & 7; if (vp->media_override == 2) vp->media_override = 0; vp->full_duplex = (vp->options & 8) ? 1 : 0; vp->bus_master = (vp->options & 16) ? 1 : 0; } else { vp->media_override = 7; vp->full_duplex = 0; vp->bus_master = 0; }#ifdef MODULE list_add(&vp->list, &root_corkscrew_dev);#endif printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr); spin_lock_init(&vp->lock); /* Read the station address from the EEPROM. */ EL3WINDOW(0); for (i = 0; i < 0x18; i++) { __be16 *phys_addr = (__be16 *) dev->dev_addr; int timer; outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); /* Pause for at least 162 us. for the read to take place. */ for (timer = 4; timer >= 0; timer--) { udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) break; } eeprom[i] = inw(ioaddr + Wn0EepromData); checksum ^= eeprom[i]; if (i < 3) phys_addr[i] = htons(eeprom[i]); } checksum = (checksum ^ (checksum >> 8)) & 0xff; if (checksum != 0x00) printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); printk(" %s", print_mac(mac, dev->dev_addr)); if (eeprom[16] == 0x11c7) { /* Corkscrew */ if (request_dma(dev->dma, "3c515")) { printk(", DMA %d allocation failed", dev->dma); dev->dma = 0; } else printk(", DMA %d", dev->dma); } printk(", IRQ %d\n", dev->irq); /* Tell them about an invalid IRQ. */ if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15)) printk(KERN_WARNING " *** Warning: this IRQ is unlikely to work! ***\n"); { char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" }; __u32 config; EL3WINDOW(3); vp->available_media = inw(ioaddr + Wn3_Options); config = inl(ioaddr + Wn3_Config); if (corkscrew_debug > 1) printk(KERN_INFO " Internal config register is %4.4x, transceivers %#x.\n", config, inw(ioaddr + Wn3_Options)); printk(KERN_INFO " %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", 8 << config & Ram_size, config & Ram_width ? "word" : "byte", ram_split[(config & Ram_split) >> Ram_split_shift], config & Autoselect ? "autoselect/" : "", media_tbl[(config & Xcvr) >> Xcvr_shift].name); vp->default_media = (config & Xcvr) >> Xcvr_shift; vp->autoselect = config & Autoselect ? 1 : 0; dev->if_port = vp->default_media; } if (vp->media_override != 7) { printk(KERN_INFO " Media override to transceiver type %d (%s).\n", vp->media_override, media_tbl[vp->media_override].name); dev->if_port = vp->media_override; } vp->capabilities = eeprom[16]; vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; /* Rx is broken at 10mbps, so we always disable it. */ /* vp->full_bus_master_rx = 0; */ vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; /* The 3c51x-specific entries in the device structure. */ dev->open = &corkscrew_open; dev->hard_start_xmit = &corkscrew_start_xmit; dev->tx_timeout = &corkscrew_timeout; dev->watchdog_timeo = (400 * HZ) / 1000; dev->stop = &corkscrew_close; dev->get_stats = &corkscrew_get_stats; dev->set_multicast_list = &set_rx_mode; dev->ethtool_ops = &netdev_ethtool_ops; return register_netdev(dev);}static int corkscrew_open(struct net_device *dev){ int ioaddr = dev->base_addr; struct corkscrew_private *vp = netdev_priv(dev); __u32 config; int i; /* Before initializing select the active media port. */ EL3WINDOW(3); if (vp->full_duplex) outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ config = inl(ioaddr + Wn3_Config); if (vp->media_override != 7) { if (corkscrew_debug > 1) printk(KERN_INFO "%s: Media override to transceiver %d (%s).\n", dev->name, vp->media_override, media_tbl[vp->media_override].name); dev->if_port = vp->media_override; } else if (vp->autoselect) { /* Find first available media type, starting with 100baseTx. */ dev->if_port = 4; while (!(vp->available_media & media_tbl[dev->if_port].mask)) dev->if_port = media_tbl[dev->if_port].next; if (corkscrew_debug > 1) printk("%s: Initial media type %s.\n", dev->name, media_tbl[dev->if_port].name); init_timer(&vp->timer); vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; vp->timer.data = (unsigned long) dev; vp->timer.function = &corkscrew_timer; /* timer handler */ add_timer(&vp->timer); } else dev->if_port = vp->default_media; config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); outl(config, ioaddr + Wn3_Config); if (corkscrew_debug > 1) { printk("%s: corkscrew_open() InternalConfig %8.8x.\n", dev->name, config); } outw(TxReset, ioaddr + EL3_CMD); for (i = 20; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(RxReset, ioaddr + EL3_CMD); /* Wait a few ticks for the RxReset command to complete. */ for (i = 20; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); /* Use the now-standard shared IRQ implementation. */ if (vp->capabilities == 0x11c7) { /* Corkscrew: Cannot share ISA resources. */ if (dev->irq == 0 || dev->dma == 0 || request_irq(dev->irq, &corkscrew_interrupt, 0, vp->product_name, dev)) return -EAGAIN; enable_dma(dev->dma); set_dma_mode(dev->dma, DMA_MODE_CASCADE); } else if (request_irq(dev->irq, &corkscrew_interrupt, IRQF_SHARED, vp->product_name, dev)) { return -EAGAIN; } if (corkscrew_debug > 1) { EL3WINDOW(4); printk("%s: corkscrew_open() irq %d media status %4.4x.\n", dev->name, dev->irq, inw(ioaddr + Wn4_Media)); } /* Set the station address and mask in window 2 each time opened. */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + i); for (; i < 12; i += 2) outw(0, ioaddr + i); if (dev->if_port == 3) /* Start the thinnet transceiver. We should really wait 50ms... */ outw(StartCoax, ioaddr + EL3_CMD); EL3WINDOW(4); outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); for (i = 0; i < 10; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */ EL3WINDOW(4); inb(ioaddr + 12);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?