网卡驱动的书写格式很简单
1.申请一个网卡设备结构体
2.设置这个结构体,硬件相关初始化
3.注册这个网卡设备
参考的韦东山老师的视屏,代码如下
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
static struct net_device *v_ndev; //定义网卡设备
static int virtual_net_init(void)
{
/* 1.分配一个net_device结构 */
v_ndev = alloc_etherdev(0);
if(!v_ndev) {
printk("alloc_etherdev fail\n");
return -ENOMEM;
}
/* 2.设置v_ndev,设置硬件等 */
/* 3. 注册网卡设备 */
register_netdev(v_ndev);
return 0;
}
static void virtual_net_exit(void)
{
unregister_netdev(v_ndev);
free_netdev(v_ndev); /* free device structure */
}
module_init(virtual_net_init);
module_exit(virtual_net_exit);
当然这个代码,在2.6.33版本内核之后,安装时会,会报错。更何况我这里用的是4.19版本的内核。
我这里贴一下报错信息。
[[email protected]]/# insmod /drivers/virnet.ko
virnet: loading out-of-tree module taints kernel.
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = (ptrval)
[00000000] *pgd=4ef5a831, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1] PREEMPT ARM
Modules linked in: virnet(O+)
CPU: 0 PID: 1167 Comm: insmod Tainted: G O 4.19.0 #12
Hardware name: Samsung S5PC110/S5PV210-based board
PC is at register_netdevice+0x88/0x5d8
LR is at 0x31687465
pc : [<804b16a0>] lr : [<31687465>] psr: 20000013
sp : 9ef41d98 ip : 0000001c fp : 80926d08
r10: 9ef10000 r9 : 9ef95924 r8 : 00000000
r7 : 7f000000 r6 : 80905048 r5 : 00000001 r4 : 00000001
r3 : 00000000 r2 : 66f97d82 r1 : 9ef41d60 r0 : 00000001
Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
Control: 10c5387d Table: 4ef70019 DAC: 00000051
Process insmod (pid: 1167, stack limit = 0x(ptrval))
Stack: (0x9ef41d98 to 0x9ef42000)
1d80: 9ef10000 00000000
1da0: 9ef268c0 804ab868 00000000 00000000 00000000 809337c0 00000001 9ef10000
1dc0: 00000001 80905048 7f000000 00000000 9ef95924 00000124 7f002000 804b1c0c
1de0: 809337c0 7f000024 00000000 80102698 9ef95a00 8012974c 80129514 00000000
1e00: 00000001 dead4ead ffffffff ffffffff 9ef41e10 7f002048 00000000 00000000
1e20: 00000012 9ef95100 9ef950c0 9f401e40 9f401e40 006000c0 7f002000 66f97d82
1e40: 807404b4 7f002000 00000001 9ef950c0 00000001 9ef95900 9ef95924 80176378
1e60: 00000001 80905048 9ef41f3c 00000001 80905048 80175348 7f00200c 00007fff
1e80: 7f002000 801729f8 80006828 00000028 a0942000 7f002048 7f0020fc 7f002174
1ea0: 806017e0 7f00200c a0942140 80905048 006002c0 9f7669c0 00000000 00000000
1ec0: 00000000 00000000 00000000 00000000 6e72656b 00006c65 00000000 00000000
1ee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
1f00: 00000000 00000000 00000000 66f97d82 8017579c 000085fb 76fe2603 00000000
1f20: a096a5fb 9ef40000 00206fc0 00000051 00000000 80175858 9f5b4d00 a09420b6
1f40: a0942140 a0942000 000285fb a0961244 a0961122 a096a4bc 00003000 000030f0
1f60: 00000000 00000000 00000000 00000460 0000001d 0000001e 00000009 00000000
1f80: 00000006 00000000 000285fb 7ec84f5b 7ec84f62 00000080 80101204 9ef40000
1fa0: 00000080 80101000 000285fb 7ec84f5b 76fba008 000285fb 00206fc0 7ec84f62
1fc0: 000285fb 7ec84f5b 7ec84f62 00000080 00000000 00000000 00000000 00000000
1fe0: 7ec84ce8 7ec84cd8 00021b10 00009410 20000010 76fba008 00000000 00000000
[<804b16a0>] (register_netdevice) from [<804b1c0c>] (register_netdev+0x1c/0x34)
[<804b1c0c>] (register_netdev) from [<7f000024>] (virturl_net_init+0x24/0x2c [virnet])
[<7f000024>] (virturl_net_init [virnet]) from [<80102698>] (do_one_initcall+0x50/0x19c)
[<80102698>] (do_one_initcall) from [<80176378>] (do_init_module+0x60/0x1d8)
[<80176378>] (do_init_module) from [<80175348>] (load_module+0x1c6c/0x205c)
[<80175348>] (load_module) from [<80175858>] (sys_init_module+0x120/0x160)
[<80175858>] (sys_init_module) from [<80101000>] (ret_fast_syscall+0x0/0x54)
Exception stack(0x9ef41fa8 to 0x9ef41ff0)
1fa0: 000285fb 7ec84f5b 76fba008 000285fb 00206fc0 7ec84f62
1fc0: 000285fb 7ec84f5b 7ec84f62 00000080 00000000 00000000 00000000 00000000
1fe0: 7ec84ce8 7ec84cd8 00021b10 00009410
Code: ebffe39b e2504000 ba000008 e59a3120 (e5933000)
---[ end trace 5632e5b563c2656b ]---
Segmentation fault
[[email protected]]/#
错误信息也很明显,在register_netdevice中访问了NULL地址。
简单分析代码,后发现,是在红框中,因为我们没绑定net_devops,所以net_devops指针默认是NULL的。使用这个NULL再做深一层的引用,肯定会发生段错误。
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
static struct net_device *v_ndev;
static const struct net_device_ops v_nops = {
};
static int virturl_net_init(void)
{
/* 1.分配一个net_device结构 */
v_ndev = alloc_etherdev(0);
if(!v_ndev) {
printk("alloc_etherdev fail\n");
return -1;
}
/* 2.设置v_ndev,设置硬件等 */
v_ndev->netdev_ops = &v_nops;
/* 3. 注册网卡设备 */
register_netdev(v_ndev);
return 0;
}
static void virturl_net_exit(void)
{
unregister_netdev(v_ndev);
free_netdev(v_ndev); /* free device structure */
}
module_init(virturl_net_init);
module_exit(virturl_net_exit);
当然这次是可以安装驱动了,但配置ip的时候却出错了。
出错信息如下:
[[email protected]]/#
[[email protected]]/# insmod /drivers/virnet.ko
[[email protected]]/#
[[email protected]]/#
[[email protected]]/# ifconfig eth1 3.3.3.3
[[email protected]]/# Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = 553bf097
[00000000] *pgd=00000000
Internal error: Oops: 80000007 [#1] PREEMPT ARM
Modules linked in: virnet(O) [last unloaded: virnet]
CPU: 0 PID: 0 Comm: swapper Tainted: G O 4.19.0 #12
Hardware name: Samsung S5PC110/S5PV210-based board
PC is at (null)
LR is at dev_hard_start_xmit+0x88/0x118
pc : [<00000000>] lr : [<804ad40c>] psr: 60000113
sp : 80901c50 ip : 7f008024 fp : 80912cf0
r10: 9eef4a80 r9 : 80905ccc r8 : 809058c4
r7 : 9ef77180 r6 : 00000000 r5 : 9eeff800 r4 : 00000000
r3 : 9eeff850 r2 : 00000000 r1 : 9eeff800 r0 : 9eef4a80
Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
Control: 10c5387d Table: 4ef90019 DAC: 00000051
Process swapper (pid: 0, stack limit = 0xe057334f)
Stack: (0x80901c50 to 0x80902000)
1c40: 9eeff850 80901c84 00000000 00000000
1c60: 00000000 9f599400 9eef4a80 9eeff800 9ef77180 00000000 00000001 804d9858
1c80: 00425c00 00000010 9f599400 00000001 00000040 9f599440 9eef4a80 00000000
1ca0: 9f599400 804d9b28 00000000 00000001 9eef4a80 805c756c 80900000 9f5994a4
1cc0: 804d8b68 00000000 9ef77180 9eef4a80 00000000 00000000 3e000000 9f599400
1ce0: 9eeff800 804ad9a8 804bebf8 805c7900 00000006 804bebf8 00000000 80927260
1d00: 9efc8678 00000000 000086dd 004d8318 fffffff4 00000000 9efc8600 9eeff800
1d20: 00000000 804bdb98 00000000 9eef4a80 9eccfa80 16000000 00000000 9efc8600
1d40: 3e000000 a77bcaff 00000006 8054a644 9eccfa80 80926d08 000005dc 0000001c
1d60: 9eef1010 9eef4a80 80926d08 80905048 9efbae00 00000001 9eef1040 80573574
1d80: 9eef1028 00000005 00000005 00000000 00000000 003a0000 00000000 00000000
1da0: 00000000 00000000 00000000 00000000 000002ff 00000000 00000000 16000000
1dc0: 00000000 00000000 00000000 00000000 00000000 0000008f 00000000 00000000
1de0: 20000193 3000ebda 20000193 00000000 9eef4a80 00000000 9ef1dbd0 9efbae4c
1e00: 00000001 00000001 9efbae14 80574040 00000001 00000000 9efbae78 9efbae00
1e20: 0000012b 00000101 80573e7c 80901e80 00000200 00000000 80573e7c 00000001
1e40: 00000101 8015d2c4 80912d40 00000000 9efbae4c 9efbae4c 80911bc0 8015d718
1e60: 80901e80 80911bc0 00000001 80901e7c 80911bc0 80912cf0 40000001 8015d90c
1e80: 00000000 8092e7ac 00000001 8047b07c 809265c0 9f409100 00000000 8014bf7c
1ea0: 00000080 80933fac 80933fa8 80900000 80933fa8 80102218 00000000 00000001
1ec0: 9f406800 8090c818 0000000a 8092e540 80912cf0 00018107 806007e8 00200102
1ee0: 8033a9f0 8092dd80 00000000 00000000 00000001 9f406800 80900000 80825a40
1f00: 9fffcbc0 8011a884 00000091 8014b980 80967df8 80901f40 00000000 80967df8
1f20: 00000000 8033abd4 801086a4 60000013 ffffffff 80901f74 809337c0 80101a0c
1f40: 00000000 0000c788 80901f98 80112c80 80900000 809050a8 8092e78c 00000001
1f60: 809337c0 00000001 80825a40 9fffcbc0 00002b10 80901f90 801086a0 801086a4
1f80: 60000013 ffffffff 00000051 00000000 00000000 80139fe4 9f4311e1 8093381c
1fa0: 809337c0 ffffffff 80905040 8013a2e8 9f4311e1 80800ca4 ffffffff ffffffff
1fc0: 00000000 80800670 80825a40 3004f8da 00000000 80800330 00000051 10c03c7d
1fe0: 00000998 3fff7000 412fc082 10c53c7d 00000000 00000000 00000000 00000000
[<804ad40c>] (dev_hard_start_xmit) from [<804d9858>] (sch_direct_xmit+0xfc/0x29c)
[<804d9858>] (sch_direct_xmit) from [<804d9b28>] (__qdisc_run+0x130/0x4e4)
[<804d9b28>] (__qdisc_run) from [<804ad9a8>] (__dev_queue_xmit+0x464/0x708)
[<804ad9a8>] (__dev_queue_xmit) from [<8054a644>] (ip6_finish_output2+0x1e8/0x4d8)
[<8054a644>] (ip6_finish_output2) from [<80573574>] (mld_sendpack+0x1a4/0x2fc)
[<80573574>] (mld_sendpack) from [<80574040>] (mld_ifc_timer_expire+0x1c4/0x2cc)
[<80574040>] (mld_ifc_timer_expire) from [<8015d2c4>] (call_timer_fn+0x20/0x98)
[<8015d2c4>] (call_timer_fn) from [<8015d718>] (expire_timers+0x88/0x94)
[<8015d718>] (expire_timers) from [<8015d90c>] (run_timer_softirq+0x9c/0x16c)
[<8015d90c>] (run_timer_softirq) from [<80102218>] (__do_softirq+0xd8/0x24c)
[<80102218>] (__do_softirq) from [<8011a884>] (irq_exit+0xd0/0xdc)
[<8011a884>] (irq_exit) from [<8014b980>] (__handle_domain_irq+0x58/0xa4)
[<8014b980>] (__handle_domain_irq) from [<8033abd4>] (vic_handle_irq+0x5c/0x98)
[<8033abd4>] (vic_handle_irq) from [<80101a0c>] (__irq_svc+0x6c/0xa8)
Exception stack(0x80901f40 to 0x80901f88)
1f40: 00000000 0000c788 80901f98 80112c80 80900000 809050a8 8092e78c 00000001
1f60: 809337c0 00000001 80825a40 9fffcbc0 00002b10 80901f90 801086a0 801086a4
1f80: 60000013 ffffffff
[<80101a0c>] (__irq_svc) from [<801086a4>] (arch_cpu_idle+0x38/0x3c)
[<801086a4>] (arch_cpu_idle) from [<80139fe4>] (do_idle+0x88/0xe8)
[<80139fe4>] (do_idle) from [<8013a2e8>] (cpu_startup_entry+0xc/0x10)
[<8013a2e8>] (cpu_startup_entry) from [<80800ca4>] (start_kernel+0x3e0/0x3ec)
Code: bad PC value
---[ end trace c10bb536d34c237e ]---
Kernel panic - not syncing: Fatal exception in interrupt
---[ end Kernel panic - not syncing: Fatal exception in interrupt ]---
问题也比较明显,dev_hard_start_xmit函数出错了。
进去查找一下可能出错的地方。
struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
struct sk_buff *skb = first;
int rc = NETDEV_TX_OK;
while (skb) {
struct sk_buff *next = skb->next;
skb->next = NULL;
rc = xmit_one(skb, dev, txq, next != NULL); //这里肯定要执行
if (unlikely(!dev_xmit_complete(rc))) {
skb->next = next;
goto out;
}
skb = next;
if (netif_xmit_stopped(txq) && skb) {
rc = NETDEV_TX_BUSY;
break;
}
}
out:
*ret = rc;
return skb;
}
static int xmit_one(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, bool more)
{
unsigned int len;
int rc;
if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all))
dev_queue_xmit_nit(skb, dev);
len = skb->len;
trace_net_dev_start_xmit(skb, dev);
rc = netdev_start_xmit(skb, dev, txq, more); //执行发送函数
trace_net_dev_xmit(skb, rc, dev, len);
return rc;
}
static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, bool more)
{
const struct net_device_ops *ops = dev->netdev_ops; //获取到ops
int rc;
rc = __netdev_start_xmit(ops, skb, dev, more);
if (rc == NETDEV_TX_OK)
txq_trans_update(txq);
return rc;
}
static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,
struct sk_buff *skb, struct net_device *dev,
bool more)
{
skb->xmit_more = more ? 1 : 0;
return ops->ndo_start_xmit(skb, dev); //执行ndo_start_xmit函数
}
很明显我们没设置ndo_start_xmit函数,肯定又段错误了。
这里我们给一个空的ndo_start_xmit函数。看一下
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
static struct net_device *v_ndev;
netdev_tx_t vnet_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
return NETDEV_TX_OK;
}
static const struct net_device_ops v_nops = {
.ndo_start_xmit = vnet_start_xmit,
};
static int virturl_net_init(void)
{
/* 1.分配一个net_device结构 */
v_ndev = alloc_etherdev(0);
if(!v_ndev) {
printk("alloc_etherdev fail\n");
return -1;
}
/* 2.设置v_ndev,设置硬件等 */
v_ndev->netdev_ops = &v_nops;
/* 3. 注册网卡设备 */
register_netdev(v_ndev);
return 0;
}
static void virturl_net_exit(void)
{
unregister_netdev(v_ndev);
free_netdev(v_ndev); /* free device structure */
}
module_init(virturl_net_init);
module_exit(virturl_net_exit);
这次安装,配置都ip都ok。ping 自己也ok
[[email protected]]/# insmod /drivers/virnet.ko
virnet: loading out-of-tree module taints kernel.
[[email protected]]/#
[[email protected]]/#
[[email protected]]/#
[[email protected]]/#
[[email protected]]/# ifconfig
eth0 Link encap:Ethernet HWaddr 00:00:DE:AD:BE:EF
inet addr:192.168.0.20 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::200:deff:fead:beef/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1943 errors:0 dropped:0 overruns:0 frame:0
TX packets:588 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1917158 (1.8 MiB) TX bytes:91264 (89.1 KiB)
Interrupt:144 Base address:0x7000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
[[email protected]]/# ifconfig eth1 6.6.6.6
[[email protected]]/#
[[email protected]]/# ifconfig
eth0 Link encap:Ethernet HWaddr 00:00:DE:AD:BE:EF
inet addr:192.168.0.20 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::200:deff:fead:beef/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2038 errors:0 dropped:0 overruns:0 frame:0
TX packets:620 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1990672 (1.8 MiB) TX bytes:98224 (95.9 KiB)
Interrupt:144 Base address:0x7000
eth1 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:6.6.6.6 Bcast:6.255.255.255 Mask:255.0.0.0
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
[[email protected]]/#
[[email protected]]/# ping 6.6.6.6
PING 6.6.6.6 (6.6.6.6): 56 data bytes
64 bytes from 6.6.6.6: seq=0 ttl=64 time=0.477 ms
64 bytes from 6.6.6.6: seq=1 ttl=64 time=0.257 ms
64 bytes from 6.6.6.6: seq=2 ttl=64 time=0.257 ms
64 bytes from 6.6.6.6: seq=3 ttl=64 time=0.253 ms
64 bytes from 6.6.6.6: seq=4 ttl=64 time=0.251 ms
64 bytes from 6.6.6.6: seq=5 ttl=64 time=0.249 ms
64 bytes from 6.6.6.6: seq=6 ttl=64 time=0.248 ms
64 bytes from 6.6.6.6: seq=7 ttl=64 time=0.251 ms
^C
--- 6.6.6.6 ping statistics ---
8 packets transmitted, 8 packets received, 0% packet loss
round-trip min/avg/max = 0.248/0.280/0.477 ms
[[email protected]]/#
当然ping 同一网段其它网卡就不行了。
[[email protected]]/# ping 6.6.6.7
PING 6.6.6.7 (6.6.6.7): 56 data bytes
^C
--- 6.6.6.7 ping statistics ---
6 packets transmitted, 0 packets received, 100% packet loss
继续完善,在上面发包函数的基础上,增加自己被调用的次数。
netdev_tx_t vnet_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
static int cnt;
printk(KERN_INFO"vnet_start_xmit %d\n", ++cnt);
return NETDEV_TX_OK;
}
可以看到配置自己ip和ping 同网段的其它设备都要调用自己发送数据包。
[[email protected]]/# ifconfig eth1 6.6.6.6
[[email protected]]/# vnet_start_xmit 1
vnet_start_xmit 2
vnet_start_xmit 3
vnet_start_xmit 4
vnet_start_xmit 5
vnet_start_xmit 6
vnet_start_xmit 7
vnet_start_xmit 8
[[email protected]]/#
[[email protected]]/# ping 6.6.6.7
vnet_start_xmit 9
PING 6.6.6.7 (6.vnet_start_xmit 10
6.6.7): 56 data bytes
vnet_start_xmit 11
vnet_start_xmit 12
vnet_start_xmit 13
vnet_start_xmit 14
vnet_start_xmit 15
vnet_start_xmit 16
vnet_start_xmit 17
vnet_start_xmit 18
vnet_start_xmit 19
vnet_start_xmit 20
vnet_start_xmit 21
^C
--- 6.6.6.7 ping statistics ---
15 packets transmitted, 0 packets received, 100% packet loss
[[email protected]]/# ping 6.6.6.6
PING 6.6.6.6 (6.6.6.6): 56 data bytes
64 bytes from 6.6.6.6: seq=0 ttl=64 time=3.346 ms
64 bytes from 6.6.6.6: seq=1 ttl=64 time=0.256 ms
64 bytes from 6.6.6.6: seq=2 ttl=64 time=0.252 ms
64 bytes from 6.6.6.6: seq=3 ttl=64 time=0.248 ms
64 bytes from 6.6.6.6: seq=4 ttl=64 time=0.249 ms
^C
--- 6.6.6.6 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.248/0.870/3.346 ms
[[email protected]]/#
网卡对数据包统计的统计和mac地址都是没有的。
每个网卡设备,都有一个对设备状态的统计,如下:
struct net_device {
......
struct net_device_stats stats;
......
};
/*
* Old network device statistics. Fields are native words
* (unsigned long) so they can be read and written atomically.
*/
struct net_device_stats {
unsigned long rx_packets;
unsigned long tx_packets;
unsigned long rx_bytes;
unsigned long tx_bytes;
unsigned long rx_errors;
unsigned long tx_errors;
unsigned long rx_dropped;
unsigned long tx_dropped;
unsigned long multicast;
unsigned long collisions;
unsigned long rx_length_errors;
unsigned long rx_over_errors;
unsigned long rx_crc_errors;
unsigned long rx_frame_errors;
unsigned long rx_fifo_errors;
unsigned long rx_missed_errors;
unsigned long tx_aborted_errors;
unsigned long tx_carrier_errors;
unsigned long tx_fifo_errors;
unsigned long tx_heartbeat_errors;
unsigned long tx_window_errors;
unsigned long rx_compressed;
unsigned long tx_compressed;
};
netdev_tx_t vnet_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
static int cnt;
printk(KERN_INFO"vnet_start_xmit %d\n", ++cnt);
/* 对于真实的网,这个函数里面就是对通过网卡来发送数据包 */
/* 统计发生信息 */
dev->stats.tx_packets ++;
dev->stats.tx_bytes += skb->len;
return NETDEV_TX_OK;
}
static int virtual_net_init(void)
{
/* 分配一个net_device结构 */
v_ndev = alloc_etherdev(0);
if(!v_ndev) {
printk("alloc_etherdev fail\n");
return -1;
}
v_ndev->netdev_ops = &v_nops;
/* 设置mac地址 */
v_ndev->dev_addr[0] = 0x08;
v_ndev->dev_addr[1] = 0x89;
v_ndev->dev_addr[2] = 0x89;
v_ndev->dev_addr[3] = 0x89;
v_ndev->dev_addr[4] = 0x89;
v_ndev->dev_addr[5] = 0x89;
register_netdev(v_ndev);
return 0;
}
经过上面设置之后的调试信息如下
[[email protected]]/# insmod /drivers/virt_net.ko
[[email protected]]/# ifconfig eth1 8.8.8.8
[[email protected]]/# vnet_start_xmit 1
vnet_start_xmit 2
vnet_start_xmit 3
vnet_start_xmit 4
vnet_start_xmit 5
vnet_start_xmit 6
vnet_start_xmit 7
vnet_start_xmit 8
vnet_start_xmit 9
vnet_start_xmit 10
vnet_start_xmit 11
[[email protected]]/#
下面这个是一个网卡驱动的伪代码
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
/* 网卡设备 */
static struct net_device *v_ndev;
/* 物理网卡发送数据 */
static void device_send_packet(struct sk_buff *skb, struct net_device *dev)
{
}
/* 物理网卡接收函数 */
static void device_rx(struct net_device *dev)
{
unsigned int rx_bytes;
struct sk_buff *skb;
/* 获取接收长度 */
/* ......... */ //rx_bytes = read_rx_len();
/* 获取一个skb */
skb = netdev_alloc_skb(dev, rx_bytes + 4);
if(!skb) {
dev_err(&dev->dev,"netdev_alloc_skb no memoey\n");
return;
}
/* 把数据保存在skb中,并设置好数据长度 */
/* ........ */
dev->stats.rx_bytes ++;
/* 接收数据上报 */
netif_rx(skb);
}
#define NET_TX_OK 1
#define NET_RX_OK 2
/* 网卡中断函数 */
static irqreturn_t device_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
int status = 0;
/* 读网卡中断状态寄存器 */
//status =
/* 判断是否为接收中断 */
if(status & NET_RX_OK) {
device_rx(dev);
}
/* 判断是否为发送中断 */
if(status & NET_TX_OK) {
/* 唤醒队列 */
netif_wake_queue(dev);
}
return IRQ_HANDLED;
}
netdev_tx_t vnet_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
// static int cnt;
// printk(KERN_INFO"vnet_start_xmit %d\n", ++cnt);
/* 对于真实的网,这个函数里面就是对通过网卡来发送数据包 */
/* 停止上层对vnet_start_xmit函数的调用 */
netif_stop_queue(dev);
/* 把skb中的数据写入真实的网卡中 */
device_send_packet(dev, skb);
/* 释放skb */
dev_consume_skb_any(skb);
/* 统计网卡发生的信息 */
dev->stats.tx_packets ++;
dev->stats.tx_bytes += skb->len;
return NETDEV_TX_OK;
}
static const struct net_device_ops v_nops = {
.ndo_start_xmit = vnet_start_xmit,
};
static int virtual_net_init(void)
{
/* 分配一个net_device结构 */
v_ndev = alloc_etherdev(0);
if(!v_ndev) {
printk("alloc_etherdev fail\n");
return -1;
}
v_ndev->netdev_ops = &v_nops;
v_ndev->dev_addr[0] = 0x08;
v_ndev->dev_addr[1] = 0x89;
v_ndev->dev_addr[2] = 0x89;
v_ndev->dev_addr[3] = 0x89;
v_ndev->dev_addr[4] = 0x89;
v_ndev->dev_addr[5] = 0x89;
/* 注册网卡接收终端函数 */
// if (request_irq(dev->irq, device_interrupt, irq_flags, dev->name, dev))
// return -EAGAIN;
register_netdev(v_ndev);
return 0;
}
static void virtual_net_exit(void)
{
unregister_netdev(v_ndev);
free_netdev(v_ndev); /* free device structure */
}
module_init(virtual_net_init);
module_exit(virtual_net_exit);
MODULE_LICENSE("GPL");
因为这个是虚拟的网卡,所以网卡中断函数肯定不能注册。但网卡驱动程序的主要架构就是上面这样。
细节部分都是和网卡设备的硬件有关系的,这个就不属于框架知识了,只需要拿到网卡手册和demo历程修改即可。