📄 (ldd) ch14-网络驱动程序(上)(转载).htm
字号:
color=#ffffff size=3>
<P>但主要目的是提供一个参考而不是要被记住。本章的其余部分在一个域被示例代码用到<BR>的时候会简单地描述一下,所以你不必不停地回头来参考本节。<BR> <BR>结构device在结构上可以分为两个部分:“可见的”和“不可见的”。可见部分由那些<BR>在静态device结构中显式赋值的域组成,象前面给出的在snull中出现的两项。其余的域<BR>内部使用。有些被驱动程序访问(例如在初始化时被赋值的那些),而有些不能动。本<BR>章在版本2.0.30前都是完全的。<BR> <BR>可见的头<BR> <BR>结构device的第一部分由下列域组成,按序为:<BR> <BR>char *name;<BR> <BR>设备名。如果第一个字符是0(NULL字符)或空格,register_netdev给它分配名字ethn<BR>,n取合适的值。<BR> <BR>unsigned long rmem_end;<BR> <BR>unsigned long rmem_start;<BR> <BR>unsigned long mem_end;<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>unsigned long mem_start;<BR> <BR>这些域存有设备使用的共享内存的开始和结束地址。如果设备有不同的发送和接收内存<BR>,那么mem域就用做发送内存,而rmem用做接收内存。mem_end和mem_start可以在系统引<BR>导时在核心命令行指定,它们的值由ifconfig获取。Rmem域在驱动程序以外不会被引用<BR>。一般地,end域被设置成使得end-start为板上可用内存量。<BR> <BR>unsigned long base_addr;<BR> <BR>I/O基地址。这个域,和前面的一样,在设备检测时被赋值。ifconfig可以用来显示和修<BR>改当前值。base_addr可以在系统引导或加载时在核心命令行显式赋值。<BR> <BR>unsigned char irq;<BR> <BR>被赋予的中断号。当接口被列出时dev->irq由ifconfig打印出来。这个值通常在引导或<BR>加载时被设置,以后可以用ifconfig修改。<BR> <BR>unsigned char start;<BR> <BR>unsigned char interrupt;<BR> <BR>这些域是二进制标志。start通常在设备打开时设置,在关闭时清楚。在接口准备号运行<BR></P></FONT><FONT
color=#ffffff size=3>
<P>这些域是二进制标志。start通常在设备打开时设置,在关闭时清楚。在接口准备号运行<BR>时它是非零。interrupt是用来告诉代码的高层一个中断到达接口,并正在处理中。<BR> <BR>unsigned long tbusy;<BR> <BR>这个域表明“传送忙”。当驱动程序不能再接收新的包发送时(既所有的输出缓冲区都<BR>满了),它应该为非零。使用long类型而不是char是因为有时要使用原子的位操作以避<BR>免竞争条件。注意在核心1.2,tbusy的确是个八位的域,向后可移植的驱动程序应该注<BR>意这一点。原子的位操作在第九章的“使用锁变量”一节中介绍过。<BR> <BR>struct device *next;<BR> <BR>用来维护链表;任何驱动程序都不能动这个域。<BR> <BR>int (*init)(struct device *dev);<BR> <BR>初始化函数。这个域通常是device结构中显式列出的最后一个域。<BR> <BR>隐藏的域<BR> <BR>device结构包含几个额外的域,通常在设备初始化时被赋值。这些域中的一些携带了接<BR>口的信息,一些存在只是为了方便驱动程序(也就是说,核心并不使用它们);还有一<BR>些域,最引人注意的是一些设备方法,它们是核心和驱动程序的接口。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>些域,最引人注意的是一些设备方法,它们是核心和驱动程序的接口。<BR> <BR>我想分别列为三组,与域的实际顺序无关,那并不重要。<BR> <BR>接口信息<BR> <BR>多数接口信息都由函数ether_setup来正确设置。以太网卡在大部分域都可以依赖这个通<BR>用目的的函数,但flags和dev_addr域是设备特定的,必须在初始化时显式地赋值。<BR> <BR>一些非以太网的接口可以使用类似于ether_setup的助手函数。driver/net/net_init.c<BR>引出tr_setup(令牌环)和fddi_setup。如果你的设备不属于这些类中的一种,你需要自<BR>己为所有的域赋值。<BR> <BR>unsigned short hard_header_len;<BR> <BR>“硬件包头长”。发送包头中IP头(或其它协议信息)之前那部分的八元组个数。对以<BR>太网接口来说,这个值是14。<BR> <BR>unsigned short mtu;<BR> <BR>“最大传送单元”。在包传输时,这个域由网络层使用。以太网的MTU为1500个八元组。<BR> <BR>__u32 tx_queue_len;<BR></P></FONT><FONT
color=#ffffff size=3>
<P>__u32 tx_queue_len;<BR> <BR>在设备传送队列中可以排队的最大祯数。ether_setup将这个值设为100,不过你可以改<BR>变它。例如,plip使用10以避免浪费系统内存(plip比实际的以太网接口吞吐率要低)<BR>。<BR> <BR>unsigned short type;<BR> <BR>接口的硬件类型。这个域被ARP使用以判断接口支持的硬件地址类型。以太网接口把它设<BR>为ARPHRD_ETHER----ether_setup为你做这件事。<BR> <BR>unsigned char addr_len;<BR> <BR>unsigned char broadcast[MAX_ADDR_LEN];<BR> <BR>unsigned char dev_addr[MAX_ADDR_LEN];<BR> <BR>以太网地址长为六个八元组(我们是指接口板的硬件标志),播送地址由六个0xff八元<BR>组组成;ether_setup负责这些值的正确设置。另一方面,设备地址必须以设备特定的方<BR>式从接口板中读出,驱动程序应把它复制到dev_addr。这个硬件地址用来在把包交给驱<BR>动程序传送前产生正确的以太网包头。snull并不使用物理接口,它生成一个它自己的物<BR>理地址。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>unsigned short family;<BR> <BR>接口的地址族,通常为AF_INET。接口并不常查看这个域或者向其赋值。<BR> <BR>unsigned short pa_alen;<BR> <BR>协议地址长。对AF_INET来说为四个八元组。接口不需要修改这个数。<BR> <BR>unsigned long pa_addr;<BR> <BR>unsigned long pa_brdaddr;<BR> <BR>unsigned long pa_mask;<BR> <BR>刻划接口的三个地址:接口地址,播送地址,及网络掩码。这些值是协议特定的(既它<BR>们是“协议地址”);如果dev->family是INET,则它们为IP地址。这些域由ifconfig赋<BR>值,对驱动程序是只读的。<BR> <BR>unsigned long pa_dstaddr;<BR> <BR>plip和ppp一类点到点协议使用这个域记录连接另一侧的IP号码。和前面的域一样,它也<BR>是只读的。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>是只读的。<BR> <BR>unsigned short flags;<BR> <BR>接口标志。这个域含有下列位值。前缀IFF意为接口标志(InterFace Flags)。有些标<BR>志由核心管理,有些则是在初始化时由接口设置,以确认接口的能力。有效的标志是:<BR> <BR>IFF_UP<BR> <BR>当接口是活跃的时,核心置上该标志。这个标志对驱动程序是只读的。<BR> <BR>IFF_BROADCAST<BR> <BR>这个标志表明接口的播送地址是有效的。以太网卡支持播送。<BR> <BR>IFF_DEBUG<BR> <BR>查错模式。这标志控制printk调用的唠叨,还用在其它一些查错目的。尽管目前没有官<BR>方驱动程序使用它,用户程序可以通过ioctl来对其置位或者清除,你的驱动程序可以使<BR>用它。misc-progs/netifdebug程序可以用来将这个标志打开或关闭。<BR> <BR>IFF_LOOPBACK<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>这个标志在环回接口中要被置位。核心检测这个标志而不是将名字lo作为特殊接口硬写<BR>入程序。<BR> <BR>IFF_POINTOPOINT<BR> <BR>点到点的初始化函数应置位这个标志。例如,plip对它置位。ifconfig工具也可以对其<BR>置位和清除。当它被置位时,dev->pa_dstaddr应该指向连接的另一端。<BR> <BR>IFF_NOARP<BR> <BR>常规网络接口可以传送ARP包。如果接口不能进行ARP,它必须置这个标志。例如,点到<BR>点接口并不需要运行ARP,它只能增加额外的通信,却不能获取任何有用的信息。snull<BR>不具有ARP能力,因此它要对其置位。<BR> <BR>IFF_PROMISC<BR> <BR>这个标志被置位以获得杂类操作。在缺省情况下,以太网接口使用硬件过滤器以保证它<BR>只收到播送包和指向其硬件地址的包。而象tcpdump一类包监视器则在接口上设置杂类模<BR>式,以获取经过接口传输介质的所有包。<BR> <BR>IFF_MULTICAST<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>能进行选播传送的接口要置这个标志。ether_setup在缺省情况下对其置位。所以如果你<BR>的驱动程序不支持选播,它必须在初始化时清除这个标志。<BR> <BR>IFF_ALLMULTI<BR> <BR>这个标志告诉接口接收所有的选播包。只有当IFF_MULTICAST被置位,而主机由进行选播<BR>路由时,核心对其置位。它对接口时只读的。IFF_MULTICAST和IFF_ALLMULTI早在1.2版<BR>就已经定义了,但那时并未使用。在后面“选播”一节我们将看到它是如何使用的。<BR> <BR>IFF_MASTER<BR> <BR>IFF_SLAVE<BR> <BR>这些标志被加载均衡代码使用。接口驱动程序不需要知道它们。<BR> <BR>IFF_NOTRAILERS<BR> <BR>IFF_RUNNING<BR> <BR>这些标志在Linux中不使用,只是为了和BSD兼容而存在。<BR> <BR>当一个程序改变IFF_UP,open和close方法会被调用。当IFF_UP或其它标志被修改时,se<BR></P></FONT><FONT
color=#ffffff size=3>
<P>当一个程序改变IFF_UP,open和close方法会被调用。当IFF_UP或其它标志被修改时,se<BR>t_multicast_list方法被调用。如果驱动程序因为标志的修改而要执行一些动作,那么<BR>必须在set_multicast_list中进行。例如,当IFF_PROMIS被置位或清除时,板上硬件过<BR>滤器必须被通知。这个设备方法的责任将在后面的“选播”一节简单介绍。<BR> <BR> <BR> <BR>设备方法<BR> <BR>与字符设备和块设备的情况一样,每个网络设备要声明在其上操作的函数。可以在网络<BR>接口上进行的操作列在下面。一些操作可以留为NULL,还有一些通常不去动它们,因为e<BR>ther_setup给它们分配合适的方法。<BR> <BR>一个网络接口的设备方法可以分为两类:基本的和可选的。基本的包括那些为访问接口<BR>所需要的;可选的方法实现一些并不严格要求的高级功能。下面是基本方法:<BR> <BR>int (*open)(struct device *dev);<BR> <BR>打开接口。只要ifconfig激活一个接口,它就被打开了。open方法要注册它需要的所有<BR>资源(I/O端口,IRQ,DMA,等),打开硬件,增加模块的使用计数。<BR> <BR>int (*stop)(struct device *dev);<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>终止接口。接口在关闭时就终止了;在打开时进行的操作应被保留。<BR> <BR>int (*hard_start_xmit)(struct sk_buff *skb, struct device *dev);<BR> <BR>硬件开始传送。这个方法请求一个包的传送。这个包含在一个套接字缓冲区结构(sk_bu<BR>ff)中。套接字缓冲区在下面介绍。<BR> <BR>int (*rebuild_header)(void *buf, struct device *dev, unsigned long raddr,<BR>struct sk_buffer *skb);<BR> <BR>这个函数用来在一个包传送之前重构硬件包头。这个以太网设备使用的缺省包头用ARP向<BR>包中填入缺少的信息。snull驱动程序实现了它自己的这个方法,因为ARP并不在sn接口<BR>上运行。(在本章的后面会介绍ARP。)这个方法的参数是一些指针,分别指向硬件包头<BR>,设备,“路由器地址”(包的初始目的地),以及被传送的缓冲区。<BR> <BR>int (*hard_header)(struct sk_buffer *skb, struct device *dev, unsigned<BR>short type,<BR> <BR> void *daddr, void *saddr, unsigned len);<BR> <BR>硬件包头。这个函数用以前获取的源和目的地址构造包头;它的任务是组织那些以参数<BR>的形式传给它的信息。eth_header是以太网类接口的缺省函数, ether_setup相应地对<BR></P></FONT><FONT
color=#ffffff size=3>
<P>的形式传给它的信息。eth_header是以太网类接口的缺省函数, ether_setup相应地对<BR>这个域赋值。给出的参数顺序适用于核心2.0或更高版本,但与1.2有所不同。这个改变<BR>对以太网驱动程序是透明的,因为它继承了 eth_header的实现;其它驱动程序可能要处<BR>理一下这个不同,如果它们想保持向后兼容的话。<BR> <BR>struct enet_statistics * (*get_stats)(struct device *dev);<BR> <BR>当应用希望获得接口的统计信息时需要调用这个方法,例如,当运行ifconfig或netstat<BR> –i时。在snull中的一个示例实现将在后面“统计信息”中介绍。<BR> <BR>int (*set_config)(struct device *dev, struct ifmap *map);<BR> <BR>改变接口的配置。这个方法是配置驱动程序的入口点。设备的I/O地址和中断号可以在运<BR>行时用set_config改变。在接口不能探测到时,系统管理员可以适用这个能力。这个方<BR>法在后面的“运行时配置”中介绍。<BR> <BR> <BR> <BR>其余的设备方法是被我称为可选的那些。传递给其中一些的参数在Linux1.2到Linux2.0<BR>的转变中改了好几次。如果你想写一个可以在两个版本核心都工作的驱动程序,你可以<BR>只为从2.0开始的版本实现这些操作。<BR> <BR>int (*do_ioctl)(struct devices *dev, struct ifreg *ifr, int cmd);<BR></P></FONT><FONT
color=#ffffff size=3>
<P>int (*do_ioctl)(struct devices *dev, struct ifreg *ifr, int cmd);<BR> <BR>执行接口特定的ioctl命令。这些命令的实现在后面的“自定义ioctl命令”中描述。这<BR>里给出的原形在1.2以上的核心都能工作。如果接口不需要任何接口特定的命令,那么结<BR>构device中相应的域可以留为NULL。<BR> <BR>void (*set_multicast_list)(struct device *dev);<BR> <BR>当设备的选播列表改变和标志改变时,将调用这个方法。这里的参数传递与1.2版本不同<BR>。更多的细节和一个示例实现见“选播”一节。<BR> <BR>int (*set_mac_address)(struct device *dev, void *addr);<BR> <BR>如果接口支持改变硬件地址的能力,可实现这个函数。多数接口要么不支持这个能力,<BR>要么使用缺省的eth_mac_addr实现。这个原形与1.2版也不同。<BR> <BR>#define HAVE_HEADER_CACHE<BR> <BR>void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev,<BR>unsigned short htype, __u32 daddr);<BR> <BR>void (*header_cache_update)(struct hh_cache *hh, struct device *dev,<BR>unsigned char *haddr);<BR></P></FONT><FONT
color=#ffffff size=3>
<P>unsigned char *haddr);<BR> <BR>这些函数和宏在Linux1.2中没有。以太网驱动程序不必关心header_cache的问题,因为e<BR>th_setup会安排使用缺省的方法。<BR> <BR>#define HAVE_CACHE_MTU<BR> <BR>int (*change_mtu)(struct device *dev, int new_mtu);<BR> <BR>如果接口的MTU(最大传送单元)发生了改变,这个函数负责采取动作。这个函数和宏在<BR>Linux1.2中都没有。当MTU改变时,如果驱动程序要做一些特殊的事情,它应该声明它自<BR>己的函数,不然将由缺省函数来完成。如果你感兴趣,snull有一个这个函数的模版。<BR> <BR> <BR> <BR>工具域<BR> <BR>其余的结构device中的域被接口用来保存一些有用的状态信息。其中一些被ifconfig和n<BR>etstat用来向用户提供当前配置的信息。因此,接口应该对这些域赋值。<BR> <BR>unsigned long trans_start;<BR> <BR>unsigned long last_rx;<BR></P></FONT><FONT
color=#ffffff size=3>
<P>unsigned long last_rx;<BR> <BR>这两个域用来保存一些瞬间值。它们目前不用,但核心有可能将来使用这些计时提示。<BR>驱动程序负责在传送开始时和收到包时更新这些值。trans_start域还可以被驱动程序用<BR>来检测锁定。驱动程序可以在等待一个“传送完成”的中断时用trans_start来检查超时<BR>。<BR> <BR>void *priv<BR> <BR>等价于filp->private_data。驱动程序拥有这个指针,可以随意使用。通常这个私有数<BR>据结构含有一个enet_statistics结构项。这个域在以前的“初始化每个设备”中用过。<BR> <BR> <BR>unsigned char if_prot;<BR> <BR>这个域用来记录哪个硬件端口被接口使用(例如,BNC,AUI,TP)。任何数值都可以按<BR>需要赋给它。<BR> <BR>unsigned char dma;<BR> <BR>被接口使用的DMA通道。这个域被ioctl的SIOCGIFMAP命令使用。<BR> <BR>struct dev_mc_list *mc_list;<BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>unsigned char dma;<BR> <BR>被接口使用的DMA通道。这个域被ioctl的SIOCGIFMAP命令使用。<BR> <BR>struct dev_mc_list *mc_list;<BR> <BR>int mc_count<BR> <BR>这两个域被用来处理选播传送。Mc_count是mc_list中项的个数。更多的细节见“选播”<BR>结构device中还有一些别的域,但驱动程序没有使用它们<BR>--<BR><FONT
color=#00ff00>※ 来源:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.196.234]</FONT><BR>--<BR><FONT
color=#00ffff>※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#0000ff>※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ffff00>※ 转载:.南京大学小百合站 bbs.nju.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ff0000>※ 转载:·饮水思源 bbs.sjtu.edu.cn·[FROM: 211.80.41.106]</FONT><BR></P></FONT>
<P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT
color=#ffffff size=2>目录页</FONT></A> | <A
href="http://joyfire.net/lsdp/16.htm"><FONT color=#ffffff
size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/18.htm"><FONT
color=#ffffff size=2>下一页</FONT></A></P></SPAN></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>
<TBODY>
<TR>
<TD colSpan=3 height=2>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -