驱动11.网卡驱动程序
1 网络传输的简介
(1)接收过程,如上图,网络上的数据包到达网卡后,网卡产生中断,然后设备驱动层收到中断后,开始进行网络包的接收,接收完之后调用一个netif_rx函数交给网络协议层(层次结构上图一),然后就是一层一层的网上传到用户空间了。
(2)发送过程,从用户空间过来的数据包,经过层层穿越之后,到达网络协议层,然后调用一个dev_queue_xmit()函数之后就不管了,剩下的交给驱动层经过处理后,使用函数hard_start_xmit()函数发送,然后硬件上网卡开始发送数据包了。
2 dm9000网卡驱动源代码分析
2.1 首先,看入口函数
static int __init dm9000_init(void) { printk(KERN_INFO "%s Ethernet Driver\n", CARDNAME); return platform_driver_register(&dm9000_driver); /* search board and register */ }
这是一个平台总线的结构,注册dm9000_driver。
static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, }, .probe = dm9000_probe, .remove = dm9000_drv_remove, .suspend = dm9000_drv_suspend, .resume = dm9000_drv_resume, };
如果平台存在与其同名的平台设备,将调用probe函数
2.2 probe函数
1 /* 2 * Search DM9000 board, allocate space and register it 3 */ 4 static int 5 dm9000_probe(struct platform_device *pdev) 6 { 7 struct dm9000_plat_data *pdata = pdev->dev.platform_data; 8 struct board_info *db; /* Point a board information structure */ 9 struct net_device *ndev; 10 unsigned long base; 11 int ret = 0; 12 int iosize; 13 int i; 14 u32 id_val; 15 16 #if defined(CONFIG_ARCH_S3C2410xxx) 17 unsigned int oldval_bwscon; /* 用来保存BWSCON寄存器的值 */ 18 unsigned int oldval_bankcon4; /* 用来保存S3C2410_BANKCON4寄存器的值 */ 19 #endif 20 21 /* Init network device */ 22 ndev = alloc_etherdev(sizeof (struct board_info)); 23 if (!ndev) { 24 printk("%s: could not allocate device.\n", CARDNAME); 25 return -ENOMEM; 26 } 27 28 SET_MODULE_OWNER(ndev); 29 SET_NETDEV_DEV(ndev, &pdev->dev); 30 31 PRINTK2("dm9000_probe()"); 32 33 #if defined(CONFIG_ARCH_S3C2410xxx) 34 /* 设置Bank4: 总线宽度为16, 使能nWAIT。by www.100ask.net */ 35 oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON); 36 *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<16)) \ 37 | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4; 38 39 /* 设置BANK4的时间参数, by www.100ask.net */ 40 oldval_bankcon4 = *((volatile unsigned int *)S3C2410_BANKCON4); 41 *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c; 42 #endif 43 44 /* setup board info structure */ 45 db = (struct board_info *) ndev->priv; 46 memset(db, 0, sizeof (*db)); 47 48 spin_lock_init(&db->lock); 49 50 if (pdev->num_resources < 2) { 51 ret = -ENODEV; 52 goto out; 53 } else if (pdev->num_resources == 2) { 54 base = pdev->resource[0].start; 55 56 if (!request_mem_region(base, 4, ndev->name)) { 57 ret = -EBUSY; 58 goto out; 59 } 60 61 ndev->base_addr = base; 62 ndev->irq = pdev->resource[1].start; 63 db->io_addr = (void __iomem *)base; 64 db->io_data = (void __iomem *)(base + 4); 65 66 } else { 67 db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 68 db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 69 db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 70 71 if (db->addr_res == NULL || db->data_res == NULL || 72 db->irq_res == NULL) { 73 printk(KERN_ERR PFX "insufficient resources\n"); 74 ret = -ENOENT; 75 goto out; 76 } 77 78 i = res_size(db->addr_res); 79 db->addr_req = request_mem_region(db->addr_res->start, i, 80 pdev->name); 81 82 if (db->addr_req == NULL) { 83 printk(KERN_ERR PFX "cannot claim address reg area\n"); 84 ret = -EIO; 85 goto out; 86 } 87 88 db->io_addr = ioremap(db->addr_res->start, i); 89 90 if (db->io_addr == NULL) { 91 printk(KERN_ERR "failed to ioremap address reg\n"); 92 ret = -EINVAL; 93 goto out; 94 } 95 96 iosize = res_size(db->data_res); 97 db->data_req = request_mem_region(db->data_res->start, iosize, 98 pdev->name); 99 100 if (db->data_req == NULL) { 101 printk(KERN_ERR PFX "cannot claim data reg area\n"); 102 ret = -EIO; 103 goto out; 104 } 105 106 db->io_data = ioremap(db->data_res->start, iosize); 107 108 if (db->io_data == NULL) { 109 printk(KERN_ERR "failed to ioremap data reg\n"); 110 ret = -EINVAL; 111 goto out; 112 } 113 114 /* fill in parameters for net-dev structure */ 115 116 ndev->base_addr = (unsigned long)db->io_addr; 117 ndev->irq = db->irq_res->start; 118 119 /* ensure at least we have a default set of IO routines */ 120 dm9000_set_io(db, iosize); 121 } 122 123 /* check to see if anything is being over-ridden */ 124 if (pdata != NULL) { 125 /* check to see if the driver wants to over-ride the 126 * default IO width */ 127 128 if (pdata->flags & DM9000_PLATF_8BITONLY) 129 dm9000_set_io(db, 1); 130 131 if (pdata->flags & DM9000_PLATF_16BITONLY) 132 dm9000_set_io(db, 2); 133 134 if (pdata->flags & DM9000_PLATF_32BITONLY) 135 dm9000_set_io(db, 4); 136 137 /* check to see if there are any IO routine 138 * over-rides */ 139 140 if (pdata->inblk != NULL) 141 db->inblk = pdata->inblk; 142 143 if (pdata->outblk != NULL) 144 db->outblk = pdata->outblk; 145 146 if (pdata->dumpblk != NULL) 147 db->dumpblk = pdata->dumpblk; 148 } 149 150 dm9000_reset(db); 151 152 /* try two times, DM9000 sometimes gets the first read wrong */ 153 for (i = 0; i < 2; i++) { 154 id_val = ior(db, DM9000_VIDL); 155 id_val |= (u32)ior(db, DM9000_VIDH) << 8; 156 id_val |= (u32)ior(db, DM9000_PIDL) << 16; 157 id_val |= (u32)ior(db, DM9000_PIDH) << 24; 158 159 if (id_val == DM9000_ID) 160 break; 161 printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val); 162 } 163 164 if (id_val != DM9000_ID) { 165 printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val); 166 goto release; 167 } 168 169 /* from this point we assume that we have found a DM9000 */ 170 171 /* driver system function */ 172 ether_setup(ndev); 173 174 ndev->open = &dm9000_open; 175 ndev->hard_start_xmit = &dm9000_start_xmit; 176 ndev->tx_timeout = &dm9000_timeout; 177 ndev->watchdog_timeo = msecs_to_jiffies(watchdog); 178 ndev->stop = &dm9000_stop; 179 ndev->get_stats = &dm9000_get_stats; 180 ndev->set_multicast_list = &dm9000_hash_table; 181 #ifdef CONFIG_NET_POLL_CONTROLLER 182 ndev->poll_controller = &dm9000_poll_controller; 183 #endif 184 185 #ifdef DM9000_PROGRAM_EEPROM 186 program_eeprom(db); 187 #endif 188 db->msg_enable = NETIF_MSG_LINK; 189 db->mii.phy_id_mask = 0x1f; 190 db->mii.reg_num_mask = 0x1f; 191 db->mii.force_media = 0; 192 db->mii.full_duplex = 0; 193 db->mii.dev = ndev; 194 db->mii.mdio_read = dm9000_phy_read; 195 db->mii.mdio_write = dm9000_phy_write; 196 197 /* Read SROM content */ 198 for (i = 0; i < 64; i++) 199 ((u16 *) db->srom)[i] = read_srom_word(db, i); 200 201 /* Set Node Address */ 202 for (i = 0; i < 6; i++) 203 ndev->dev_addr[i] = db->srom[i]; 204 205 if (!is_valid_ether_addr(ndev->dev_addr)) { 206 /* try reading from mac */ 207 208 for (i = 0; i < 6; i++) 209 ndev->dev_addr[i] = ior(db, i+DM9000_PAR); 210 } 211 212 if (!is_valid_ether_addr(ndev->dev_addr)) { 213 printk("%s: Invalid ethernet MAC address. Please " 214 "set using ifconfig\n", ndev->name); 215 #if defined(CONFIG_ARCH_S3C2410) 216 printk("Now use the default MAC address: 08:90:90:90:90:90\n"); 217 ndev->dev_addr[0] = 0x08; 218 ndev->dev_addr[1] = 0x90; 219 ndev->dev_addr[2] = 0x90; 220 ndev->dev_addr[3] = 0x90; 221 ndev->dev_addr[4] = 0x90; 222 ndev->dev_addr[5] = 0x90; 223 #endif 224 } 225 226 platform_set_drvdata(pdev, ndev); 227 ret = register_netdev(ndev); 228 229 if (ret == 0) { 230 printk("%s: dm9000 at %p,%p IRQ %d MAC: ", 231 ndev->name, db->io_addr, db->io_data, ndev->irq); 232 for (i = 0; i < 5; i++) 233 printk("%02x:", ndev->dev_addr[i]); 234 printk("%02x\n", ndev->dev_addr[5]); 235 } 236 return 0; 237 238 release: 239 out: 240 printk("%s: not found (%d).\n", CARDNAME, ret); 241 #if defined(CONFIG_ARCH_S3C2410xxx) 242 /* 恢复寄存器原来的值 */ 243 *((volatile unsigned int *)S3C2410_BWSCON) = oldval_bwscon; 244 *((volatile unsigned int *)S3C2410_BANKCON4) = oldval_bankcon4; 245 #endif 246 dm9000_release_board(pdev, db); 247 free_netdev(ndev); 248 249 return ret; 250 }probe函数
主要完后以下工作:
①分配一个net_device结构体(alloc_etherdev)
②定义一个单板相关信息board_info结构体
③设置net_device和board_info结构体
④使用board_info信息来初始化单板
⑤初始化网卡
⑥设置操作函数open,hard_start_xmit(发包函数),tx_timeout(超时函数),watchdog_timeo(看门狗函数),stop,get_stats,set_multicast_list
设置MAC地址
最后,注册net_device结构体
2.3 发包函数
1 /* 2 * Hardware start transmission. 3 * Send a packet to media from the upper layer. 4 */ 5 static int 6 dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) 7 { 8 board_info_t *db = (board_info_t *) dev->priv; 9 10 PRINTK3("dm9000_start_xmit\n"); 11 12 if (db->tx_pkt_cnt > 1) 13 return 1; 14 15 netif_stop_queue(dev); 16 17 /* Disable all interrupts */ 18 iow(db, DM9000_IMR, IMR_PAR); 19 20 /* Move data to DM9000 TX RAM */ 21 writeb(DM9000_MWCMD, db->io_addr); 22 23 (db->outblk)(db->io_data, skb->data, skb->len); 24 db->stats.tx_bytes += skb->len; 25 26 /* TX control: First packet immediately send, second packet queue */ 27 if (db->tx_pkt_cnt == 0) { 28 29 /* First Packet */ 30 db->tx_pkt_cnt++; 31 32 /* Set TX length to DM9000 */ 33 iow(db, DM9000_TXPLL, skb->len & 0xff); 34 iow(db, DM9000_TXPLH, (skb->len >> 8) & 0xff); 35 36 /* Issue TX polling command */ 37 iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ 38 39 dev->trans_start = jiffies; /* save the time stamp */ 40 41 } else { 42 /* Second packet */ 43 db->tx_pkt_cnt++; 44 db->queue_pkt_len = skb->len; 45 } 46 47 /* free this SKB */ 48 dev_kfree_skb(skb); 49 50 /* Re-enable resource check */ 51 if (db->tx_pkt_cnt == 1) 52 netif_wake_queue(dev); 53 54 /* Re-enable interrupt */ 55 iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM); 56 57 return 0; 58 }发包函数
主要完后以下工作:
1.停止该网卡的队列,把skb数据写入网卡netif_stop_queue(dev);2.构造一个虚假的skb_buff上报3.释放skb(dev_kfree_skb)
4.数据全部发送出去后唤醒网卡队列(netif_wake_queue)
5.更新统计信息
3 写代码
1 /* 2 * 参考 drivers\net\cs89x0.c 3 */ 4 5 #include <linux/module.h> 6 #include <linux/errno.h> 7 #include <linux/netdevice.h> 8 #include <linux/etherdevice.h> 9 #include <linux/kernel.h> 10 #include <linux/types.h> 11 #include <linux/fcntl.h> 12 #include <linux/interrupt.h> 13 #include <linux/ioport.h> 14 #include <linux/in.h> 15 #include <linux/skbuff.h> 16 #include <linux/slab.h> 17 #include <linux/spinlock.h> 18 #include <linux/string.h> 19 #include <linux/init.h> 20 #include <linux/bitops.h> 21 #include <linux/delay.h> 22 #include <linux/ip.h> 23 24 #include <asm/system.h> 25 #include <asm/io.h> 26 #include <asm/irq.h> 27 28 static struct net_device *vnet_dev; 29 30 static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev) 31 { 32 /* 参考LDD3 */ 33 unsigned char *type; 34 struct iphdr *ih; 35 __be32 *saddr, *daddr, tmp; 36 unsigned char tmp_dev_addr[ETH_ALEN]; 37 struct ethhdr *ethhdr; 38 39 struct sk_buff *rx_skb; 40 41 // 从硬件读出/保存数据 42 /* 对调"源/目的"的mac地址 */ 43 ethhdr = (struct ethhdr *)skb->data; 44 memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN); 45 memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN); 46 memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN); 47 48 /* 对调"源/目的"的ip地址 */ 49 ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); 50 saddr = &ih->saddr; 51 daddr = &ih->daddr; 52 53 tmp = *saddr; 54 *saddr = *daddr; 55 *daddr = tmp; 56 57 //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ 58 //((u8 *)daddr)[2] ^= 1; 59 type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr); 60 //printk("tx package type = %02x\n", *type); 61 // 修改类型, 原来0x8表示ping 62 *type = 0; /* 0表示reply */ 63 64 ih->check = 0; /* and rebuild the checksum (ip needs it) */ 65 ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl); 66 67 // 构造一个sk_buff 68 rx_skb = dev_alloc_skb(skb->len + 2); 69 skb_reserve(rx_skb, 2); /* align IP on 16B boundary */ 70 memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); 71 72 /* Write metadata, and then pass to the receive level */ 73 rx_skb->dev = dev; 74 rx_skb->protocol = eth_type_trans(rx_skb, dev); 75 rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ 76 dev->stats.rx_packets++; 77 dev->stats.rx_bytes += skb->len; 78 79 // 提交sk_buff 80 netif_rx(rx_skb); 81 } 82 83 static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev) 84 { 85 static int cnt = 0; 86 printk("virt_net_send_packet cnt = %d\n", ++cnt); 87 88 /* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */ 89 netif_stop_queue(dev); /* 停止该网卡的队列 */ 90 /* ...... */ /* 把skb的数据写入网卡 */ 91 92 /* 构造一个假的sk_buff,上报 */ 93 emulator_rx_packet(skb, dev); 94 95 dev_kfree_skb (skb); /* 释放skb */ 96 netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列 */ 97 98 /* 更新统计信息 */ 99 dev->stats.tx_packets++; 100 dev->stats.tx_bytes += skb->len; 101 102 return 0; 103 } 104 105 106 static int virt_net_init(void) 107 { 108 /* 1. 分配一个net_device结构体 */ 109 vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);; /* alloc_etherdev */ 110 111 /* 2. 设置 */ 112 vnet_dev->hard_start_xmit = virt_net_send_packet; 113 114 /* 设置MAC地址 */ 115 vnet_dev->dev_addr[0] = 0x08; 116 vnet_dev->dev_addr[1] = 0x89; 117 vnet_dev->dev_addr[2] = 0x89; 118 vnet_dev->dev_addr[3] = 0x89; 119 vnet_dev->dev_addr[4] = 0x89; 120 vnet_dev->dev_addr[5] = 0x11; 121 122 /* 设置下面两项才能ping通 */ 123 vnet_dev->flags |= IFF_NOARP; 124 vnet_dev->features |= NETIF_F_NO_CSUM; 125 126 /* 3. 注册 */ 127 //register_netdevice(vnet_dev); 128 register_netdev(vnet_dev); 129 130 return 0; 131 } 132 133 static void virt_net_exit(void) 134 { 135 unregister_netdev(vnet_dev); 136 free_netdev(vnet_dev); 137 } 138 139 module_init(virt_net_init); 140 module_exit(virt_net_exit); 141 142 MODULE_AUTHOR("[email protected],[email protected]"); 143 MODULE_LICENSE("GPL");virtnet.c