网卡驱动的书写格式很简单

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再做深一层的引用,肯定会发生段错误。

从零开始之驱动发开、linux驱动(四十四、虚拟网卡驱动)

 


#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]]/# 

 

从零开始之驱动发开、linux驱动(四十四、虚拟网卡驱动)

网卡对数据包统计的统计和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]]/# 

 

从零开始之驱动发开、linux驱动(四十四、虚拟网卡驱动)

 

 

 

下面这个是一个网卡驱动的伪代码


#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历程修改即可。

 

相关文章: