说到网络,对于学过计算机网络的人来说,大概会先想到网络5层协议,但是由于一般的操作系统都集成了网络协议模块,所以网卡驱动模块跟协议模块的关系其实是微乎其微的,硬要说是在协议中的哪一部分的话,应该是在物理层,网卡驱动就是连接物理层跟其它上层协议的模块。
网卡驱动的主要功能有:
1.接收硬件传来的数据,并且向上层汇报。
当硬件接收到数据,就会发生中断,在该中断内进行数据的读取,并且复制到内存的网络缓冲区中,其余事情交由网络上层协议处理
2.提供给网络上层协议向物理层传输的接口。
当需要向外界发送数据时,需要对硬件进行操作,这时就需要驱动向上层协议提供操作硬件的接口。
3.完善网卡驱动的其他操作...
如:
1. 提供网卡的读写函数
2. 采用NAPI(当接收数据忙的时候不采用中断方式,而是用poll轮询加快数据传送)
3. 当网卡空闲一段时间后采用看门狗重启网卡
4. 进行信息统计
...
以下是基于mini2440的虚拟网卡驱动:
1 #include <linux/errno.h> 2 #include <linux/netdevice.h> 3 #include <linux/etherdevice.h> 4 #include <linux/kernel.h> 5 #include <linux/types.h> 6 #include <linux/fcntl.h> 7 #include <linux/interrupt.h> 8 #include <linux/ioport.h> 9 #include <linux/in.h> 10 #include <linux/skbuff.h> 11 #include <linux/slab.h> 12 #include <linux/spinlock.h> 13 #include <linux/string.h> 14 #include <linux/init.h> 15 #include <linux/bitops.h> 16 #include <linux/delay.h> 17 18 #include <asm/system.h> 19 #include <asm/io.h> 20 #include <asm/irq.h> 21 22 #include <linux/ip.h> 23 //#include <module.h> 24 25 static struct net_device *vnet_dev; 26 27 static void emulator_rx_packet(struct sk_buff *skb,struct net_device *dev) 28 { 29 /* 参考LDD3 */ 30 unsigned char *type; 31 struct iphdr *ih; 32 __be32 *saddr, *daddr, tmp; 33 unsigned char tmp_dev_addr[ETH_ALEN]; 34 struct ethhdr *ethhdr; 35 36 struct sk_buff *rx_skb; 37 38 // 从硬件读出/保存数据 39 /* 对调"源/目的"的mac地址 */ 40 ethhdr = (struct ethhdr *)skb->data; 41 memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN); 42 memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN); 43 memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN); 44 45 /* 对调"源/目的"的ip地址 */ 46 ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); 47 saddr = &ih->saddr; 48 daddr = &ih->daddr; 49 50 tmp = *saddr; 51 *saddr = *daddr; 52 *daddr = tmp; 53 54 //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ 55 //((u8 *)daddr)[2] ^= 1; 56 type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr); 57 //printk("tx package type = %02x\n", *type); 58 // 修改类型, 原来0x8表示ping 59 *type = 0; /* 0表示reply */ 60 61 ih->check = 0; /* and rebuild the checksum (ip needs it) */ 62 ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl); 63 64 // 构造一个sk_buff 65 rx_skb = dev_alloc_skb(skb->len + 2); 66 skb_reserve(rx_skb, 2); /* align IP on 16B boundary */ 67 memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); 68 69 /* Write metadata, and then pass to the receive level */ 70 rx_skb->dev = dev; 71 rx_skb->protocol = eth_type_trans(rx_skb, dev); 72 rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don\'t check it */ 73 dev->stats.rx_packets++; 74 dev->stats.rx_bytes += skb->len; 75 76 // 提交sk_buff 77 netif_rx(rx_skb); 78 } 79 static int virt_net_send_packet(struct sk_buff *skb,struct net_device *dev) 80 { 81 static int count=0; 82 printk("sending packet...count = %d\n",count++); 83 /*对于真实的网卡,把skb里的数据通过网卡发送出去*/ 84 netif_stop_queue(dev); /*停止该网卡的队列,表示当前正在发送数据包,缓冲区不允许写入*/ 85 /*...*/ /*把skb的数据写入网卡*/ 86 emulator_rx_packet(skb,dev); /*由于这里是虚拟网卡,我们在这里模拟一个接收到的数据包,进行上报,原本是网卡接收到数据然后才在中断程序内上报数据的*/ 87 dev_kfree_skb(skb); /*释放skb*/ 88 netif_wake_queue(dev); /*数据发送完成后会产生中断(这里省略),然后中断内唤醒队列*/ 89 90 /*完善:*/ 91 /*添加统计信息*/ 92 dev->stats.tx_packets++; 93 dev->stats.tx_bytes+=skb->len; 94 return 0; 95 } 96 97 static const struct net_device_ops virt_net_ops = { 98 .ndo_start_xmit = virt_net_send_packet, 99 }; 100 101 102 103 static int virt_net_init(void) 104 { 105 /*1.分配一个net_device结构体*/ 106 //vnet_dev= alloc_etherdev(sizeof(struct net_device)); 107 vnet_dev = alloc_netdev(0, "vnet%d", ether_setup); 108 109 /*2.设置*/ 110 vnet_dev->netdev_ops = &virt_net_ops; 111 /*设置MAC地址*/ 112 vnet_dev->dev_addr[0]=0x08; 113 vnet_dev->dev_addr[1]=0x08; 114 vnet_dev->dev_addr[2]=0x08; 115 vnet_dev->dev_addr[3]=0x08; 116 vnet_dev->dev_addr[4]=0x08; 117 vnet_dev->dev_addr[5]=0x08; 118 119 /* 设置下面两项才能ping通 */ 120 vnet_dev->flags |= IFF_NOARP; 121 vnet_dev->features |= NETIF_F_NO_CSUM; 122 123 /*3.注册*/ 124 //register_netdevice(vnet_dev); 125 register_netdev(vnet_dev); 126 127 return 0; 128 } 129 130 static void virt_net_exit(void) 131 { 132 unregister_netdev(vnet_dev); 133 free_netdev(vnet_dev); 134 } 135 136 module_init(virt_net_init); 137 module_exit(virt_net_exit); 138 139 MODULE_AUTHOR("taigacon"); 140 MODULE_LICENSE("GPL");
网卡驱动编写能分为以下几个步骤:
1. 分配net_device结构体 alloc_netdev (对于不同的网卡会分配不同的私有空间,并且会对分配函数进行封装)
2. 设置net_device,包括:
设置网卡mac地址
设置netdev_ops(编写提供上层协议传输数据的接口.ndo_start_xmit)
注册和编写中断处理函数(当硬件数据到来的时候,发出中断,中断处理程序内部读取数据,并且通知上层协议)。
3. 注册net_device结构体。
4. 硬件方面的操作。
因为要操作到硬件,而且对网卡的读写跟对内存的读写一致:都是发送命令和发送地址之后,进行数据的读写,
所以我们需要根据时序图,对网卡所在的bank进行内存控制器的设置。
一般来说,网卡产商都会提供该网卡的相应驱动,该驱动一般是以总线设备驱动框架进行编写,因此会在probe函数内部进行网卡的初始化操作(上面步骤)。
但是因为网卡产商并不知道我们的芯片型号,所以驱动其中的某些部分是不适合我们的芯片的,所以我们需要在该驱动上进行相应的修改,这就是所谓的移植网卡驱动,
修改的主要部分主要是与我们的芯片相关,其中就有芯片型号的判定,芯片内存控制器的初始化等。