网络设备驱动相比字符型设备的驱动要复杂一些,除了总体上驱动的框架有一些相似外,有很多地方都是不同,但网络设备驱动有一个很大的特点就是有固定的框架
可以遵循,具体的框架会在后边详细的叙述,这里主要分析网络设备驱动的结构,和整个tcp/ip网络结构一样,整个网络设备驱动也是一个分层的结构。具体
如下:
1.网络协议接口层
在网络协议接口层,只提供了两个抽象函数dev_queue_xmit()与
netif_rx(),之所以称之为抽象函数,是因为这两个函数抽象了很多底层的操作,不管是那个芯片它在网络协议结构的操作函数都是这两个函数,采用这
样的抽象后,给上层带来了很多的方便,给上层协议提供统一的数据包收发接口,无论上层是ARP协议还是IP协议,都通过dev_queue_xmit()
函数发送数据,通过netif_rx()函数接收数据。此层使上层协议独立于具体的设备。
相关数据结构sk_buff:
sk_buff
称为“套接字缓冲区”,用于在Linux网络子系统中各层之间传递数据。是Linux网络子系统数据传递的“中枢神经”。sk_buff定义位置
为:include/linux/skbuff.h,这个数据结构定义了很多用于网络操作的函数,更多的设计整个协议的实现,包括各层报文的头信息,以及
报文的帧格式,下边这个这个结构体是有关报文header信息的,下边的代码摘自kernel2.6.29/include/linux
/skbuff.h
struct sk_buff { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev;
struct sock *sk; ktime_t tstamp; struct net_device *dev;
union { struct dst_entry *dst; struct rtable *rtable; }; #ifdef CONFIG_XFRM struct sec_path *sp; #endif /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ char cb[48];
unsigned int len, data_len; __u16 mac_len, hdr_len; union { __wsum csum; struct { __u16 csum_start; __u16 csum_offset; }; }; __u32 priority; __u8 local_df:1, cloned:1, ip_summed:2, nohdr:1, nfctinfo:3; __u8 pkt_type:3, fclone:2, ipvs_property:1, peeked:1, nf_trace:1; __be16 protocol;
void (*destructor)(struct sk_buff *skb); #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; struct sk_buff *nfct_reasm; #endif #ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info *nf_bridge; #endif
int iif; __u16 queue_mapping; #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT __u16 tc_verd; /* traffic control verdict */ #endif #endif #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif #if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE) __u8 do_not_encrypt:1; __u8 requeue:1; #endif /* 0/13/14 bit hole */
#ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie; #endif #ifdef CONFIG_NETWORK_SECMARK __u32 secmark; #endif
__u32 mark;
__u16 vlan_tci;
sk_buff_data_t transport_header;//传输层帧头 sk_buff_data_t network_header;//网络层帧头 sk_buff_data_t mac_header;//数据链路层帧头 /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail;//下边的tail、end、head、data是与缓冲区相关 sk_buff_data_t end; unsigned char *head,*data; unsigned int truesize; atomic_t users; };
|
sk_buff中的数据缓冲区的指针
Linux必须分配用于容纳数据包的缓冲区,sk_buff中定义了4个指向这片缓冲区的不同位置的指针head、data、tail、end。
head:指针指向内存中已分配的用于存储网路数据的缓冲区起始地址,sk_buff和相关数据块在分配后,该指针的值就固定了。
data:指针指向对应当前协议层有效数据的起始地址。每个协议的有效数据含义不同。
tail:指向对应当前协议层有效数据负载的结尾地址,与data对应。
end:指向内存分配的数据缓冲区的结尾地址,与head指针对应。和head一样,sk_buff和相关数据块被分配后,end指针也就固定了。如图:
套接字缓冲区sk_buff相关操作:
分配空间:
struct sk_buff *dev_alloc_skb(unsigned len)
释放空间:
dev_kfree_skb(struct sk_buff *skb)
dev_kfree_skb_irq(struct sk_buff *skb)
dev_kfree_skb_any(struct sk_buff *skb)
put操作:
unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
unsigned char *__skb_put(struct sk_buff *skb, unsigned int len);
作用:tail指针下移len长度,并增加sk_buff中len的值,返回改变后的tail值。主要用于在尾部追加数据。
push操作:
unsigned char *skb_push(struct sk_buff *skb, unsigned int len);
unsigned char *__skb_push(struct sk_buff *skb, unsigned int len);
作用:将data指针上移,同时增加sk_buff中的len。主要用于在数据包发送时添加头部。函数带__和不带__的区别在于:带__的会检测放入缓冲区的数据,后则不会。
pull操作:
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len);
作用:将data指针下移,并减少sk_buff中的len值。这个操作一般用于下层协议向上层协议移交数据包,使data指针指向上一层协议的协议头
reserve操作:
void skb_reserve(struct sk_buff *skb, unsigned int len);
作用:将data和tail指针同时下移,这个操作主要用于在存储空间的头部预留len长度的空隙。
2.网络设备接口层
我们知道,通常的驱动编写就是填充一个设备相关的结构体,网络设备也是这样,在2.6.29的内核中,需要填写结构体如下:
kernel2.6.29/include/linux/netdevice.h
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); int (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb); #define HAVE_CHANGE_RX_FLAGS void (*ndo_change_rx_flags)(struct net_device *dev, int flags); #define HAVE_SET_RX_MODE void (*ndo_set_rx_mode)(struct net_device *dev); #define HAVE_MULTICAST void (*ndo_set_multicast_list)(struct net_device *dev); #define HAVE_SET_MAC_ADDR int (*ndo_set_mac_address)(struct net_device *dev, void *addr); #define HAVE_VALIDATE_ADDR int (*ndo_validate_addr)(struct net_device *dev); #define HAVE_PRIVATE_IOCTL int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); #define HAVE_SET_CONFIG int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); #define HAVE_CHANGE_MTU int (*ndo_change_mtu)(struct net_device *dev, int new_mtu); int (*ndo_neigh_setup)(struct net_device *dev, struct neigh_parms *); #define HAVE_TX_TIMEOUT void (*ndo_tx_timeout) (struct net_device *dev);
struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp); void (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid); void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid); #ifdef CONFIG_NET_POLL_CONTROLLER #define HAVE_NETDEV_POLL void (*ndo_poll_controller)(struct net_device *dev); #endif };
|
在我们实现这些函数中的一部分后,就实现了驱动的功能了,为千变万化的网络设备定义统一的、抽象的数据结构net_device结构体,以不变应万
变,实现多种硬件在软件层次上的统一。net_device结构体在内核中指代一个网络设备,网络设备驱动只需填充其结构体就可以实现内核与具体硬件操作
函数的挂接。实际驱动的编写过程中,我们并不需要实现全部的函数,实际上,我们只要根据具体的需要实现上边的部分就可以了。
net_device结构体的相关成员
网络设备的名称:
char name[IFNAMESIZ]
设备初始化指针:
int (*init)(struct net_device *dev);
硬件信息:
unsigned long mem_end;
unsigned long mem_start;
二者分别定义了设备所用的共享内存 的起始和结束地址
unsigned long base_addr;网络设备的I/O基地址
unsigned char irq;设备使用的中断号
unsigned char if_port;多端口设备中端口选择
unsigned char dma;分配给设备的DMA通道
接口信息
unsigned short hard_header_len;网络设备的硬件头长度,在以太网设备的初始化函数中,该成员被赋值为ETH_HLEN,即14
unsigned short type;接口的硬件类型
unsigned mtu;最大传输单元
unsigned char dev_addr[MAX_ADDR_LEN];
unsigned char broadcase[MAX_ADDR_LEN];
二者分别用于存放设备的硬件地址和广播地址,以太网设备的广播地址为6个0xFF
unsigned short flags;网络接口标志
设备操作函数
打开、关闭
int (*open)(struct net_device *dev);
int (*stop)(struct net_device *dev);
启动数据包发送
int (*hard_start_xmit)(struct sk_buff *skb, struct net_device *dev);
获得网络设备状态
struct net_device_status * (*get_status)(struct net_device *dev);
设备I/O控制
int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
配置接口,I/O地址,中断号
int (*set_config)(struct net_device *dev, struct ifmap *map);
设置MAC地址
int (*set_mac_address)(struct net_device *dev, void *addr);
辅助成员
unsigned long trans_start;
unsigned long last_rx;二者分别为:最后一次开始发送的时间戳、最后一次接收到数据包的时间戳,这两个时间戳记录都是jiffies,驱动程序应维护这两个成员。
void *priv;私有信息指针
spinlock_t xmit_lock;
int xmit_lock_owner;xmit_lock是避免hard_start_xmit函数同时多次调用的自旋锁。xmit_lock_owner指向拥有此自旋锁的CPU的编号。
阅读(1623) | 评论(0) | 转发(0) |