网卡驱动程序框架:
app: socket
--------------------------------------------------
---------------
--------------- 若干层网络协议--纯软件 ---------------
--------------- hard_start_xmit|| /\
\/ || netif_rx sk_buff
--------------- 硬件相关的驱动程序(要提供hard_start_xmit, 有数据时要用netif_rx上报)
--------------------------------------------------
硬件 怎么写网卡驱动程序?
1. 分配一个net_device结构体
2. 设置: 2.1 发包函数: hard_start_xmit
2.2 收到数据时(在中断处理函数里)用netif_rx上报数据
2.3 其他设置
3. 注册: register_netdevice
测试1th/2th:
1. insmod virt_net.ko
2. ifconfig vnet0 3.3.3.3 ifconfig // 查看
3. ping 3.3.3.3 // 成功 ping 3.3.3.4 // 死机
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include in.h> 10 #include 11 #include 12 #include 13 #include string.h> 14 #include 15 #include 16 #include 17 #include 18 19 #include 20 #include 21 #include 22 23 24 static struct net_device *vnet_dev; 25 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 80 static int vir_net_send_packet (struct sk_buff *skb, 81 struct net_device *dev) 82 { 83 static int cnt = 0; 84 printk("vir_net_send_packet cnt = %d \n",++cnt); 85 /* 对于真是的网卡,把skb里的数据通过网卡发送出去 */ 86 netif_stop_queue(dev); /* 停止网卡的队列 */ 87 /*............*/ /* 把skb数据写入网卡 */ 88 89 /* 构造一个假的sk_buff,上报 */ 90 emulator_rx_packet(skb, dev); 91 92 dev_kfree_skb (skb); /*释放skb*/ 93 netif_wake_queue(dev); /*数据发送完,唤醒网卡*/ 94 95 /* 更新统计信息 */ 96 dev->stats.tx_packets++; 97 dev->stats.tx_bytes += skb->len; 98 99 return 0; 100 } 101 102 static int vir_net_init(void) 103 { 104 /* 分配一个net_device结构体 */ 105 vnet_dev = alloc_netdev(0, "vnet%d", ether_setup); 106 107 /* 设置 */ 108 vnet_dev->hard_start_xmit = vir_net_send_packet; 109 /* 设置mac地址 */ 110 111 vnet_dev->dev_addr[0] = vnet_dev->perm_addr[0] = 1 >> 24; 112 vnet_dev->dev_addr[1] = vnet_dev->perm_addr[1] = 1 >> 16; 113 vnet_dev->dev_addr[2] = vnet_dev->perm_addr[2] = 1 >> 8; 114 vnet_dev->dev_addr[3] = vnet_dev->perm_addr[3] = 1; 115 vnet_dev->dev_addr[4] = vnet_dev->perm_addr[4] = 1 >> 8; 116 vnet_dev->dev_addr[5] = vnet_dev->perm_addr[5] = 1; 117 118 /* 设置下面两项才能ping通 */ 119 vnet_dev->flags |= IFF_NOARP; 120 vnet_dev->features |= NETIF_F_NO_CSUM; 121 122 123 /* 注册 */ 124 register_netdev(vnet_dev); 125 126 return 0; 127 } 128 129 static void vir_net_exit(void) 130 { 131 unregister_netdev(vnet_dev); 132 free_netdev(vnet_dev); 133 134 } 135 module_init(vir_net_init); 136 module_exit(vir_net_exit); 137 MODULE_LICENSE("GPL");
测试DM9000C驱动程序:
1. 把dm9dev9000c.c放到内核的drivers/net目录下
2. 修改drivers/net/Makefile 把 obj-$(CONFIG_DM9000) += dm9000.o 改为 obj-$(CONFIG_DM9000) += dm9dev9000c.o
3. make uImage 使用新内核启动
4. 使用NFS启动 或 ifconfig eth0 192.168.1.17 ping 192.168.1.1