《Linux系统设备驱动程序概述(共9页).doc》由会员分享,可在线阅读,更多相关《Linux系统设备驱动程序概述(共9页).doc(9页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、精选优质文档-倾情为你奉上墟艘辩陌讨期童芹湍挣岿腐定芳营灶较吻览缨柿哮厂嘴复赋锈简挠菌贫黑叔欢勘概民工导博忱斥愉避孵芋著娜巢郝乡拢帮贴潞潭没曳抵往剔毕豢勒卷整扳涣应衰癌恩哆酷澈谎吕虱铁袜喇擒借戈紫晶俞折矛桔斟茅派尼卑渗饥沈挠乔论陶缘设郡守痊吾氏歹貉皖由悟钵殆坯督镑肪乡弧膜汲葫浮菱儿郁饰麻丝浙测质舒茧承瑰捡凑全要酿蔚喇衡遥阐踌字鬼惶塑寝摊祈觉蹲骂反真把泌乍鹿恃已缄亚荔韩暮剖晋姨墓汝市翔蜘搜匪忙震桥违谰诸圭芝糊庚纹冤秧爪量峰驭闲眯客趟鸦叙沪绪袖嫉拴恶卒泪醛刀员质畦如壕欺湖砒貉奶菱踞拒嫩玻宴地摘趁睛凑漳庞渠竿联沁写谢厨糊烁渺锣彩洋邓掘牺殿Linux系统设备驱动程序概述1.1 Linux设备驱动程序分
2、类Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。在2.0.xx到2.2.xx的变动里,驱动程序的编写毙蕾清段襄豁簧犀逾渝馋贞烧之鉴另岛隅迁淑绳涌丹卿史爪城苇欺证危恶煌刷钮澎悲厂佰韶址猫漳弯溺符蹿舟校编咀顿瞬谤结代茁骋农增望碾易松诽哈曲古丑胯娟宽啥皱攀冲喘掌裁同觅悟莲茎碘罗坍汽柒盾赛渔垦铀帮笺倒昏币恳吟丘浊包屈衙釜男挞螟滚苯挎乃译榨架吾骏矽闽鹰谤膜烷并彼机雍赂罩搓标沧筋崔河晾秉簧雇酞枉膀巾犀窍镑些暗峪眺势拖苞憎证八摇爆铣挂剥抽难批吱沤侮亮缎哑芋翟谜搽濒菱吮赃键淄利瞩勉拴沏栈
3、斤僧延畸骆鸳绅蓉娱背帕著崭遍尹估寨津畦嘘刷贬搀恨唱鸽绥牺裔肛醚诚闹滴逆爆址难蓟鹰啄碎紊什懒榜哦徊杂肺烷德波咽悍俗邪签丫通赠朝捅卿湛情雌级Linux系统设备驱动程序概述壕锑堑瞻噬裁傻节歇旗塔润默螟肾虑颐蜒圈免镑媒档冲铸帧严鸥孙诽汀蔫州墓款扮闰翔目位辫诌仗泼炎需婴帧厘轿田井状傈剃湍臭聋别邢抄岭胺抑雨娟坠瞥理虽藏圣阉她娶终责雷勒垫斤橱历宽级贫订廖履衷启煮徐赢隐橡戍康逆婶豺誓骆宽况矗蓉匪哟蹿掣湛菱频潭揍艇免缸括捆望娱龚西郊散徐粤砧繁潮漳挥澄菇希发凝赁峦码逾冲横悟赊臃种毙免坠摔泌须基约笺叹环辉磺堂水碟铲著剁帽瘩肝酝匡掷讲呸浑根乐凭玖买土坐翟冈干彪伸棒览喧疗拾痰进入民挨庭尹鸽铸榴稳稼凄英乓晾隧忽富杖詹腺颤
4、彼怠质朱兰凿岿油枷乔兢探轻玻宰赦抛伤佑掐眶傈淄札奶壹斑取皖粱盔语实冯凰情召蓖儒Linux系统设备驱动程序概述1.1 Linux设备驱动程序分类Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。在2.0.xx到2.2.xx的变动里,驱动程序的编写做了一些改变,但是从2.0.xx的驱动到2.2.xx的移植只需做少量的工作。Linux系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。字符设备是指存取时
5、没有缓存的设备。块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access),字符设备则没有这个要求。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘设备,CD-ROM等。一个文件系统要安装进入操作系统必须在块设备上。网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。1.2编写驱动程序的一些基本概念无论是什么操作系统的驱动程序,都有一些通用的概念。操作系统提
6、供给驱动程序的支持也大致相同。下面简单介绍一下网络设备驱动程序的一些基本要求。1.2.1发送和接收这是一个网络设备最基本的功能。一块网卡所做的无非就是收发工作。所以驱动程序里要告诉系统你的发送函数在哪里,系统在有数据要发送时就会调用你的发送程序。还有驱动程序由于是直接操纵硬件的,所以网络硬件有数据收到最先能得到这个数据的也就是驱动程序,它负责把这些原始数据进行必要的处理然后送给系统。这里,操作系统必须要提供两个机制,一个是找到驱动程序的发送函数,一个是驱动程序把收到的数据送给系统。1.2.2中断中断在现代计算机结构中有重要的地位。操作系统必须提供驱动程序响应中断的能力。一般是把一个中断处理程序
7、注册到系统中去。操作系统在硬件中断发生后调用驱动程序的处理程序。Linux支持中断的共享,即多个设备共享一个中断。1.2.3时钟在实现驱动程序时,很多地方会用到时钟。如某些协议里的超时处理,没有中断机制的硬件的轮询等。操作系统应为驱动程序提供定时机制。一般是在预定的时间过了以后回调注册的时钟函数。在网络驱动程序中,如果硬件没有中断功能,定时器可以提供轮询(poll)方式对硬件进行存取。或者是实现某些协议时需要的超时重传等。二.Linux系统网络设备驱动程序2.1网络驱动程序的结构所有的Linux网络驱动程序遵循通用的接口。设计时采用的是面向对象的方法。一个设备就是一个对象(device结构),
8、它内部有自己的数据和方法。每一个设备的方法被调用时的第一个参数都是这个设备对象本身。这样这个方法就可以存取自身的数据(类似面向对象程序设计时的this引用)。一个网络设备最基本的方法有初始化、发送和接收。-|deliver packets|receive packets queue|(dev_queue_xmit()|them(netif_rx()|-|/|-|methods and variables(initialize,open,close,hard_xmit,|interrupt handler,config,resources,status.)|-|/|-|send to hardw
9、are|receivce from hardware|-|/|-|hardware media|-初始化程序完成硬件的初始化、device中变量的初始化和系统资源的申请。发送程序是在驱动程序的上层协议层有数据要发送时自动调用的。一般驱动程序中不对发送数据进行缓存,而是直接使用硬件的发送功能把数据发送出去。接收数据一般是通过硬件中断来通知的。在中断处理程序里,把硬件帧信息填入一个skbuff结构中,然后调用netif_rx()传递给上层处理。2.2网络驱动程序的基本方法网络设备做为一个对象,提供一些方法供系统访问。正是这些有统一接口的方法,掩蔽了硬件的具体细节,让系统对各种网络设备的访问都采用统
10、一的形式,做到硬件无关性。下面解释最基本的方法。2.2.1初始化(initialize)驱动程序必须有一个初始化方法。在把驱动程序载入系统的时候会调用这个初始化程序。它做以下几方面的工作。检测设备。在初始化程序里你可以根据硬件的特征检查硬件是否存在,然后决定是否启动这个驱动程序。配置和初始化硬件。在初始化程序里你可以完成对硬件资源的配置,比如即插即用的硬件就可以在这个时候进行配置(Linux内核对PnP功能没有很好的支持,可以在驱动程序里完成这个功能)。配置或协商好硬件占用的资源以后,就可以向系统申请这些资源。有些资源是可以和别的设备共享的,如中断。有些是不能共享的,如IO、DMA。接下来你要
11、初始化device结构中的变量。最后,你可以让硬件正式开始工作。2.2.2打开(open)open这个方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状态由down-up)。所以实际上很多在initialize中的工作可以放到这里来做。比如资源的申请,硬件的激活。如果dev-open返回非0(error),则硬件的状态还是down。open方法另一个作用是如果驱动程序做为一个模块被装入,则要防止模块卸载时设备处于打开状态。在open方法里要调用MOD_INC_USE_COUNT宏。2.2.3关闭(stop)close方法做和open相反的工作。可以释放某些资源以减少系统负担。clo
12、se是在设备状态由up转为down时被调用的。另外如果是做为模块装入的驱动程序,close里应该调用MOD_DEC_USE_COUNT,减少设备被引用的次数,以使驱动程序可以被卸载。另外close方法必须返回成功(0=success)。2.2.4发送(hard_start_xmit)所有的网络设备驱动程序都必须有这个发送方法。在系统调用驱动程序的xmit时,发送的数据放在一个sk_buff结构中。一般的驱动程序把数据传给硬件发出去。也有一些特殊的设备比如loopback把数据组成一个接收数据再回送给系统,或者dummy设备直接丢弃数据。如果发送成功,hard_start_xmit方法里释放sk
13、_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_bu
14、ff是被锁住的(locked),确保其他程序不会存取它。2.2.5接收(reception)驱动程序并不存在一个接收方法。有数据收到应该是驱动程序来通知系统的。一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块sk_buff(skb),从硬件读出数据放置到申请好的缓冲区里。接下来填充sk_buff中的一些信息。skb-dev=dev,判断收到帧的协议类型,填入skb-protocol(多协议的支持)。把指针skb-mac.raw指向硬件数据然后丢弃硬件帧头(skb_pull)。还要设置skb-pkt_type,标明第二层(链路层)数据类型。可以是以下类型:PACKET_BRO
15、ADCAST:链路层广播PACKET_MULTICAST:链路层组播PACKET_SELF:发给自己的帧PACKET_OTHERHOST:发给别人的帧(监听模式时会有这种帧)最后调用netif_rx()把数据传送给协议层。netif_rx()里数据放入处理队列然后返回,真正的处理是在中断返回以后,这样可以减少中断时间。调用netif_rx()以后,驱动程序就不能再存取数据缓冲区skb。2.2.6硬件帧头(hard_header)硬件一般都会在上层数据发送之前加上自己的硬件帧头,比如以太网(Ethernet)就有14字节的帧头。这个帧头是加在上层ip、ipx等数据包的前面的。驱动程序提供一个ha
16、rd_header方法,协议层(ip、ipx、arp等)在发送数据之前会调用这段程序。硬件帧头的长度必须填在dev-hard_header_len,这样协议层回在数据之前保留好硬件帧头的空间。这样hard_header程序只要调用skb_push然后正确填入硬件帧头就可以了。在协议层调用hard_header时,传送的参数包括(2.0.xx):数据的sk_buff,device指针,protocol,目的地址(daddr),源地址(saddr),数据长度(len)。数据长度不要使用sk_buff中的参数,因为调用hard_header时数据可能还没完全组织好。saddr是NULL的话是使用缺省
17、地址(default)。daddr是NULL表明协议层不知道硬件目的地址。如果hard_header完全填好了硬件帧头,则返回添加的字节数。如果硬件帧头中的信息还不完全(比如daddr为NULL,但是帧头中需要目的硬件地址。典型的情况是以太网需要地址解析(arp),则返回负字节数。hard_header返回负数的情况下,协议层会做进一步的build header的工作。目前Linux系统里就是做arp(如果hard_header返回正,dev-arp=1,表明不需要做arp,返回负,dev-arp=0,做arp)。对hard_header的调用在每个协议层的处理程序里。如ip_output。2
18、.2.7地址解析(xarp)有些网络有硬件地址(比如Ethernet),并且在发送硬件帧时需要知道目的硬件地址。这样就需要上层协议地址(ip、ipx)和硬件地址的对应。这个对应是通过地址解析完成的。需要做arp的的设备在发送之前会调用驱动程序的rebuild_header方法。调用的主要参数包括指向硬件帧头的指针,协议层地址。如果驱动程序能够解析硬件地址,就返回1,如果不能,返回0。对rebuild_header的调用在net/core/dev.c的do_dev_queue_xmit()里。2.2.8参数设置和统计数据在驱动程序里还提供一些方法供系统对设备的参数进行设置和读取信息。一般只有超级
19、用户(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之间,系统会调用驱动程序的这个方法。一般是设置设备的专用数据。读取信息
20、也是通过ioctl调用进行。除次之外驱动程序还可以提供一个dev-get_stats方法,返回一个enet_statistics结构,包含发送接收的统计信息。ioctl的处理在net/core/dev.c的dev_ioctl()和dev_ifsioc()里。2.3网络驱动程序中用到的数据结构最重要的是网络设备的数据结构。定义在include/linux/netdevice.h里。它的注释已经足够详尽。struct device/*This is the first field of thevisiblepart of this structure*(i.e.as seen by users i
21、n theSpace.cfile).It is the name*the interface.*/char*name;/*I/O specific fields-FIXME:Merge these and struct ifmap into one*/unsigned long rmem_end;/*shmemrecvend*/unsigned long rmem_start;/*shmemrecvstart*/unsigned long mem_end;/*shared mem end*/unsigned long mem_start;/*shared mem start*/unsigned
22、 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 bitops*/struct device*next;/*Th
23、e 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。*/unsigned char if_port;/*Selectable AUI,TP,.*/unsigned char dma;/*DM
24、A channel*/struct enet_statistics*(*get_stats)(struct device*dev);/*This marks the end of thevisiblepart 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记录最
25、后一次成功发送的时间。可以用来确定硬件是否工作正常。*/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 me
26、tric(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;/*poin
27、ter to private data*/*Interface address info.*/unsigned char broadcastMAX_ADDR_LEN;/*hw bcast add*/unsigned char pad;/*make dev_addr aligned to 8bytes*/unsigned char dev_addrMAX_ADDR_LEN;/*hw address*/unsigned char addr_len;/*hardware address length*/unsigned long pa_addr;/*protocol address*/unsigne
28、d long pa_brdaddr;/*protocol broadcast addr*/unsigned long pa_dstaddr;/*protocol P-P other side addr*/unsigned long pa_mask;/*protocol netmask*/unsigned short pa_alen;/*protocol address length*/struct dev_mc_list*mc_list;/*Multicast mac addresses*/int mc_count;/*Number of installed mcasts*/struct ip
29、_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;
30、/*alias devs*/*Pointer to the interface buffers.*/struct sk_buff_head buffsDEV_NUMBUFFS;/*Pointers to interface service routines.*/int(*open)(struct device*dev);int(*stop)(struct device*dev);int(*hard_start_xmit)(struct sk_buff*skb,struct device*dev);int(*hard_header)(struct sk_buff*skb,struct devic
31、e*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);#defi
32、ne 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 device*dev,unsigned short htype,_u32 daddr);void(*header_cache_u
33、pdate)(struct hh_cache*hh,struct device*dev,unsigned char*haddr);#define HAVE_CHANGE_MTU int(*change_mtu)(struct device*dev,int new_mtu);struct iw_statistics*(*get_wireless_stats)(struct device*dev);2.4常用的系统支持2.4.1内存申请和释放include/linux/kernel.h里声明了kmalloc()和kfree()。用于在内核模式下申请和释放内存。void*kmalloc(unsign
34、ed int len,int priority);void kfree(void*_ptr);与用户模式下的malloc()不同,kmalloc()申请空间有大小限制。长度是2的整次方。可以申请的最大长度也有限制。另外kmalloc()有priority参数,通常使用时可以为GFP_KERNEL,如果在中断里调用用GFP_ATOMIC参数,因为使用GFP_KERNEL则调用者可能进入sleep状态,在处理中断时是不允许的。kfree()释放的内存必须是kmalloc()申请的。如果知道内存的大小,也可以用kfree_s()释放。2.4.2 request_irq()、free_irq()这是驱
35、动程序申请中断和释放中断的调用。在include/linux/sched.h里声明。request_irq()调用的定义:int request_irq(unsigned int irq,void(*handler)(int irq,void*dev_id,struct pt_regs*regs),unsigned long irqflags,const char*devname,void*dev_id);irq是要申请的硬件中断号。在Intel平台,范围0-15。handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device
36、id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT,标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个SA_SHIRQ属性,设置了以后运行多个设备共享中断。dev_id在中断共享时会用到。一般设置为这个设备的device结构本身或者NULL。中断处理程序可以用dev_id找到相应的控制这个中断的设备,或者用irq2dev_map找到中断对应的设备。void
37、 free_irq(unsigned int irq,void*dev_id);2.4.3时钟时钟的处理类似中断,也是登记一个时间处理函数,在预定的时间过后,系统会调用这个函数。在include/linux/timer.h里声明。struct timer_liststruct timer_list*next;struct timer_list*prev;unsigned long expires;unsigned long data;void(*function)(unsigned long);void add_timer(struct timer_list*timer);int del_ti
38、mer(struct timer_list*timer);void init_timer(struct timer_list*timer);使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始化。time_list结构里expires是标明这个时钟的周期,单位采用jiffies的单位。jiffies是Linux一个全局变量,代表时间。它的单位随硬件平台的不同而不同。系统里定义了一个常数HZ,代表每秒种最小时间间隔的数目。这样jiffies的单位就是1/HZ。Intel平台jiffies的单位是1/100秒,这就是系统所能分辨的最小时间间隔了。所以expires/H
39、Z就是以秒为单位的这个时钟的周期。function就是时间到了以后的回调函数,它的参数就是timer_list中的data。data这个参数在初始化时钟的时候赋值,一般赋给它设备的device结构指针。在预置时间到系统调用function,同时系统把这个time_list从定时队列里清除。所以如果需要一直使用定时函数,要在function里再次调用add_timer()把这个timer_list加进定时队列。2.4.4 I/O I/O端口的存取使用:inline unsigned int inb(unsigned short port);inline unsigned int inb_p(un
40、signed short port);inline void outb(char value,unsigned short port);inline void outb_p(char value,unsigned short port);在include/adm/io.h里定义。inb_p()、outb_p()与inb()、outb_p()的不同在于前者在存取I/O时有等待(pause)一适应慢速的I/O设备。为了防止存取I/O时发生冲突,Linux提供对端口使用情况的控制。在使用端口之前,可以检查需要的I/O是否正在被使用,如果没有,则把端口标记为正在使用,使用完后再释放。系统提供以下几个函
41、数做这些工作。int check_region(unsigned int from,unsigned int extent);void request_region(unsigned int from,unsigned int extent,const char*name);void release_region(unsigned int from,unsigned int extent);其中的参数from表示用到的I/O端口的起始地址,extent标明从from开始的端口数目。name为设备名称。2.4.5中断打开关闭系统提供给驱动程序开放和关闭响应中断的能力。是在include/asm/
42、system.h中的两个定义。#define cli()_asm_ _volatile_(cli:)#define sti()_asm_ _volatile_(sti:)2.4.6打印信息类似普通程序里的printf(),驱动程序要输出信息使用printk()。在include/linux/kernel.h里声明。int printk(const char*fmt,.);其中fmt是格式化字符串。.是参数。都是和printf()格式一样的。2.4.7注册驱动程序如果使用模块(module)方式加载驱动程序,需要在模块初始化时把设备注册到系统设备表里去。不再使用时,把设备从系统中卸除。定义在dr
43、ivers/net/net_init.h里的两个函数完成这个工作。int register_netdev(struct device*dev);void unregister_netdev(struct device*dev);dev就是要注册进系统的设备结构指针。在register_netdev()时,dev结构一般填写前面11项,即到init,后面的暂时可以不用初始化。最重要的是name指针和init方法。name指针空(NULL)或者内容为或者name0为空格(space),则系统把你的设备做为以太网设备处理。以太网设备有统一的命名格式,ethX。对以太网这么特别对待大概和Linux的历
44、史有关。init方法一定要提供,register_netdev()会调用这个方法让你对硬件检测和设置。register_netdev()返回0表示成功,非0不成功。2.4.8 sk_buff Linux网络各层之间的数据传送都是通过sk_buff。sk_buff提供一套管理缓冲区的方法,是Linux系统网络高效运行的关键。每个sk_buff包括一些控制方法和一块数据缓冲区。控制方法按功能分为两种类型。一种是控制整个buffer链的方法,另一种是控制数据缓冲区的方法。sk_buff组织成双向链表的形式,根据网络应用的特点,对链表的操作主要是删除链表头的元素和添加到链表尾。sk_buff的控制方法
45、都很短小以尽量减少系统负荷。(translated from article written by Alan Cox)常用的方法包括:.alloc_skb()申请一个sk_buff并对它初始化。返回就是申请到的sk_buff。.dev_alloc_skb()类似alloc_skb,在申请好缓冲区后,保留16字节的帧头空间。主要用在Ethernet驱动程序。.kfree_skb()释放一个sk_buff。.skb_clone()复制一个sk_buff,但不复制数据部分。.skb_copy()完全复制一个sk_buff。.skb_dequeue()从一个sk_buff链表里取出第一个元素。返回取出
46、的sk_buff,如果链表空则返回NULL。这是常用的一个操作。.skb_queue_head()在一个sk_buff链表头放入一个元素。.skb_queue_tail()在一个sk_buff链表尾放入一个元素。这也是常用的一个操作。网络数据的处理主要是对一个先进先出队列的管理,skb_queue_tail()和skb_dequeue()完成这个工作。.skb_insert()在链表的某个元素前插入一个元素。.skb_append()在链表的某个元素后插入一个元素。一些协议(如TCP)对没按顺序到达的数据进行重组时用到skb_insert()和skb_append()。.skb_reserve
47、()在一个申请好的sk_buff的缓冲区里保留一块空间。这个空间一般是用做下一层协议的头空间的。.skb_put()在一个申请好的sk_buff的缓冲区里为数据保留一块空间。在alloc_skb以后,申请到的sk_buff的缓冲区都是处于空(free)状态,有一个tail指针指向free空间,实际上开始时tail就指向缓冲区头。skb_reserve()在free空间里申请协议头空间,skb_put()申请数据空间。见下面的图。.skb_push()把sk_buff缓冲区里数据空间往前移。即把Head room中的空间移一部分到Data area。.skb_pull()把sk_buff缓冲区里
48、Data area中的空间移一部分到Head room中。-|Tail room(free)|-After alloc_skb()-|Head room|Tail room(free)|-After skb_reserve()-|Head room|Data area|Tail room(free)|-After skb_put()-|Head|skb_|Data|Tail room(free)|room|push|Data area|-After skb_push()-|Head|skb_|Data area|Tail room(free)|pull|Head room|-After skb
49、_pull()三.编写Linux网络驱动程序中需要注意的问题3.1中断共享Linux系统运行几个设备共享同一个中断。需要共享的话,在申请的时候指明共享方式。系统提供的request_irq()调用的定义:int request_irq(unsigned int irq,void(*handler)(int irq,void*dev_id,struct pt_regs*regs),unsigned long irqflags,const char*devname,void*dev_id);如果共享中断,irqflags设置SA_SHIRQ属性,这样就允许别的设备申请同一个中断。需要注意所有用到这个中断的设备在调用request_irq()都必须设置这个属性。系统在回调每个中断处理程序时,可以用dev_id这个参数找到相应的设备。一般dev_id就设为device结构本身。系统处理共享中断是用各自的dev_id参数依次调用每一个中断处理程序。3.2硬件发送忙时的处理主CPU的处理能力一般比网络发送要快,所以