linux网络驱动

linux 设备驱动

linux设备驱动在linux中作为linux的内核模块存在。而内核模块可以在系统运行期间动态扩展系统。所以,我们可以在用户空间,使用insmod和rmmod动态安装或卸载模块。

现实世界中存在着大量的设备,这些设备在电气特性和I/O方式上都各不相同。为了简化设备驱动程序员的工作,linux系统从这些各异的设备中提取了共性的特征,将其划分为三大类:字符设备、块设备和网络设备。

linux设备驱动模型

Linux设备驱动模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,从而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。

用户接口

利用系统中所有设备的完整分层关系图,可以很容易的向用户空间导出一副完整的视图。

这通过一个叫sysfs的特殊的虚拟文件系统实现了,用户可以在用户空间的任意节点挂载这一文件系统。正所谓Linux下一切皆文件。sysfs将变量和功能都作为接口向用户空间公开了,使得设备驱动变得相当透明,对设备驱动的操作也可以简单的变成了文件操作。结合守护进程和内核事件层,sysfs是一个用户空间和内核空间通信的完美的渠道。

linux设备底层模型

设备模型是层次的结构,层次的每一个节点都是通过kobject实现的,在文件上则体现在sysfs文件系统。

kobject结构内核中存在struct kobject数据结构,每个加载到系统中的kobject都唯一对应/sys或者子目录中的一个文件夹。可以这样说,许多kobject结构就构成设备模型的层次结构。每个kobject对应一个或多个struct attribute描述属性的结构。

struct kobject {
    const char *name; /* 对应sysfs的目录名 */
    struct list_head entry; /* kobjetct双向链表 */
    struct kobject *parent; /* 指向kset中的kobject,相当于指向父目录 */
    struct kset *kset; /*指向所属的kset */
    struct kobj_type *ktype; /*负责对kobject结构跟踪*/
    struct sysfs_dirent *sd;
    struct kref kref; /*kobject引用计数*/
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
};

网卡驱动程序

网络设备(又称“网卡”),用来完成高层网络协议(如TCP/UDP等)的底层数据传输及设备控制等功能。

发送数据包

网络设备在/dev目录下没有入口点,换句话说,网络设备在系统中并不像块设备那样以一个设备文件的形式存在,在应用层,用户通过套接口API的socket函数来使用网络设备。

socket API的原型函数为:

<sys/socket.h>
int socket(int family, int type, int protocol)

其中family表示套接口所使用的协议族,包括AF_INET(IPV4协议族)和AF_INET6(IPv6协议族)等。

参数type用来表示套接口的类型,有SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口)。

一般派说,函数的第三个参数在实际使用中常设置为0,除非是用在原始套接口上。

之后linux内核中高层网络代码负责分配skb以存储网络数据包,然后将该skb传到驱动程序的发送函数中。驱动程序的发送函数一般会使用DMA来将位于主存中的skb传输到设备内存中,再由硬件逻辑发送出去。硬件在成功发送完一个数据帧后,通常会以中断的方式通知处理器,接下来相应驱动程序的中断处理函数被调用来处理这种情况。对于成功发送分组产生的中断,在其中断处理函数中会释放该分组所在的skb。

接受数据包

网络设备除了响应来自内核的请求wait,还需要异步地处理来自外部世界的数据包,而对于块设备而言,只需要响应来自内核的请求。这使得网络设备驱动程序的设计模式,除了处理数据wait,还需要完成诸如地址设置、配置网络传输参数以及流量统计等一些管理类的任务。

底层网络设备在成功将一个分组传输到主存后,会以中断的方式通知驱动程序。后者负责分配一个新的skb来容纳该新入的分组,然后通过调用netif_rx函数通知网络子系统高层新数据包到达。

网络设备和驱动程序在TCP/IP协议栈中的位置

网络设备和驱动程序完成的是最底层的物理层和数据链路层,该层面向实际承担数据传输任务的物理媒体,为数据通信的介质提供规范和定义,主要关心的是在通信线路上传输比特流的问题。

网络驱动程序的结构

所有的Linux网络驱动程序遵循通用的接口,设计时采用的是面向对象的方法。

一个设备就是一个对象(device 结构),它内部有自己的数据和方法。

每一个设备的方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身的数据(类似面向对象程序设计时的this引用)。

一个网络设备最基本的方法有初始化、发送和接收。

网络驱动程序的基本方法

  1. 初始化(initialize)

    驱动程序必须有一个初始化方法,在把驱动程序载入系统的时候会调用这个初始化程序,它做以下几方面的工作:

    检测设备。在初始化程序里你可以根据硬件的特征检查硬件是否存在,然后决定是否启动这个驱动程序。

    配置和初始化硬件。在初始化程序里你可以完成对硬件资源的配置,比如即插即用的硬件就可以在这个时候进行配置(Linux内核对PnP功能没有很好的支持,可以在驱动程序里完成这个功能)。

    配置或协商好硬件占用的资源以后,就可以向系统申请这些资源。有些资源是可以和别的设备共享的,如中断。有些是不能共享的,如IO、DMA。

    接下来你要初始化device结构中的变量

    最后,你可以让硬件正式开始工作

  2. 打开(open)

    open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状态由down–>up)。

    所以实际上很多在initialize中的工作可以放到这里来做。比如资源的申请,硬件的激活。如果dev->open返回非0(error),则硬件的状态还是down。

    open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止模块卸载时设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。

  3. 关闭(stop)

    close方法做和open相反的工作。可以释放某些资源以减少系统负担。close是在设备状态由up转为down时被调用的。

    另外如果是做为模块装入的驱动程序,close里应该调用MOD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。

    另外close方法必须返回成功(0==success)。

  4. 发送(hard_start_xmit)

    所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit 时,发送的数据放在一个sk_buff结构中。

    一般的驱动程序把数据传给硬件发出去。也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统,或者dummy设备直接丢弃数据。

    如果发送成功,hard_start_xmit方法里释放sk_buff,返回0(发送成功)。

    如果设备暂时无法处理,比如硬件忙,则返回1。这时如果dev->tbusy置为非0,则系统认为硬件忙,要等到dev->tbusy置0以后才会再次发送。tbusy的置0任务一般由中断完成。硬件在发送结束后产生中断,这时可以把tbusy置0,然后用mark_bh()调用通知系统可以再次发送。在发送不成功的情况下,也可以不置dev->tbusy为非0,这样系统会不断尝试重发。如果hard_start_xmit发送不成功,则不要释放sk_buff。传送下来的sk_buff中的数据已经包含硬件需要的帧头。所以在发送方法里不需要再填充硬件帧头,数据可以直接提交给硬件发送。

    sk_buff是被锁住的(locked),确保其他程序不会存取它。

  5. 接收(reception)

    驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。

    一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。

    接下来填充sk_buff中的一些信息。skb->dev = dev,判断收到帧的协议类型,填入skb->protocol(多协议的支持)。

    把指针skb->mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要设置skb->pkt_type,标明第二层(链路层)数据类型。可以是以下类型:

    PACKET_BROADCAST : 链路层广播

    PACKET_MULTICAST : 链路层组播

    PACKET_SELF : 发给自己的帧

    PACKET_OTHERHOST : 发给别人的帧(监听模式时会有这种帧)

    最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后返回,真正的处理是在中断返回以后,这样可以减少中断时间。

    调用netif_rx()以后,驱动程序就不能再存取数据缓冲区skb。

  6. 地址解析(xarp)

    有些网络有硬件地址(比如Ethernet),并且在发送硬件帧时需要知道目的硬件地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。

    这个对应是通过地址解析完成的。需要做arp的的设备在发送之前会调用驱动程序的rebuild_header方法。调用的主要参数包括指向硬件帧头的指针,协议层地址。

    如果驱动程序能够解析硬件地址,就返回1,如果不能,返回0。

    对rebuild_header的调用在net/core/dev.c的do_dev_queue_xmit()里。

  7. 参数设置和统计数据

    在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般只有超级用户(root)权限才能对设备参数进行设置。

    设置方法有:

    dev->set_mac_address() 当用户调用ioctl类型为SIOCSIFHWADDR时是要设置这个设备的mac地址。一般对mac地址的设置没有太大意义的。

    dev->set_config() 当用户调用ioctl时类型为SIOCSIFMAP时,系统会调用驱动程序的set_config方法。用户会传递一个ifmap结构包含需要的I/O、中断等参数。

    dev->do_ioctl()如果用户调用ioctl时类型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,系统会调用驱动程序的这个方法。一般是设置设备的专用数据。读取信息也是通过ioctl调用进行。

    除次之外驱动程序还可以提供一个dev->get_stats方法,返回一个enet_statistics结构,包含发送接收的统计信息。ioctl的处理在net/core/dev.c的dev_ioctl()和dev_ifsioc()里。

网络设备数据结构

struct device
{
 /*
  * This is the first field of the "visible" part of this structure
  * (i.e. as seen by users in the "Space.c" file). It is the name
  * the interface.
  */
 char          *name;
 /* I/O specific fields - FIXME: Merge these and struct ifmap into one */
 unsigned long      rmem_end;       /* shmem "recv" end   */
 unsigned long      rmem_end;       /* shmem "recv" end   */
 unsigned long      rmem_start;      /* shmem "recv" start  */
 unsigned long      mem_end;       /* shared mem end    */
 unsigned long      mem_start;      /* shared mem start   */
 unsigned long      base_addr;      /* device I/O address  */
 unsigned char      irq;         /* device IRQ number  */
 /* Low-level status flags. */
 volatile unsigned char start,        /* start an operation  */
             interrupt;      /* interrupt arrived  */
 /* 在处理中断时interrupt设为1,处理完清0。 */
 unsigned long      tbusy;        /* transmitter busy must be long
 for
 struct device      *next;
 /* The device initialization function. Called only once. */
 /* 指向驱动程序的初始化方法。 */
 int           (*init)(struct device *dev);
 /* Some hardware also needs these fields, but they are not part of the
   usual set specified in Space.c. */
 /* 一些硬件可以在一块板上支持多个接口,可能用到if_port。 */
 /* 一些硬件可以在一块板上支持多个接口,可能用到if_port。 */
 unsigned char      if_port;       /* Selectable AUI, TP,..*/
 unsigned char      dma;         /* DMA channel     */
 struct enet_statistics* (*get_stats)(struct device *dev);
 /*
  * This marks the end of the "visible" part of the structure. All
  * fields hereafter are internal to the system, and may change at
  * will (read: may be cleaned up at will).
  */
 /* These may be needed for future network-power-down code. */
 /* trans_start记录最后一次成功发送的时间。可以用来确定硬件是否工作正常。*/
 unsigned long      trans_start; /* Time (in jiffies) of last Tx */
 unsigned long      last_rx;   /* Time of last Rx       */
 /* flags里面有很多内容,定义在include/linux/if.h里。*/
 unsigned short     flags;    /* interface flags (a la BSD)  */
 unsigned short     family;    /* address family ID (AF_INET) */
 unsigned short     metric;    /* routing metric (not used)  */
 unsigned short     mtu;     /* interface MTU value     */
 /* type标明物理硬件的类型。主要说明硬件是否需要arp。定义在
   include/linux/if_arp.h里。 */
 unsigned short     type;     /* interface hardware type   */
 /* 上层协议层根据hard_header_len在发送数据缓冲区前面预留硬件帧头空间。*/
 unsigned short     hard_header_len;   /* hardware hdr length */
 /* priv指向驱动程序自己定义的一些参数。*/
 void          *priv;    /* pointer to private data   */
 /* Interface address info. */
 unsigned char      broadcast[MAX_ADDR_LEN];   /* hw bcast add */
 unsigned char      pad;             /* make dev_addr aligned
 to 8
bytes */
 unsigned char      dev_addr[MAX_ADDR_LEN];    /* hw address  */
 unsigned char      addr_len;   /* hardware address length   */
 unsigned long      pa_addr;   /* protocol address       */
 unsigned long      pa_brdaddr;  /* protocol broadcast addr   */
 unsigned long      pa_dstaddr;  /* protocol P-P other side addr */
 unsigned long      pa_mask;   /* protocol netmask       */
 struct dev_mc_list   *mc_list;   /* Multicast mac addresses   */
 int          mc_count;   /* Number of installed mcasts  */
 struct ip_mc_list   *ip_mc_list;  /* IP multicast filter chain  */
 __u32         tx_queue_len;  /* Max frames per queue allowed */
 /* For load balancing driver pair support */
 unsigned long      pkt_queue;  /* Packets queued */
 struct device      *slave;    /* Slave device */
 struct net_alias_info     *alias_info;  /* main dev alias info */
 struct net_alias       *my_alias;   /* alias devs */
 /* Pointer to the interface buffers. */
 struct sk_buff_head   buffs[DEV_NUMBUFFS];
 /* Pointers to interface service routines. */
 int           (*open)(struct device *dev);
 int           (*hard_start_xmit) (struct sk_buff *skb,
                       struct device *dev);
 int           (*hard_header) (struct sk_buff *skb,
                     struct device *dev,
                     unsigned short type,
                     void *daddr,
                     void *saddr,
                     unsigned len);
 int           (*rebuild_header)(void *eth, struct device *dev,
                unsigned long raddr, struct sk_buff *skb);
#define HAVE_MULTICAST
 void          (*set_multicast_list)(struct device *dev);
#define HAVE_SET_MAC_ADDR
 int           (*set_mac_address)(struct device *dev, void *addr);
#define HAVE_PRIVATE_IOCTL
 int           (*do_ioctl)(struct device *dev, struct ifreq *ifr, int
 cmd);
#define HAVE_SET_CONFIG
 int           (*set_config)(struct device *dev, struct ifmap *map);
#define HAVE_HEADER_CACHE
 void          (*header_cache_bind)(struct hh_cache **hhp, struct dev
ice
*dev, unsigned short htype, __u32 daddr);
*dev, unsigned short htype, __u32 daddr);
 void          (*header_cache_update)(struct hh_cache *hh, struct dev
ice
*dev, unsigned char * haddr);
#define HAVE_CHANGE_MTU
 struct iw_statistics*  (*get_wireless_stats)(struct device *dev);
};
时间: 2024-08-20 00:11:01

linux网络驱动的相关文章

网络驱动移植之解析Linux网络驱动的基本框架

内核源码:linux-2.6.38.8.tar.bz2 概括而言,编写Linux网络驱动其实只要完成两件事即可,一是分配并初始化网络设备,二是注册网络设备. 1.分配并初始化网络设备 动态分配网络设备(从C语言角度来看,其实就是定义了一个struct net_device结构体变量,并对这个结构体变量的某些成员进行了初始化而已)及其私有数据的大致过程如下图(以以太网设备为例): 下面将结合linux-2.6.38.8中的代码详细分析网络设备的分配和初始化过程. [cpp] view plain

Linux网络驱动架构

网络设备介绍 网络设备是计算机体系结构中必不可少的一部分,处理器如果想与外界通信,通常都会选择网络设备作为通信接口.众所周知,在 OSI(Open Systems Interconnection,开放网际互连)中,网络被划分为七个层次,从下到上分别是物理层.数据链路层.网络层.传输层.会话层.表示层和应用层.我们所讲的网络设备也包括两个层次,一层叫做 MAC(Media Access Control)层,对应于 OSI 的数据链路层:另一层叫做 PHY(Physical Layer)层,对应于物

linux 网络设备驱动

linux 网络驱动 谨以此文纪念过往的岁月 一.前言在linux中网络驱动也是一个大头,如何去理解网络驱动是作为一个linux驱动工程师必备的技能.不过同样的设备,在不同人的手中会有不同的效果,其原因就在于驱动的好与否. 二.设备注册学习网络的驱动与学习普通cdev驱动一样,都是学习其模板,然后再创造学习.在学习网络驱动过程中,我们忽略其对硬件的具体操作,这样会更具有通用性.以dm9000A为例.网络驱动亦如usb驱动一样,其内核将许多工作都完成了.DM9000A认采用了platform bu

Linux 网卡驱动学习(分析一个虚拟硬件的网络驱动例子)

在Linux,网络分为两个层,分别是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层.网络堆栈是硬件中独立出来的部分,主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层. 网络设备驱动程序的主要功能是: (1)模块加载或内核启动相关的初始化处理 (2)清除模块时的处理 (3)网络设备的检索和探测 (4)网络设备的初始化和注册 (5)打开或关闭网络设备 (6)发送网络数据 (7)接收网络数据 (8)中断处理(在发送完数据时,硬件向内核产生一个中断,告

Linux 网卡驱动学习(二)(网络驱动接口小结)

[摘要]前文我们分析了一个虚拟硬件的网络驱动样例.从中我们看到了网络设备的一些接口.事实上网络设备驱动和块设备驱动的功能比較相似,都是发送和接收数据包(数据请求). 当然它们实际是有非常多不同的. 1.引言 首先块设备在/dev文件夹下有设备节点.而网络设备没有这种设备入口. read,write等常规的文件接口在网络设备下也没有意义. 最大的差别在于:块设备仅仅响应内核的数据请求:而网络设备驱动要异步地接收来自外部的数据包.简单地说.块设备驱动是被要求数据传输而网络设备是主动请求数据传输.网络

Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)

在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层和网络硬件的中间层. 网络设备驱动程序的主要功能是: (1)模块载入或内核启动相关的初始化处理 (2)清除模块时的处理 (3)网络设备的检索和探測 (4)网络设备的初始化和注冊 (5)打开或关闭网络设备 (6)发送网络数据 (7)接收网络数据 (8)中断处理(在发送完数据时.硬件向内核产生一个中断.

Linux 网络设备驱动开发(一) —— linux内核网络分层结构

Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护. 一.内核网络结构 在Linux内核中,对网络部分按照网络协议层.网络设备层.设备驱动功能层和网络媒介层的分层体系设计. 网络驱动功能层主要通过网络驱动程序实现. 在Linux内核,所有的网络设备都被抽象为一个接口处理,该接口提供了所有的网络操作. net_device结构表示网络设

linux无线网卡驱动安装

环境  在笔记本里的虚拟机10.0版本,centos-6.5 无线网卡fast-fw300um 第一步要查看芯片  lsusb  当你得到芯片之后接下来查看内核,如果内核已经有芯片模块就不用再装了,如果不支持的话,那么接下来就到芯片官网 下载Linux驱动 http://www.realtek.com.tw/default.aspx  **虽然我的无线网卡是fast 生产的 ,但是他并没有给我们Linux的驱动,反倒是芯片商提供有驱动,所以要到芯片官网下载驱动** 首先到官网上下载无线网卡的驱动

linux网卡驱动移植

这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可以像IIS设备那样,SOC内有IIS的控制器,而声卡UDA1341放在片外.网卡当然也有这种设计,它是把PHY的下层MAC放入SOC内,片外的是PHY,当然我暂时还没见过这种的.DM9000A的输入是并行的总线,可以和CPU直接IO.而IIS那种需要通过:CPU CORE BUS->I2S控制器->