网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下。

 

Linux系统对网络设备驱动定义了四个层次:

1. 网络协议接口层:用于实现统一的数据包收发的协议,通过dev_queue_xmit()函数发送数据, 通过netif_rx()函数接收数据

2. 网络设备接口层:通过struct net_device来描述一个具体的网络设备的信息

3. 设备驱动功能层:同于驱动网络设备实现各个功能,通过hard_start_xmit()函数发送数据,通过网络设备上的中断函数接收数据

4. 网络设备与媒介层:用于负责完成数据包发送和接收的物理实体

层次结构如下图(此图基于Linux kernel 4.0,个别函数不适用于kernel 3.5):

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

 

在设计具体的网络设备驱动程序时,我们需要完成的主要工作是编写设备驱动功能层的函数填充struct net_device并将其注册入内核。struct net_device定义如下:

struct net_device {
    char            name[IFNAMSIZ];
...
    unsigned long        mem_end;    /* 内存结束地址        */
    unsigned long        mem_start;  /* 内存开始地址        */
    unsigned long        base_addr;  /* 内存I/O基地址    */
    unsigned int        irq;         /* 中断号            */
...
    struct net_device_stats    stats;             /* 用于保存统计信息    */
    
    const struct net_device_ops *netdev_ops;      /* 网络设备操作函数 */
...
    unsigned int        mtu;                      /* 最大数据包        */
    unsigned short        type;                   /* 接口硬件类型        */
    unsigned short        hard_header_len;        /* hardware hdr length,一般赋值为ETH_HLEN    */
...
    unsigned char        perm_addr[MAX_ADDR_LEN]; /* 设备MAC地址可以通过ifconfig看到 */
...
    /* Called from unregister, can be used to call free_netdev */
    void (*destructor)(struct net_device *dev);
...
};

其中,

1. struct net_device_stats定义如下:

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;           /* 发送的错误数据包数 */
...
};

2. struct net_device_ops定义如下:

struct net_device_ops {
    int            (*ndo_init)(struct net_device *dev);
    void        (*ndo_uninit)(struct net_device *dev);
    int            (*ndo_open)(struct net_device *dev);
    int            (*ndo_stop)(struct net_device *dev);
    /* 数据包发送函数,也就是图中的hard_start_xmit() */
    netdev_tx_t    (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);
...
    void        (*ndo_set_rx_mode)(struct net_device *dev);
    int            (*ndo_set_mac_address)(struct net_device *dev, void *addr);
...
    /* 发送超时函数 */
    void        (*ndo_tx_timeout) (struct net_device *dev);
..
    struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
...
};

在数据包发送函数中,我们可以知道数据包是struct sk_buff(socket buffer,套接字缓冲区),此结构体定义如下:

struct sk_buff {
    struct sk_buff        *next;    /* 双向链表 */
    struct sk_buff        *prev;
...
    unsigned int        len,        /* 数据包大小 */
                data_len;           /* 数据大小 */
    __u16            mac_len,       /* MAC包大小 */
                hdr_len;            /* 帧大小 */
...
    __u32            priority;      /* 优先级 */
...
    __be16            protocol;     /* 存放上层的协议类型,可通过eth_type_trans()获取 */
...
    sk_buff_data_t        transport_header;    /* 传输层头部的偏移值 */
    sk_buff_data_t        network_header;      /* 网络层头部的偏移值 */
    sk_buff_data_t        mac_header;          /* MAC数据链路层头部的偏移值 */
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t        tail;     /* 数据包的尾部 */
    sk_buff_data_t        end;      /* 尾部空间的尾部 */
    unsigned char        *head,     /* 头部空间的头部 */
                *data;              /* 数据包的头部 */
...
};

此结构体数据存放格式如下图:

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

其中头部空间用于存放协议类型,尾部空间用于存储应用层数据。数据包格式如下:

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

 

 

分析完了各个结构体,现在我们通过分析具体文件确定结构体的注册和初始化方式。我选用的设备驱动文件为/drivers/net/cs89x0.c。

文件链接:

https://files.cnblogs.com/files/Lioker/20_cs89x0.zip

二、网络设备驱动分析

在此不再一个一个函数分析,直接给出调用关系:

int __init cs89x0_init(void)
  -> platform_driver_probe(&cs89x0_driver, cs89x0_platform_probe);
    -> struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
      -> alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
        -> setup(dev);                       /* 调用传入的ether_setup()设置net_device成员 */
        -> netif_alloc_netdev_queues(dev)    /* 分配接收队列和发送队列 */ 
    -> cs89x0_probe1(dev, virt_addr, 0);
      -> iowrite16(PP_ChipID, ioaddr + ADD_PORT);    /* 设置寄存器 */
      -> dev->netdev_ops    = &net_ops;              /* net_device操作函数 */
      -> register_netdev(dev);                       /* 注册net_device */
        -> register_netdevice(dev);
          -> netdev_register_kobject(dev);
            -> device_add(dev);

init()函数所做的有以下几点

1. 使用alloc_etherdev()分配struct net_device

2. 设置网卡硬件相关寄存器

3. 设置struct net_device的成员

4. 使用register_netdev()注册struct net_device

 

接下来,我们来看net_device操作函数net_ops的数据包发送函数。

static const struct net_device_ops net_ops = {
...
    .ndo_start_xmit        = net_send_packet,
...
};

此函数调用关系如下:

static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
  -> netif_stop_queue(dev);      /* 停止上层传下来的数据包 */
    -> set_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state);
  -> iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);                /* 设置寄存器,初始化传输队列 */
  -> writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);   /* 发送包 */
    -> iowrite16(tmp16, lp->virt_addr + portno);
  -> dev->stats.tx_bytes += skb->len;
  -> dev_kfree_skb(skb);        /* 释放sk_buff */

 

现在出现一个问题,在ndo_start_xmit()有停止函数netif_stop_queue(),但没有唤醒函数,因此我们可以在文件中搜索“netif_”查找唤醒函数。

在net_timeout()和net_interrupt()中查找到了netif_wake_queue(),net_timeout定义在net_ops中,那么net_interrupt()又是在哪里注册的呢?

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

经搜索后,确定net_interrupt()在net_open()中注册。

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

 

也就是数据包发送成功后,会产生中断,调用net_interrupt()唤醒上层继续传输数据包

因此,ndo_start_xmit()函数所做的有以下几点

1. 数据包发送之前,使用netif_stop_queue()停止上层传输数据包

2. 设置寄存器,通过网络设备硬件发送数据包

3. 当数据包发送后,使用dev_kfree_skb()释放struct sk_buff

4. 当数据包发送后,会产生中断,调用net_interrupt()唤醒上层继续传输数据包并更新net_device_stats数据

5. 若数据包发送超时,会调用net_timeout()唤醒上层继续传输数据包

 

分析完初始化和发包过程,接下来需要分析收包过程。

收包主要通过中断函数net_interrupt()处理,函数内部判断中断类型,若为ISQ_RECEIVER_EVENT,表示为接收中断,调用net_rx()将数据包传输给上层。中断函数net_interrupt()定义如下:

20、网卡框架分析、虚拟网卡驱动和DM9621驱动分析
 1 static irqreturn_t net_interrupt(int irq, void *dev_id)
 2 {
 3     struct net_device *dev = dev_id;
 4     struct net_local *lp;
 5     int status;
 6     int handled = 0;
 7 
 8     lp = netdev_priv(dev);
 9 ...
10     while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
11         cs89_dbg(4, debug, "%s: event=%04x\n", dev->name, status);
12         handled = 1;
13         switch (status & ISQ_EVENT_MASK) {
14         case ISQ_RECEIVER_EVENT:
15             /* 接包 */
16             net_rx(dev);
17             break;
18         case ISQ_TRANSMITTER_EVENT:
19             dev->stats.tx_packets++;
20             netif_wake_queue(dev);    /* 唤醒 */
21             /* 更新net_device_stats数据 */
22             if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) {
23                 if ((status & TX_OK) == 0)
24                     dev->stats.tx_errors++;
25                 if (status & TX_LOST_CRS)
26                     dev->stats.tx_carrier_errors++;
27                 if (status & TX_SQE_ERROR)
28                     dev->stats.tx_heartbeat_errors++;
29                 if (status & TX_LATE_COL)
30                     dev->stats.tx_window_errors++;
31                 if (status & TX_16_COL)
32                     dev->stats.tx_aborted_errors++;
33             }
34             break;
35 ...
36             break;
37         case ISQ_RX_MISS_EVENT:
38             dev->stats.rx_missed_errors += (status >> 6);
39             break;
40         case ISQ_TX_COL_EVENT:
41             dev->stats.collisions += (status >> 6);
42             break;
43         }
44     }
45     return IRQ_RETVAL(handled);
46 }
View Code

相关文章:

  • 2021-11-24
  • 2022-12-23
  • 2021-09-05
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-12-03
猜你喜欢
  • 2021-12-09
  • 2021-11-11
  • 2022-02-05
  • 2022-12-23
  • 2022-03-14
  • 2022-12-23
  • 2021-12-03
相关资源
相似解决方案