程序使用来描述这些寄存器的标识符。如下:
struct pci_device_id { __u32 vendor, device; __u32 subvendor, subdevice; __u32 class, class_mask; kernel_ulong_t driver_#; };
#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\ PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \ PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich } static struct pci_device_id e100_id_table[] = { INTEL_8255X_ETHERNET_DEVICE(0x1029, 0), INTEL_8255X_ETHERNET_DEVICE(0x1030, 0), INTEL_8255X_ETHERNET_DEVICE(0x1031, 3), …… { 0, } };
struct pci_driver { struct list_head node; char *name; struct module *owner; const struct pci_device_id *id_table; int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); void (*remove) (struct pci_dev *dev); int (*suspend) (struct pci_dev *dev, pm_message_t state); int (*resume) (struct pci_dev *dev); int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); void (*shutdown) (struct pci_dev *dev); struct device_driver driver; struct pci_dynids dynids; };
static struct pci_driver e100_driver = { .name = DRV_NAME, .id_table = e100_id_table, .probe = e100_probe, .remove = __devexit_p(e100_remove), #ifdef CONFIG_PM .suspend = e100_suspend, .resume = e100_resume, #endif .driver = { .shutdown = e100_shutdown, } };
static int __init e100_init_module(void) { if(((1 << debug) - 1) & NETIF_MSG_DRV) { printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT); } return pci_module_init(&e100_driver); }
int pci_request_regions(struct pci_dev *pdev, char *res_name) { int i; for (i = 0; i < 6; i++) if(pci_request_region(pdev, i, res_name)) goto err_out; return 0;
int pci_request_region(struct pci_dev *pdev, int bar, char *res_name) { if (pci_resource_len(pdev, bar) == 0) return 0; if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) { if (!request_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar), res_name)) goto err_out; } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { if (!request_mem_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar), res_name)) goto err_out; } return 0;
static int __devinit e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct nic *nic; int err; if(!(netdev = alloc_etherdev(sizeof(struct nic)))) { if(((1 << debug) - 1) & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Etherdev alloc failed, abort.\n"); return -ENOMEM; } netdev->open = e100_open; netdev->stop = e100_close; netdev->hard_start_xmit = e100_xmit_frame; netdev->get_stats = e100_get_stats; netdev->set_multicast_list = e100_set_multicast_list; netdev->set_mac_address = e100_set_mac_address; netdev->change_mtu = e100_change_mtu; netdev->do_ioctl = e100_do_ioctl; SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops); netdev->tx_timeout = e100_tx_timeout; netdev->watchdog_timeo = E100_WATCHDOG_PERIOD; netdev->poll = e100_poll; netdev->weight = E100_NAPI_WEIGHT; #ifdef CONFIG_NET_POLL_CONTROLLER netdev->poll_controller = e100_netpoll; #endif strcpy(netdev->name, pci_name(pdev)); nic = netdev_priv(netdev); nic->netdev = netdev; nic->pdev = pdev; nic->msg_enable = (1 << debug) - 1; pci_set_drv#(pdev, netdev); if((err = pci_enable_device(pdev))) { DPRINTK(PROBE, ERR, "Cannot enable PCI device, aborting.\n"); goto err_out_free_dev; } if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { DPRINTK(PROBE, ERR, "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; goto err_out_disable_pdev; } if((err = pci_request_regions(pdev, DRV_NAME))) { DPRINTK(PROBE, ERR, "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable_pdev; } if((err = pci_set_dma_mask(pdev, 0xFFFFFFFFULL))) { DPRINTK(PROBE, ERR, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } SET_MODULE_OWNER(netdev); SET_NETDEV_DEV(netdev, &pdev->dev); nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr)); if(!nic->csr) { DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n"); err = -ENOMEM; goto err_out_free_res; } if(ent->driver_#) nic->flags |= ich; else nic->flags &= ~ich; e100_get_defaults(nic); spin_lock_init(&nic->cb_lock); spin_lock_init(&nic->cmd_lock); e100_hw_reset(nic); pci_set_master(pdev); init_timer(&nic->watchdog); nic->watchdog.function = e100_watchdog; nic->watchdog.# = (unsigned long)nic; init_timer(&nic->blink_timer); nic->blink_timer.function = e100_blink_led; nic->blink_timer.# = (unsigned long)nic; INIT_WORK(&nic->tx_timeout_task, (void (*)(void *))e100_tx_timeout_task, netdev); if((err = e100_alloc(nic))) { DPRINTK(PROBE, ERR, "Cannot alloc driver memory, aborting.\n"); goto err_out_iounmap; } e100_phy_init(nic); if((err = e100_eeprom_load(nic))) goto err_out_free; memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN); if(!is_valid_ether_addr(netdev->dev_addr)) { DPRINTK(PROBE, ERR, "Invalid MAC address from " "EEPROM, aborting.\n"); err = -EAGAIN; goto err_out_free; } if((nic->mac >= mac_82558_D101_A4) && (nic->eeprom[eeprom_id] & eeprom_id_wol)) nic->flags |= wol_magic; pci_enable_wake(pdev, 0, 0); strcpy(netdev->name, "eth%d"); if((err = register_netdev(netdev))) { DPRINTK(PROBE, ERR, "Cannot register net device, aborting.\n"); goto err_out_free; } DPRINTK(PROBE, INFO, "addr 0x%lx, irq %d, " "MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n", pci_resource_start(pdev, 0), pdev->irq, netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); return 0; err_out_free: e100_free(nic); err_out_iounmap: iounmap(nic->csr); err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: pci_disable_device(pdev); err_out_free_dev: pci_set_drv#(pdev, NULL); free_netdev(netdev); return err; }
static int e100_open(struct net_device *netdev) { struct nic *nic = netdev_priv(netdev); int err = 0; netif_carrier_off(netdev); if((err = e100_up(nic))) DPRINTK(IFUP, ERR, "Cannot open interface, aborting.\n"); return err; }
对于探测网卡网线是否连接,这一组函数被使用得较多;
接着,调用e100_up函数启动网卡,这个“启动”的过程,最重要的步骤有:
1、调用request_irq向内核注册中断;
2、调用netif_wake_queue函数来重新启动传输队例;
static int e100_up(struct nic *nic) { int err; if((err = e100_rx_alloc_list(nic))) return err; if((err = e100_alloc_cbs(nic))) goto err_rx_clean_list; if((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); e100_start_receiver(nic, 0); mod_timer(&nic->watchdog, jiffies); if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ, nic->netdev->name, nic->netdev))) goto err_no_irq; netif_wake_queue(nic->netdev); netif_poll_enable(nic->netdev); e100_enable_irq(nic); return 0; err_no_irq: del_timer_sync(&nic->watchdog); err_clean_cbs: e100_clean_cbs(nic); err_rx_clean_list: e100_rx_clean_list(nic); return err; }
三、网卡中断
从本质上来讲,中断,是一种电信号,当设备有某种事件发生的时候,它就会产生中断,通过总线把电信号发送给中断控制器,如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到内存中内核设置的中断处理程序的入口点,进行中断处理。
在内核中断处理中,会检测中断与我们刚才注册的中断号匹配,于是,注册的中断处理函数就被调用了。
当需要发/收数据,出现错误,连接状态变化等,网卡的中断信号会被触发。当接收到中断后,中断函数读取中断状态位,进行合法性判断,如判断中断信号是否是自己的等,然后,应答设备中断——OK,我已经知道了,你回去继续工作吧……
接着,它就屏蔽此中断,然后netif_rx_schedule函数接收,接收函数会在未来某一时刻调用设备的poll函数(对这里而言,注册的是e100_poll)实现设备的轮询
static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *netdev = dev_id; struct nic *nic = netdev_priv(netdev); u8 stat_ack = readb(&nic->csr->scb.stat_ack); DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack); if(stat_ack == stat_ack_not_ours || stat_ack == stat_ack_not_present) return IRQ_NONE; writeb(stat_ack, &nic->csr->scb.stat_ack); if(stat_ack & stat_ack_rnr) nic->ru_running = RU_SUSPENDED; e100_disable_irq(nic); netif_rx_schedule(netdev); return IRQ_HANDLED; }
static int e100_poll(struct net_device *netdev, int *budget) { struct nic *nic = netdev_priv(netdev); unsigned int work_to_do = min(netdev->quota, *budget); unsigned int work_done = 0; int tx_cleaned; e100_rx_clean(nic, &work_done, work_to_do); tx_cleaned = e100_tx_clean(nic); if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) { netif_rx_complete(netdev); e100_enable_irq(nic); return 0; } *budget -= work_done; netdev->quota -= work_done; return 1; } static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done, unsigned int work_to_do) { struct rx *rx; int restart_required = 0; struct rx *rx_to_start = NULL; if(RU_SUSPENDED == nic->ru_running) restart_required = 1; for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { int err = e100_rx_indicate(nic, rx, work_done, work_to_do); if(-EAGAIN == err) { restart_required = 0; break; } else if(-ENO# == err) break; } if(restart_required) rx_to_start = nic->rx_to_clean; for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; } if(restart_required) { // ack the rnr? writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); e100_start_receiver(nic, rx_to_start); if(work_done) (*work_done)++; } }