网卡驱动程序框架:
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 #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 #include <linux/ip.h>
18
19 #include <asm/system.h>
20 #include <asm/io.h>
21 #include <asm/irq.h>
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
来源:https://www.cnblogs.com/CZM-/p/5292835.html