网卡驱动程序设计

本博客转载于:http://blog.163.com/sunshine_linting/blog/static/4489332320118203737560/


Linux网络体系架构

需要掌握的知识点:

1.  Linux的协议栈层次

2.  Linux的网络子系统架构

 

协议栈:

Linux的优点之一在于它丰富而稳定的网络协议栈。其范围从协议无关层(例如通用 socket 层接口或设备层)到各种具体的网络协议实现。

 

协议简介:

对于网络的理论介绍一般都采用 OSI(Open Systems Interconnection)模型,但是Linux 中网络栈的介绍一般分为四层的Internet 模型。

 

协议栈层次对比


 

网络接口层:

网络接口层把数据链路层和物理层合并在了一起,提供访问物理设备的驱动程序,对应的网络协议主要是以太网协议。

 

网际层:

网络层协议管理离散的计算机间的数据传输,如IP协议为用户和远程计算机提供了信息包的传输方法,确保信息包能正确地到达目的机器。重要的网络层协议包括ARP(地址解析协议)、ICMP(Internet控制消息协议)和IP协议(网际协议)等。

 

传输层:

传输层的功能包括:格式化信息流、提供可靠传输。传输层包括TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议),它们是传输层中最主要的协议。

应用层:

应用层位于协议栈的顶端,它的主要任务是服务于应用,如利用FTP(文件传输协议)传输一个文件。常见的应用层协议有:HTTP,FTP,Telnet等。应用层是Linux网络设定很关键的一层,Linux服务器的配置文档主要针对应用层中的协议。

 

Linux网络子系统:


Linux网络子系统

Linux 网络子系统的顶部是系统调用接口层。它为用户空间的应用程序提供了一种访问内核网络子系统的方法。位于其下面的是一个协议无关层,它提供了一种通用方法来使用传输层协议。然后是具体协议的实现,在 Linux 中包括内嵌的协议 TCP、UDP,当然还有 IP。然后是设备无关层,它提供了协议与设备驱动通信的通用接口,最下面是设备驱动程序。

 

系统调用接口:

为应用程序提供访问内核网络子系统的方法:Socket系统调用。

 

协议无关接口:

实现一组通用函数来访问各种不同的协议:通过socket实现。Linux 中的 socket 使用struct sock来描述,这个结构包含了特定socket 所需要的所有状态信息,还包括socket 所使用的特定协议和在 socket 上可以执行的一些操作。

 

网络协议:

网络协议层用于实现各种具体的网络协议,如: TCP、UDP 等。

 

设备无关接口:

设备无关接口将协议与各种网络设备驱动连接在一起。

这一层提供一组通用函数供底层网络设备驱动程序使用,让它们可以对高层协议栈进行操作。首先,设备驱动程序可能会通过调用 register_netdevice 或unregister_netdevice 在内核中进行注册或注销。调用者首先填写 net_device 结构,然后传递这个结构进行注册。内核调用它的 init 函数(如果定义了这种函数),然后执行一组健全性检查,并将新设备添加到设备列表中(内核中的活动设备链表)。

 

要从协议层向设备发送数据,需要使用dev_queue_xmit 函数,这个函数对数据进行排队,并交由底层设备驱动程序进行最终传输报文的接收通常是使用 netif_rx 执行的。当底层设备驱动程序接收到一个报文(包含在所分配的 sk_buff 中)时,就会通过调用 netif_rx 将 数据上传至设备无关层,然后,这个函数通过 netif_rx_schedule 将 sk_buff 在上层协议队列中进行排队,供以后进行处理。

 

驱动程序:

网络体系结构的最底部是负责管理物理网络设备的设备驱动程序层。

 

网卡驱动设计

设备描述

每个网络接口都由一个net_device结构来描述,该结构可使用如下内核函数动态分配:

1、struct net_device *alloc_netdev(int sizeof_priv, const char *mask, void (*setup)(struct net_device *))

sizeof_priv 私有数据区大小; mask:设备名; setup 初始化函数

2、struct net_device *alloc_etherdev(int sizeof_priv)

 

net_device

结构 net_device 的主要成员包括:

char name[IFNAMSIZ]

设备名,如:eth%d

 

unsigned long state

设备状态

 

unsigned long base_addr

I/O 基地址

 

unsigned int irq

中断号

 

int (*init)(struct net_device *dev)

初始化函数。该函数在register_netdev时被调用来完成对 net_device 结构的初始化

 

和字符驱动一样, 网络设备也要声明能操作它的函数。有些操作可以保留为 NULL, 有的可以通过 ether_setup 来使用默认设置。网络接口的设备方法可分为两组:基本的和可选的,基本方法包括那些使用接口所必需的;可选的方法实现更多高级的功能。

 

基本方法:

int (*open)(struct net_device *dev)

打开接口。ifconfig 激活时,接口将被打开。

 

int (*stop)(struct net_device *dev)

停止接口。该什么时候调用呢?

 

int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev)

数据发送函数。

 

可选操作:

int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd)

处理特定于接口的 ioctl 命令

 

int (*set_mac_address)(struct net_device *dev, void *addr)

改变Mac地址的函数,需要硬件支持该功能

 

设备注册:

网络接口驱动的注册方式与字符驱动不同之处在于它没有主次设备号,并使用如下函数注册。

int register_netdev(struct net_device *dev)

sk_buff

Linux内核中的每个网络数据包都由一个套接字缓冲区结构 struct sk_buff 描述,即一个sk_buff结构就是一个包,指向sk_buff的指针通常被称做skb。

该结构包含如下重要成员:

?   struct device *dev;  //处理该包的设备

?   __u32 saddr; //IP源地址

?   __u32 daddr; //IP目的地址

?   __u32 raddr; //IP路由器地址

 

unsigned char *head;    //分配空间的开始

unsigned char *data;     //有效数据的开始

unsigned char *tail;       //有效数据的结束

unsigned char *end;     //分配空间的结束

unsigned long len;    //有效数据的长度

 

Skb操作函数

操作sk_buff的内核函数如下:

?    struct sk_buff *alloc_skb(unsigned int len, int priority)

分配一个sk_buff 结构,供协议栈代码使用

?    struct sk_buff *dev_alloc_skb(unsigned int len)

分配一个sk_buff 结构,供驱动代码使用

unsigned char *skb_push(struct sk_buff *skb, int len)

向后移动skb的tail指针,并返回tail移动之前的值。

 

unsigned char *skb_put(struct sk_buff *skb, int len)

向前移动skb的head指针,并返回head移动之后的值。

 

kfree_skb(struct sk_buff *skb)

释放一个sk_buff 结构,供协议栈代码使用

vdev_kfree_skb(struct sk_buff *skb)

释放一个sk_buff 结构,供驱动代码使用

 

设备打开

Open 请求任何它需要的系统资源并且启动接口 :

1.注册中断,DMA等

2.设置寄存器,启动设备

3.启动发送队列

 

设备打开-例

int net_open(struct net_device *dev)

{

/*申请中断*/

request_irq(dev->irq, &net_interrupt, SA_SHIRQ,

“dm9000”, dev);

/* 设置寄存器,启动设备 */

...... ...... ...... ......

/*启动发送队列*/

netif_start_queue(dev);

}

 

数据发送

当核心需要发送一个数据包时,它调用hard_start_transmit函数,该函数将最终调用到net_device结构中的hard_start_xmit函数指针。

 

数据接收

网络接口驱动可以实现两种方式的报文接收: 中断和查询,Linux中驱动多采用中断方式。

 

接收流程

1、分配Skb

skb = dev_alloc_skb(pkt->datalen + 2)

2、从硬件中读取数据到Skb

3、调用netif_rx将数据交给协议栈netif_rx(skb)

 

中断处理

网络接口通常支持3种类型的中断: 新报文到达中断、报文发送完成中断和出错中断。中断处理程序可通过查看网卡中的中断状态寄存器,来分辨出中断类型。

 

Dm9000网卡驱动分析

芯片介绍

DM9000是开发板经常采用的网络芯片,是一种高度集成而且功耗很低的高速网络控制器,可以和CPU直连,支持10/100M以太网连接,芯片内部自带16K的SRAM(3KB用来发送,13KB用来接收) 。

 

Dm9000在收到由上层发来的以太网帧后,开始侦听网络线路,如果线路忙,就等到线路空闲为止,否则立即发送该数据帧。接收时, 它将从以太网收到的数据包在经过解码、去掉帧头和地址检验等步骤后缓存在片内。在CRC校验通过后, 它会通知CPU收到了数据帧。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值