关键词不能为空

当前您在: 主页 > 英语 >

以snull为例分析linux网络驱动程序的技术文档

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-18 05:05
tags:

-

2021年2月18日发(作者:波浪)



snull


为例分析


linux


网卡驱动的技术文档



网络 设备,即网络接口,


在操作系统核心级上处理包的发送和接收。


与块设备一样,



络接口也在特定的数据结构之中注册自己,< /p>


以利于在跟外界进行包交换的时候被调用;


但是

< br>它不象块设备一样存在于文件系统当中。


二者最主要的区别在于:


块设备是收到要求,


才向


内核发送一个块缓冲区的内容 ;网络接口是主动向内核推入从接口进入的包。


Linux


核心 的


网络子系统,在设计的时候是完全独立于协议的,对网络协议(如

IP



IPX


或其它)和硬


件协议


(以太网对令牌环等等)


都是一样的。


一个网络接口的驱动和内核的交互是一次处理


一个网络包,


这样就可以让协议问题巧妙的隐藏在驱动后面,


也可以让物理上的传 输隐藏在


协议后面。



在下面的讲述中 ,我们将以一个基于内存的(即纯软件的)模块化网络接口,


SNULL



来作为示例。为了简化讨论,我们让


snull


使用以太网协议并传输


IP


包。

< p>



How snull Is Designed


SNULL


的设计



snull


模块有两个接口,并且不同于


loopback


接口,它看起来就像两个外部的连接,但


实 际上是依赖于一个计算机本身。


我们把


snull


指定上


IP


地址,


这样并不 影响通用性的编码,


只是在示例的时候比较方便。两个接口


sn 0



sn1


对应两个

< br>C


类网络


snullnet0



snullnet1



local0< /p>



local1


是对

sn0



sn1


接口指定的


IP


地址,而


remote0



remote1


分别是


sn ullnet0



snullnet1


网络中的两个主机。我们在


/etc/networks


文件中 加入:




snullnet0


192.168.0.0



snullnet1


192.168.1.0



/etc/hosts


文件中加入:




192.168.0.88


local0



192.168.0.99


remote0



192.168.1.99


local1



192.168.1.88


remote1


并使用下面的命令建立路由信息:




# ifconfig sn0 local0



# route add -net snullnet0 netmask 255.255.255.0 sn0



# ifconfig sn1 local1



# route add -net snullnet1 netmask 255.255.255.0 sn1

< p>
由于


Linux


的内核是不会把一个包从一个接口 直接传送到本机另一个接口的,


因此在实


现上采用了一些技巧,


就是在传输数据的过程中修改源和目的地址。


换句话说就是让发 出的


包被本地的另一个接口所接收,但是接收接口由不被认为是本地的。具体的做法就是 修改


IP


地址,把目标地址的第三个字节置反,那么发往


remote0(192.168.0.99)


的包就变成了去往


local1(192.168.1.99)


的包,回到了本机的另 一个接口。这样我们就可以让


remote


端的接口变


得可达,也显示了怎样让通过


snull


到达< /p>


remote0



remote1.



The Physical Transprot of Packets


包的物理传输



就数 据的传输过程来说,


snull


接口应该算作以太网一类的,示 例代码也使用了内核对


以太网的支持。使用以太网模型建立


sn ull


首先是因为以太网设备太为通用了。其次


snull


也可以在接口上运行


tcpdump


,当然 ,运行


tcpdump


的话,接口就要叫做

ethx


,而不是


snx



snull


模块已经准备好将自己声明为


et hx.


如果在


insmod


命令中指 定


eth=1



则该功能就被选


中。如果忘了给


snull


起一个

< p>
eth


的名字,


tcpdump

< br>就会拒绝转储接口,并返回一个


unknown


physical layer type


的错误。



在实际中,


snull


的代码还会对包进行侦听甚至修改它,因为这是要求代码做的。


snull


的代码会修改每个包的


IP


包头中的源、目的和校验和,但不检查这个包是否真正的包含


IP< /p>


信息。这种修改方式捣毁非


IP


包。




Connecting to the Kernel


连接内核



我们通过剖 析


snull


的源程序来看看网络驱动的结构。同时参看一些驱 动程序的源码,


有助于下面的讨论。内核的驱动程序,由易到难,可以参看


loopback.c, plip.c, 3c509.c;


可以


作为示例代码还有


skeleton.c


,< /p>


尽管它并不能真正运行。


3c59x.c



tulip.c



pci

< p>
的并运用


DMA


的例子。




Module Loading


模块加载



当一个驱动模块载入运行的 内核中的时候,


它需要申请资源并向核心提供调用接口。



请资源并没有什么特别的,


驱动要探测它的设备和硬件的位置



I/O


端口和


IR Q


中断请求号)



但是并不进行登记。


一个网络驱动的登记是通过它的



init_module


函数完成的,


这一点不同


于字符设备和块设备的驱动程序


,


不同于取得一个设备描述符或者文件描述符,对于网络设


备,

< br>有一张全局的网络设备列表,


驱动程序对每一个新探测到的接口都会在该表中插入 一个


数据结构。



每个接口由一个



struct device


项来描述。两个



snull


接口



sn0




sn1


的结构声明如下:




char snull_names[16]; /* 2 8-byte buffers */


struct device snull_devs[2] = {



{




snull_name,


/* name -- set at load time */




0, 0, 0, 0, /* shmem addresses */




0x000,



/* ioport */




0,



/* irq line */




0, 0, 0,


/* various flags, init to 0 */




NULL,



/* next ptr */




snull_init,


/


* init function, fill other fields with NULLs */



},



{




snull_name+8, /* name -- set at load time */




0, 0, 0, 0, /* shmem addresses */




0x000,



/* ioport */




0,



/* irq line */




0, 0, 0,


/* various flags, init to 0 */




NULL,



/* next ptr */




snull_init,


/


* init function, fill other fields with NULLs */



},


}



注意第一个域



name


,指向一个在打开时填充的静态缓冲区。在这种方式下,接口的名


字可以晚一 些




选择,万一你在该结构中使用了 一个明确的缓冲区,比如



那么




码就不能可靠地工作。


因为编译会崩溃于重 复串:


以单缓冲结束,


却有两个指针来指向。

< br>此外,



编译器可能甚至选择在只读内存中保存常量串, 而不是所预想的。



前面的代码端已经已经明确的利用了



struct device




name


域和



init


域。


name



,



存接 口的名字(名字时标示接口的串)



eth0,eth1...


安数字升序。驱动可以为接口写入一个


名字,

< br>或者允许动态指定


(就是从


0


开 始按数字升序)



如果在载入时指定



eth=1



init_module


就会使用动态指定。默认的名字都是由



init_module


指定的:





if (!snull_eth) { /* call them




memcpy(snull_devs[0].name,




memcpy(snull_devs[1].name,



} else { /* use automatic assignment */




snull_devs[0].name[0] = snull_devs[1].name[0] = '';



}


init


域是一个函数指针。无论 在何时登记一个设备,内核都会让驱动自己进行初始化。


初始化意味着它测物理接口,以 正确的值填写



device


结构。 如果初始化失败,该结构就不


被连接到全局的网络设备表中。


这 种特有的建立方法在系统引导的时候最为有用,


每个驱动


都试图 登记自己的设备,


但是只有确实存在的设备才连接到全局设备表。


由于实际的初始化


是在别处执行的,所以



init_module


只需要一个语句做就够了:






for (i=0; i<2; i++)




if ( (result = register_netdev(snull_devs + i)) )






printk(






result, snull_devs[i], name);




else device_present++;



Initalizing Each Device


初始化每个设备



初始化函数


init


也经常叫做


probe



snull_init


的核心代码是这样的:





ether_setup(dev); /* assign some of the fields */





dev->open



= snull_open;



dev->stop



= snull_release;



dev->set_config



= snull_config;



dev->had_start_xmit


= snull_tx;



dev->do_ioctl



= snull_ioctl;



dev->get_stats


= snull_stats;



dev->rebuild_header


= snull_rebuild_header;



/* keep the default flags, just add NOARP */



/* NOTE: every real Ethernet interface is ARP-aware and won't set this flag!! */



/*








plip interface (an Ethernet like) can work without ARP support, like snull */



dev->flags









|= IFF_NOARP


/* xxx_probe is usually a big function with more 200 lines of code */



snull


模块声明一个


snull_priv


数据结构给


priv


使用

(priv



*dev


中的一个域


)


,其实包括


struct


enet_statistics


结构,那是存放接口静态信息的标准位 置。下面的是


snull_init


分配


dev->priv


的代码:






dev->priv = kmalloc(sizeof(struct snull_priv), GFP_KERNEL);


//














~~~Get Free Page!



if (DEV->PRIV == null)




return -ENOMEM;



memset(dev->priv, 0, sizeof(struct snull_priv));




Module Unloading


模块卸载




函数


cleanup_module


的工作首先释放分配给设备 私有的内存,然后在全局网络设备表


上注销自己的设备:





void cleanup_module(void)



{




int i;







for (i=0; i<2; i++) {





kfree(snull_devs[i].priv);





unregister_netdev(snull_devs + i);




}




return;



}



Modularized and Non-Modularized Drivers


模块化和非模块化驱动



< p>
模块化驱动是指驱动程序作为模块,


可以在运行的系统中插入和删除。


非模块化就是指


设备驱动内建入系统的内核,作为内核的一部分。对 于字符和块设备驱动


,


这点上没有显著


区别


,


但是网络设备驱动则不同。


一个 网络设备驱动内建入内核时,


它并不声明自己的


device< /p>


结构,而由在



drivers/net/Space.c


中声明的结构来代替它。


Space.c

声明一个所有网络设备


的链表,既有驱动特定的结构又有通用的以太网设备结构。< /p>




以太网驱动根本不关心它们的



device


结构,而使用通用的结构。这种通用的以太网设


备结构将声明



ethif_probe


作为它们的初始化函数。在内核中插 入一个新的以太网接口只需


要在



ethif_probe


中加入一个对驱动的初始化函数的调 用。对于非以太网


(non- eth)


的驱动,


需要在



Space.c


中加入它们的



device


结构。在这两种情况下,如果要讲一个驱动正确 连接


到内核的话,都是只修改



Space.c


就可以了。




系统引导的时候,


网络初始化代码会 轮巡



(loops through)


所有的



device


结构,


并且传


递一个指向设备本身的指针,来调用探 测函数



(dev->init)


。如 果探测成功,


Space.c


就初始




device


结构。建立驱动的方式允许将设备以升序命名为



eht0,eth1,


等等,而不改变每个


设备的< /p>



name


域。




当一个模块化设备驱动载入的时候,它声明自己的



device


结构,即便它所控制的接口

是一个以太网接口。




研读



Space.c and net_init.c



对于驱动建立的介绍,


目的在于强调初始化设备的方法


,



果驱动模块包含一个预先填好的


device


结构, 那么在



struct device


结构中



加入新的域时,


它就不能配合内核的初始化技术,也不是向前兼容的。




THE DEVICE STRRUCTURE IN DETAIL


device


结构详解



device


结构是网络设备驱动的核心



device


结构分为可见和不可见两部分。前者由明确指定的静态结构组成,正如 前面在



snull


中出现的两个条目 。后者(余下的部分)




在内部使用 的,其中有些由驱动访问(例


如在初始化的时候分配的条目)


, 还有些并不触及。




The Visible Head


可见部分




char *name;



如果第一各字符是


0(


即表示空


NULL)


或者一个空格,



register_netdev


就把它指定为



ethn


,给一个合适的



n


值。




unsigned long rmem_end;



unsigned long rmem_start;



unsigned long mem_end;



unsigned long mem_start;



这几个域记录由设备占用的共享内存的起始和结束地址。



如果接收和发送内存是分开


的话,那么


me m


表示发送内存,


rmem


表示接收内 存。


mem_start




mem_end


在系统引导


的时候可以从核心的命令行



确定,它们的值由



ifconfig


命令获得。



rmem


域从不在


driver


以外被引用。按照习惯


,




end


域就可以由



(end


-


start


)得到可用板上



(on-board)


内存的


大小。




unsigned long base_addr;



基本输入


/


输出基址。


与前面的一样,


该域是在设备探测时指定的。



ifconfig


命令可以


显示或者修改当前值。


base_addr


可以在系统引导或者(模块)载入时明确指定。






unsigned char irq;


< br>指定的中断号,


dev->irq


的值可由


ifconfig


列出,在接口列出以后。该值一般可以在系


统引导或者(模块)载入时置,也可以后来用



ifconfig


修改。






unsigned char start;



unsigned char interrupt;



这两个域时二进制标志。


start


在一般 在设备打开始置,在关闭时清除,



当接口准备好


可以操作时是非零值。


interrupt


用来告诉 高层的代码接口上有中断到来,并且正在中断服务


中。




unsigned long tbusy;



传输忙。只要驱动不能再接收一个要传输的新的包的时候(例 如所有输出缓冲区满)



该域就应置为非零值。





long


而不用



char

< br>类型,因为原子位操作有时用来避免出现竞争


(原子操作不会被中断)

< p>





struct device *next;



用来保持全局设备表,与驱动无关。




int (*init)(struct device *dev)



初始化函数。该域一般是在



device


结构中最后一个明确列出的。




*init


是一个指针,


指向一个函 数,


该函数的返回值是



int


类型。


如果没有扩号,


< br>


init


成为函数名返回类型是

int


类型的指针。





The Hidden Fields


隐藏域




device


结构包括一些另外的域,一般再设备初始化时指 定。其中某些域传达



(convey)

接口信息,有些仅仅为了有益于驱动程序本身(也就是说并不为


kernel


所用)


,还有其它的


域,主要是设备方法


(device methods)


,它作为



核心驱动的接口。




下面将分为三组介绍,与实际顺序(这一点也不重要)无关。




Interface information



接口信息





大多数的接口信息都由



ether_setup


函数正确建立。以太网卡的多数域都 可以依靠


这个通用型函数,唯有



flags




dev_addr


是依赖于设备的,


必须在初始化的时候明确指定。





某些非以太网接口卡可以用类似于



ether_setup


的公用函数


driver/net/net_init.c



export s tr_setup (token ring) and fddi_setup.


若你的设备不包含在这些类型中,你需要手工指


定所有这些域。





unsigned short hard_header_len;



传输的包 的


IP


包头之前的字节数,以太网的



hard_header_len


值是


14







unsigned



short mtu;




最大传输单元,该玉在包 传输过程中被网络层使用。以太网




mtu



1500

< br>字节。





__u32 tx_queue_len;



设备传输队列可以挂的最大帧数。



ether_setup


把它设为


1 00



但是你可以改变。例如,



plip


就用


10

< br>以避免系统内存的浪费(


plip


比实际的以太网接口的 吞吐量小)







unsigned short type;



接口的硬件类型。


ARP


用该域来知 道接口所支持的硬件地址类型。对于以太网接口,


ether_setup


会把它设置为



ARPHRD_ETHER.




unsigned char addr_len;



unsigned char broadcast[MAX_ADDR_LEN];



unsigned char dev_addr[MAX_ADDR_LEN];



以太网 的


MAC


地址长度是


6


个字节,广播地址是由


6



0 xff


字节组成的,


ether_setup


会把这些值设置正确。另一



方面,设 备地址必须由设备特定的方式从接口板上读取,并且


驱动程序要把它拷贝到



dev_addr.


硬件地址用来在包送到驱动 程序做传输之前,


产生正确的


以太网的包头。

< br>snull


设备没有用到物理接口,它会自造一个硬件地址。





unsigned


?


hort family;



接口的地址族最常用的是



AF_INET.


接口一般不需要看这个域或者为它指定值。





unsigned short pa_alen;



协议地址长度



Protocol Address Length.




AF_INET


设为


4


字节。接口不需要修改。




unsigned long pa_addr;



unsigned long pa_brdaddr;



unsigned long pa_mask;



刻画接口的三个地址:


接口地址、< /p>


广播地址和网络掩码,它们的值是协议特定的


(也就


是说它们是协议地址)



如果



dev->family




AF_INET


,


它们就是



IP


地址。这些域是由



ifconfig


指定的,对驱动来说是只读的。




unsigned long pa_dstaddr;



点对点的接口,




plip




ppp


用该域来纪录连接的另一端的



IP


值,


该域也是只读的。




unsigned short flags;



接口标志。标志域



flags


包括下面的位值。


IFF_


前缀表示



Flags


。一些标志


由内核管理,其它




在接口 初始化的时候置,用来断言不同的接口功能(或者没有功能)



合法的标志是:





IFF_UP





内核置该标志表示接口是活跃的,该标志对驱动是只读的。





IFF_BROADCAST





声明接口的广播地址是合法的。以太网卡支持广播。





IFF_DEBUG





调试模式,


Debug Mode,


用来控制



printk

< p>
调用或者其它调试目的输出的冗长


程度。


尽管目前 没有正式的驱动程序用到,但是它可以被用户程序通过



ioctl


来置位或者清


位,并且你 的驱动也可以用它。



misc- progs/netifdebug


程序可以用来置该标志和清该标志。





IFF_LOOPBACK





只能在轮讯接口


(loopback interface)


中置该标志


.


内核检查


IFF_LOOPBACK



代替



lo


的名字作为特殊接口。





IFF_POINTOPOINT





点对点接口的初始化函数要设该标 志,


例如



plip



ifconfig


也可以来设置和清


除。




IFF_POINTOPOINT


设置以后,


dev->pa_dstaddr


就参指连接的另一端。

< p>




IFF_NOARP





常规的网络接口可以传递


ARP


包,如果接口不能的话就要设置该标志。





IFF_PROMISC





设置该位以进行不区别的操作。默 认情况下,以太网接口用硬件过滤来保证


他们只接收广播包和直接送到它的硬件地址上的 包。而



tcpdump


这样的包的 吸食者就在接


口上设置不区别模式,以获



取经过接口传输介质上的所有包。





IFF_MULTICAST





兼容多播传送的接口设置该位。


ether_setup


默认设置


IFF_MULTICAST


,< /p>


如果


你的驱动不支持多播,



就有必要在初始化的时候清除它。





IFF_ALLMULTI





告诉接口接收所有的多目包。除非


IFF_MULTICAST


已经设置,内核才在该

< p>
主机执行多播路由的时候设置它。


IFF_ALLMULTI


对接口是只读的。





IFF_MASTER




IFF_SLA


VE





由负载均横化代码使用,接口驱动不必知道。





IFF_NOTRAILERS




IFF_RUNNING





这两个 标志在


Linux


中没有,但是对


BS D


兼容系统是存在的。





当一个程序改变


IFF_UP, open




close


方法就被调用。当



IFF_UP


或者其它任何


表示被修改,



s


et_multicast_list

被调用。


如果是因为标志的修改,


需要驱动做一些动作的话 ,


那么必须在


set_multicast_list


来做。例如,



IFF_PROMIS


被设置或清除,板上的硬件过滤


器就要收到通知。






The device methods



设备方法




每个网络设备都声明作用于自己的函数。


可以在网络设备上执行的操作在下面列出。



些操作可以空着,有些一般用不上,因为



ether_setup


会为他们指定合适的方法。




网络接口的设备方法可以分为基本和可选两部分。


基本方法包括使用接 口所必需的,



选方法实现并非严格要求的高级功能。下面是基 本方法:




int (*open)(struct device *dev);



打开接口。接口在任何



ifconfig


激活它的时候被打开。


open


方法应该登记它需要的任


何系统资源(



I


/O


端口,


IRQ



DMA


等等)


,开启硬件,增加模块使用计数。






int (*stop)(struct device *dev);


停止接口。当接口宕掉的时候就停止它。执行的操作跟打开的时候相反。





int (*hard_start_xmit) (struct sk_buff *skb,struct devcie *dev);



硬件开始传输。


该方法要求传输一个包。


包包含在一个套接字缓冲区



(sk_buff)


结构中。




int (*rebuild_header) (void *buf, struct device *dev,unsigned long raddr,struct sk_buff *skb);



在包传输前重建硬件包头。以太网设备默认的函数使用


ARP


来填写落下的信息。


ebuild_header


的参数是:指向硬件包头的指针,设备,


router


address


(包的初始目标)


,和


传输的缓冲区。







int (*hard_header) (struct sk_buff *skb, struct device *dev, unsigned short type,


void *daddr, void *saddr, unsigned len);



硬件包头。


该函数从此前获得的源和目的硬件地址来建立硬件包头,

< br>它的工作就是把作


为参数传递给它的信息组织起来。


et h_header


是默认的以太网类接口所用的函数,


因此它 来


指定这个域。




struct enet_statistics* (*get_stats)(struct device *dev);



任何时候一个应用程序需要接口的静态信息的时候,


这个方法被调用。< /p>


例如,


ifconfig



netstat -i


跑的时候。后面有



snull


实现的示例。





int (*set_config)(struct device *dev, struct ifmap *map);



改变接口配置。这个方法是配置驱动程序的入口点。设备的



I/O


地址和中断号可以在


运行时由



s


et_config


修改。当接口不能被侦测的时候,系统管理员可以使用该功能。




剩下的设备操作被视为可选。在传输过程中传递给他们的参数 从内核的


1.2



2.0


版本


之间修改了几次。




如果开发驱动的话只需要实现对


1.2


版本以后的操作。




int (*do_ioctl) (struct device *dev, struct ifreq *ifr, int cmd);



执行接口特定的



ioctl


命令。这些命令的实现方法在后面的




ioctl


Commands


讲到。这里所示的原型是通用的,如果接口不需要任何特定的命令的话,在


device


结构中


该域可以空着。




void (*set_multicast_list)(struct device *dev);



当设备的多播表以及当多播标志改变的时候调用该方法。




int (*set_mac_address)(struct device *dev, void *addr);



如果端口支持修改硬件地址的能力的话就可 以实现这个函数。


多数接口或者不支持此功


能,或者用默认的< /p>



eth_mac_addr


的实现。





Utility fields



效用域





device


结构中剩下的数据域是接口用来保存有用的状态信息的。有些被



ifconfig




netstat


使用,来为用户当前的配置信息。因此,接口 应该给这些域指定值。







unsigned long trans_start;




unsigned long last_rx;




这两个域都是用来保存瞬时的值,


它 们目前还没用上,


但是内核在将来可能用到这


些时间线索。




传送开始和当一个包被接收到的时候, 驱动有责任修改这些值。驱动还可


以用



trans_start


域来发现一个锁定。当等待



传送完成



中断的时候, 驱动可以由



trans_start


检查超时。





void *priv;




等于



filp->private_data.


驱动占有这个指针 并且在愿意的时候适用它。一般的,私


有数据结构包含一个



enet_statistics


条目。该域在早先的



初始化每个设备



中就 使用了。





unsigned char if_port;




用来纪录哪一个硬件端口正在被接口使用(例如,

< p>
BNC,AUI,TP




if_port


是给驱


动用的,可以根据




要给它赋上算术值。






unsigned char dma;




设备使用的


DMA

< br>通道。被



SIOCGIFMAP ioctl


命令使用。





struct dev_mc_list *mc_list;




int mc_count;




这两个域用来处理多播传输。



mc_count




mc_list


例的条目的计数值





还有其他的域,但是不被驱动使用。






OPENING AND CLOSING


打开和关闭




我们的驱动能够在模块再如或者核心引导的时候探测出接口。


下一步 是给接口指定一个


地址,以便驱动能够通过他来交换数据。打开和关闭接口是由


ifconfig


命令完成的。





ifconfig













,< /p>









< p>







ioctl(SIOCSIFADDR) (Socket I/O Control Set InterFace ADDRess).


然后再在



dev->flag


通过



ioctl(SIOCSIFLAGS) (Socket I/O Control Set InterFace FLAGS)


设置


IFF_UP


位,来打开接口。



就驱动程序所涉及 而言,


ioctl(SIOCSIFADDR)




dev->pa_addr, dev->family, dev->pa_mask,


and


dev->independent,


但是没有驱动的函数被调用,该任务是独立于设备的,



由内核来执


行它。后一个命令



i


octl(SIOCSIFLAGS)


为设备调用



open


方法。




与此类似,当接口关闭时,


ifconfig




ioctl(SIOCSIFLAGS)


来清



IFF_UP


,并且调用



stop


方法。





这两个方法在成功时返回



0


,出错时通常返回负值。




至少就实际的代码来说,


驱动必须执行与字符设备和块设备相同的任务。



open


请求它

< br>需要的任何系统资源并且让网络接口建立起来;


stop


关闭掉网络接口并且释放系统资源。




最后还有一件事情要做,如果驱动不使用共享中断的话(例如为了与旧的内核兼容)



内核输出一个



irq2dev_map


数组,


该数组由中断请求号


(IRQ number)


来定地址,


并且保存合


法的指针;驱动可能需要该数组来映射中断号到一个指向



struct


device


指针。这是在不使


用中断句柄的



dev_id


参数的情况下,支持单驱动多接口的唯一途径。




另外,网卡的硬件地址需要从板上拷贝到



dev->dev_addr


中,才能使接口与外界进行


任何通讯联系。硬件地址根据驱动的意愿在探测或者打开的时候指定。



snull


软件接口在



open


的时候指定它,它仅仅指定两个

ASCII


字符串作为硬件号。第一个字节是空。





open



close


的代码实现可以参看


fo ps->open



fops->close

< br>,代码如下:






int snull_open(struct device *dev)



{




int i;







/* request_region(), request_irq(), .... (like fops->open */






#if 0




/*





* We have no irq line, otherwise this assignment can be used to





* grab a non-shared interrupt. To share interrupt lines use





* the dev_id argument of request_irq. Seel snull_interrupt below.





*/




irq2dev_map[dev->irq] = dev;



#endif






/*





* Assign the hardware address of the board; use





* x is 0 or 1. The first byte is '0': a safe choice with regard





* to multicast.





*/




//


给接口指定硬件地址


,ETH_A LEN=6 octets




for (i=0; i < ETH_ALEN; i++)





dev->dev_addr[i] =





//


都先赋值为






dev- dev_addr[ETH_ALEN-1] += (dev - snull_devs); /* the number */




//


再修改最后一个字节


(octet),


区分< /p>









dev->start = 1;




dev->tbusy = 0;




MOD_INC_USE_COUNT;//


模块引用计数


+1




return 0;




}



正如所看到的,


某些域在



device


就够中做了修改。


start


指明接口已经准备好,


tbusy



言传送者不忙(也就是说,内核可以发出一个包)


< p>



stop


方法刚好是把



open


的错做反过来,出于这个原因,实现



stop


的函数通常叫做



close.




int snull_release(struct device *dev)



{




/* release ports, irq and such--like fops->close */




-


-


-


-


-


-


-


-



本文更新与2021-02-18 05:05,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/664303.html

以snull为例分析linux网络驱动程序的技术文档的相关文章

  • 爱心与尊严的高中作文题库

    1.关于爱心和尊严的作文八百字 我们不必怀疑富翁的捐助,毕竟普施爱心,善莫大焉,它是一 种美;我们也不必指责苛求受捐者的冷漠的拒绝,因为人总是有尊 严的,这也是一种美。

    小学作文
  • 爱心与尊严高中作文题库

    1.关于爱心和尊严的作文八百字 我们不必怀疑富翁的捐助,毕竟普施爱心,善莫大焉,它是一 种美;我们也不必指责苛求受捐者的冷漠的拒绝,因为人总是有尊 严的,这也是一种美。

    小学作文
  • 爱心与尊重的作文题库

    1.作文关爱与尊重议论文 如果说没有爱就没有教育的话,那么离开了尊重同样也谈不上教育。 因为每一位孩子都渴望得到他人的尊重,尤其是教师的尊重。可是在现实生活中,不时会有

    小学作文
  • 爱心责任100字作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文
  • 爱心责任心的作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文
  • 爱心责任作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文