Linux-PCI网络设备驱动
1, 驱动模块的加载和卸载
如果网络设备(包括wireless)是PCI规范的,则先是向内核注册该PCI设备(pci_register_driver),然后由pci_driver数据结构中的probe函数指针所指向的侦测函数来初始化该PCI设备,并且同时注册和初始化该网络设备。
如果网络设备(包括wireless)是PCMCIA规范的,则先是向内核注册该PCMCIA设备(register_pccard_driver),然后 driver_info_t数据结构中的attach函数指针所指向的侦测函数来初始化该PCMCIA设备,并且同时注册和初始化该网络设备。
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int __init tg3_init(void) { return pci_module_init(&tg3_driver); }
static void __exit tg3_cleanup(void) { pci_unregister_driver(&tg3_driver); }
module_init(tg3_init); module_exit(tg3_cleanup);
|
申明为PCI设备:
1 2 3 4 5 6 7 8
| static struct pci_driver tg3_driver = { .name = DRV_MODULE_NAME, .id_table = tg3_pci_tbl, .probe = tg3_init_one, .remove = __devexit_p(tg3_remove_one), .suspend = tg3_suspend, .resume = tg3_resume };
|
2,PCI设备探测函数probe,初始化网络设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| static int __devinit tg3_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { pci_enable_device(pdev); pci_request_regions(pdev, DRV_MODULE_NAME); pci_set_master(pdev); pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff); tg3reg_base = pci_resource_start(pdev, 0); tg3reg_len = pci_resource_len(pdev, 0); dev = alloc_etherdev(sizeof(*tp)); SET_MODULE_OWNER(dev); tp = dev->priv; tp->pdev = pdev; tp->dev = dev; …… spin_lock_init(&tp->lock); tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len); dev->irq = pdev->irq; dev->open = tg3_open; dev->stop = tg3_close; dev->get_stats = tg3_get_stats; dev->set_multicast_list = tg3_set_rx_mode; dev->set_mac_address = tg3_set_mac_addr; dev->do_ioctl = tg3_ioctl; dev->tx_timeout = tg3_tx_timeout; dev->hard_start_xmit= tg3_start_xmit; tg3_get_device_address(tp); register_netdev(dev); pci_set_drvdata(pdev, dev); }
|
3,注销网络设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static void __devexit tg3_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); unregister_netdev(dev); iounmap((void *) ((struct tg3 *)(dev->priv))->regs); kfree(dev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); }
|
4,打开网络设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| static int tg3_open(struct net_device *dev) { request_irq(dev->irq, tg3_interrupt, SA_SHIRQ, dev->name, dev);
tg3_init_hw(tp); tg3_init_rings(tp); init_timer(&tp->timer); tp->timer.expires = jiffies + tp->timer_offset; tp->timer.data = (unsigned long) tp; tp->timer.function = tg3_timer; add_timer(&tp->timer); netif_start_queue(dev); }
|
5,关闭网络设备
1 2 3 4 5 6 7 8 9 10 11 12
| static int tg3_close(struct net_device *dev) { netif_stop_queue(dev); netif_carrier_off(tp->dev); del_timer_sync(&tp->timer); tg3_free_rings(tp); free_irq(dev->irq, dev); }
|
6,硬件处理数据包发送
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) { len = (skb->len - skb->data_len); mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); tp->tx_buffers[entry].skb = skb; pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping); tg3_set_txd(tp, entry, mapping, len, base_flags, mss_and_is_end); dev->trans_start = jiffies; }
|
7,中断处理收包,发包
1 2 3 4 5 6 7
| static void tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { tg3_rx(tp); tg3_tx(tp); }
|
8,发包
1 2 3 4 5 6 7 8 9 10
| static void tg3_tx(struct tg3 *tp) { struct tx_ring_info *ri = &tp->tx_buffers[sw_idx]; struct sk_buff *skb = ri->skb; pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping), (skb->len - skb->data_len), PCI_DMA_TODEVICE); ri->skb = NULL; dev_kfree_skb_irq(skb); }
|
9,收包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static int tg3_rx(struct tg3 *tp, int budget) { struct sk_buff *copy_skb; copy_skb = dev_alloc_skb(len + 2); copy_skb->dev = tp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); pci_dma_sync_single(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); memcpy(copy_skb->data, skb->data, len); skb = copy_skb; skb->protocol = eth_type_trans(skb, tp->dev); netif_rx(skb); tp->dev->last_rx = jiffies; }
|
10, 读取包的网卡收发包的状态,统计数据
1 2 3 4 5
| static struct net_device_stats *tg3_get_stats(struct net_device *dev) { }
|
11, 用户的ioctl命令系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; switch(cmd) { case SIOCETHTOOL: return tg3_ethtool_ioctl(dev, (void *) ifr->ifr_data); case SIOCGMIIREG: { err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval) data->val_out = mii_regval; return err; } …… } }
|
12, PCI设备的挂起和恢复函数
1 2 3 4 5 6 7 8 9 10 11
| static int tg3_suspend(struct pci_dev *pdev, u32 state) { tg3_disable_ints(tp); netif_device_detach(dev); tg3_halt(tp); tg3_set_power_state(tp, state); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int tg3_resume(struct pci_dev *pdev) { tg3_set_power_state(tp, 0); netif_device_attach(dev); tg3_init_rings(tp); tg3_init_hw(tp); tg3_enable_ints(tp); }
|
13,参数设置
在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般只有超级用户(root)权限才能对设备参数进行设置。设置方法有:
tg3_set_mac_addr (dev->set_mac_address)
当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般对mac地址的设置没有太大意义的。
dev->set_config()
当用户调用ioctl时类型为SIOCSIFMAP时,系统会调用驱动程序的set_config方法
用户会传递一个ifmap结构包含需要的I/O、中断等参数。
总结:
所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。一个设备就是一个对象(net_device 结构),它内部有自己的数据和方法。一个网络设备最基本的方法有初始化,发送和接收。
Linux网络驱动程序的体系结构可以划分为四层:
网络协议接口,网络设备接口,设备驱动功能,网络设备和网络媒介层
网络驱动程序,最主要的工作就是完成设备驱动功能层。在Linux中所有网络设备都抽象为一个接口,这个接口提供了对所有网络设备的操作集合。由数据结构 struct net_device来表示网络设备在内核中的运行情况,即网络设备接口。它既包括纯软件网络设备接口,如环路(Loopback),也包括硬件网络设备 接口,如以太网卡。而由以dev_base为头指针的设备链表来集体管理所有网络设备,该设备链表中的每个元素代表一个网络设备接口。数据结构 net_device中有很多供系统访问和协议层调用的设备方法,包括初始化,打开和关闭网络设备的open和stop函数,处理数据包发送的 hard_start_xmit函数,以及中断处理函数等。
网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。