dm9000网卡驱动探究

关于源码分析的文章,网络上有很多,所以我不想通过源码来分析网卡驱动,而是换个别的角度。

当编写Linux驱动的时候,主要应该解决几个问题:

  • linux驱动框架的使用(增强移植性,并且对熟悉linux驱动框架的人来说,有更高的可读性)
  • 针对具体的设备,该如何对驱动进行控制(如对寄存器的操作),这里当然就是dm9000网卡
  • 操作逻辑(中断 + 流控)
  • 以及,对整个linux网络子系统框架的理解(当然,在编写驱动的时候,可能感觉不到其重要性,但是一旦出现问题,只有了解了整个框架的原理和细节,才能更好的快速定位问题,并对其进行解决)

1.linux网络驱动的框架设计

平台总线

分别在 驱动中注册平台驱动,板文件中注册平台设备(主要包括使用到的resource)

net_device

通用的描述网络设备的对象,该对象的私有数据中往往是一个设备的私有结构体,用来描述具体设备中特殊的部分。

首先内存分配

初始化

通过register_netdev()函数向内核进行注册(注意,在注册之前要进行基本初始化,因为调用了该函数,用户就能操作该设备)

sk_buff

在整个网络子系统中,通过sk_buff进行数据的传输,通过对sk_buff结构体的操作,对数据完成打包和解包的工作(比如添加头部和删除头部)

2.dm9000的操作

dm9000的初始化

dm9000的数据发送操作

dm9000的数据接收操作

dm9000的各种设置


3.dm9000整体操作逻辑

因为dm9000的主要功能是接收发数据,所以这里的操作逻辑是关于数据收发的。

数据收发主要通过中断 + 流控来进行。


dm9000设备的数据缓冲区共有16K,其中前3K为发送缓冲区(0x0000 - 0x02FF),后13K是接收缓冲区(0x0C00-03FFF)

发送缓冲区只能允许存储两个packet,这个一定要考虑进整个收发逻辑中。

发送逻辑

  • 上层以包的形式发送数据,数据的大小(46-1500字节)
  • 每次dm9000会发送一个包
  • 当包成功发送后,会产生一个发送成功中断,从而进入中断处理程序(进行判断)
  • 根据一个计数变量判断缓冲区中是否还有数据,如果有,则发送数据,如果没有,则返回
  • 重启发送队列()

接收逻辑

  • 当缓冲区有中断的时候,会产生接收中断
  • 进入中断处理程序,判断,并进入dm9000_rx,进行接收处理
  • 根据包头部四个字节(下图是包的结构)进行判断,如果包是好的,则继续
  • 将data按照固定的格式放入sk_buff结构体中,将sk_buff通过netif_rx()提交出去(这里需要继续讨论)
  • 异步返回、


当然,别忘了超时处理



4.网络子系统

在驱动中,网络子系统涉及到的主要对象就是sk_buff


sk_buff中对数据的长度的描述主要是

head和end(表示整个数据区的头和位),在sk_buff中是指针

data和tail(有效数据的头和尾),不是指针

关于sk_buff的操作

alloc_skb()


采用了slab内存分配


skb_put()

该函数是在数据区的末端添加某协议的尾部

/**
 *  skb_put - add data to a buffer
 *  @skb: buffer to use
 *  @len: amount of data to add
 *
 *  This function extends the used data area of the buffer. If this would
 *  exceed the total buffer size the kernel will panic. A pointer to the
 *  first byte of the extra data is returned.
 */
unsigned char *skb_put(struct sk_buff *skb, unsigned int len) 
{
    unsigned char *tmp = skb_tail_pointer(skb);
    //return skb->head + skb->tail,head是指针,而end和tail都是int
    SKB_LINEAR_ASSERT(skb);                                                                                                                   
    skb->tail += len; 
    skb->len  += len; 
    if (unlikely(skb->tail > skb->end))
        skb_over_panic(skb, len, __builtin_return_address(0));
    return tmp; 
}
EXPORT_SYMBOL(skb_put);

返回添加部分的头部

skb_trim()

从缓冲区的尾部删除数据

void skb_trim (struct sk_buff * skb, unsigned int len);  len是新的长度,通过从尾部删除数据达到新的长度

skb_push()

在数据的开始部分添加

/**
 *  skb_push - add data to the start of a buffer
 *  @skb: buffer to use
 *  @len: amount of data to add
 *
 *  This function extends the used data area of the buffer at the buffer
 *  start. If this would exceed the total buffer headroom the kernel will
 *  panic. A pointer to the first byte of the extra data is returned.
 */
unsigned char *skb_push(struct sk_buff *skb, unsigned int len) 
{
    skb->data -= len; 
    skb->len  += len; 
    if (unlikely(skb->data<skb->head))
        skb_under_panic(skb, len, __builtin_return_address(0));
    return skb->data; 
    //返回头部
}
EXPORT_SYMBOL(skb_push);  

skb_pull()

和push相反,从缓冲区的开始删除数据


skb_reserve()

该函数的作用是在数据区创建存储协议头部的空间,函数实现很简单。

  1. static inline void skb_reserve(struct sk_buff *skb, int len)  
  2. {  
  3.     skb->data += len;  
  4.     skb->tail += len;  
  5. }  


更多:

NAPI的添加,netif_rx背后的原理(使用了软中断)

还有很多不完善的地方,未完待续;

参考:

Linux内核--网络协议栈深入分析(二)--sk_buff的操作函数   http://www.linuxidc.com/Linux/2012-09/70439p2.htm 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值