Linux系统对网络设备驱动定义了4各层次:网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层,网络设备与媒介层
16.1 Linux网络设备驱动的结构
(1)网络协议接口层:提供统一的数据包收发接口,不论上层协为ARP还是IP,都通过dev_queue_xmit()函数发送数据,通过netif_rx()函数接受数据。
(2)网络设备接口层:提供统一的用于描述网络设备属性和操作的结构体net_device
(3)设备驱动功能层:设置net_device数据结构体具体成员。通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断出发接受操作。
(4)网络设备与媒介层:是晚餐数据包发送和接受的物理网络设备。包括网络适配器和具体的传输媒介。网络适配器被驱动功能层中的函数物理上驱动。对Linux该层可以是虚拟的
16.1.1 网络协议接口层
网络协议接口层最主要的功能是给上层协议提供来透明的数据包发送和接受接口。当上层ARP或IP需要发送数据包时,它将掉同网络协议接口层的dev_queue_xmit()函数发送该数据包。同行死需要传递给该函数一个指向struct sk_buff数据结构体的指针。
dev_queue_xmit(struct sk_buff *skb);
int netif_rx(struct sk_buff *skb)
struct sk_buff定义在inclue/linux/skbuf.h文件中,称为套接字缓冲区
当发送数据包时,Linux内核的网络处理模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff递交给下层,各层在sk_buff中添加不同的协议头直到交给网络设备发送。
当网络设备从网络媒介上接受到数据包后,它必须将接受到的数据转换位sk_buff数据结构并传递给上层,各层剥去相应的协议头直到交给用户。
16.2.2 网络接口层
struct net_device 定义在 include/linux/ntedevice.h 中。
(1)全局信息:
char name[IFNAMESIZE]; //网络设备名称
int (*init)(struct net_device *dev); //设备初始化
(2)硬件信息:
unsigned long mem_end;
unsigned long mem_start; //设备所使用的共享内存的起始和结束地址。
unsigned long base_addr; //网络设备IO基地址
unsigned char irq; //设备所使用的中断号
unsigned char if_port; //指定多端口设备使用哪一个端口。例如如果同时支持IF_PORT_10BASE2(同轴电缆)和IF_PORT_10BASET(双绞线)
unsigned char dma; //分配各设备的DMA通道
(3)接口信息:
unsigned short hard_header_len; //网络设备的硬件头部长度,在以太网的初始换函数中,被赋值为ETH_HLEN,即14 unsigned short type; //接口的硬件类型 unsigned mtu; //最大传输单元 unsigned char dev_addr[MAX_ADDR_LEN]; //硬件地址,MAC地址需要驱动程序从硬件上读出并填充 unsigned char broadcast[MAX_ADDR_LEN]; //广播地址,以太网的广播地址位:0xFF unsigned short flags; //网络接口标志,以IFF_(interfacs flags) 开头, //部分标志由内核管理,其他的在接口初始化时被设置以说明设备接口的能力和性能。 // IFF_UP (当设备被激活并可以开始发送数据包时,内核设置该标志) // IFF_AUTOMEDIA (设备可在多种媒介间切换) IFF_MULTICAST(允许组播) // IFF_BROADCAST (允许广播) IFF_NOARP(接口不能执行ARP) // IFF_DEBUG (调试模式) IFF_POINTOPOINT(接口连接到点到点链路) // IFF_LOOPBACK(回环)
(4)设备操作函数:
int (*open)(struct net_device *dev); //获取设备需要的IO地址,IRQ,DMA通道等 int (*close)(struct net_device *dev);
int (*hard_start_xmit)(struct sk_buff* skb, struct net_device *dev); //启动数据包发送 void (*tx_timeout)(struct net_device *dev); //当数据包发送超时时调用,重启数据包发送或重启硬件等措施来回复网络设备到正常状态 int (*hard_header)(struct sk_buff *skb, struct net_device *dev, unsigned short type, //协议类型 void *daddr, //目的地址 void *saddr, //源地址 unsigned len); //数据长度 //完成硬件帧头填充,返回填充的字节数。对于以太网而言,将内核提供的eth_header()函数赋值即可 struct net_device_stats* (*get_stats)(struct net_device *dev); //获取网络设备的状态信息,net_device_stats保存了网络设备详细的流量统计信息 //如发送和接受到的数据包数,字节数等。 int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); int (*set_config)(struct net_device *dev, struct ifmap *map); //该变设备的IO地址和中断号 int (*set_mac_address)(struct net_device *dev, void *addr); //设置设备的MAC地址 const struct ethtool_ops *ethtool_ops; //用于更改或报告我那刚落设备的设置 void (*poll_controller)(struct net_device *dev); //内核配置NET_POLL_CONTROLLER,为了支持纯粹的netconsole,可以为网络设备驱动实现poll_controller() //它完全以轮询的方式接受数据包
(5)辅助成员
unsigned long trans_start; //记录最后的数据包开始发送的实践戳 unsigned long last_rx; //记录最后一次接收到数据包时的实践戳(jiffies)
void *priv; //私有信息指针,与 filp->private_data的地位相当。设备驱动中应该以netdev_priv()函数获得该指针
通常情况下,网络设备驱动以中断方式接受数据包,而poll_controller()则采用纯论询方式,另外一种数据接受方式是NAPI(New API),其数据流程如下:
“ 接收中断来临——关闭接收中断——以论询方式接收所有数据包直接收空——开启接收中断——接收中断来临......” 内核中提供如下与NAPI相关的API:
static inline void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int),
int weight);
static inline void netif_napi_del(struct napi_struct *napi);
/* 以上两个函数分别初始化和移除一个NAPI, netif_napi_add()的poll参数是NAPI要调度执行的论询函数 */
static inline void napi_enable(struct napi_struct *n); //使能NAPI
static inline void napi_disable(struct napi_struct *n);
static inline int napi_schedule_prep(struct napi_struct *n); //检查NAPI是否可以调度,而napi_schedule()函数用于调度论询实例的运行
static inline void napi_schedule(struct napi_struct *n);
static inline void napi_complete(struct napi_struct *n); //在NAPI处理完成后调用
16.1.3 设备驱动功能层
1.设置 struct net_device 结构体
2.由于数据包的接受可由中断引发,所以要设置中断处理函数,负责读取硬件上及诶收的数据包并传送给上层协议
16.1.4 网络设备媒介层
/* 寄存器定义 */
#define DATA_REG 0X0004
#define CMD_REG 0X0008
/* 寄存器读写函数 */
static u16 xxx_readword(u32 base_addr, int portno);
static voi dxxx_writeword();