⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch14-网络驱动程序(上)(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 3 页
字号:
       
      l         如果dev->base_addr是一个有效的设备I/O地址,将不再探测其它I/O位置,
      而是使用这个值。如果这个值在加载时被赋值,这种情况就会发生。
       
      l         如果dev->base_addr是0,那么探测设备是可以接受的。拥护可以通过在加载
      时置这个I/O地址为0来请求探测。
       
      l         其它情况下,不进行探测。核心使用0xffe0来阻止探测,但其实任何无效值
      都行。这需要依赖于驱动程序来无声地拒绝base_addr中的一个无效地址。一个模块应该
      缺省地置这个地址为无效值来防止不期望的探测。注意查看PCI设备总是安全的,因为它
      并不牵扯任何探测(见第15章,外围总线概述)
       
      正如你可能已经注意到的,用一个加载时的设置来控制探测与我们在skull中使用的技术
      是一样的。
       
      当从dev->init中退出时,dev结构应该用正确的值填充。初始化例程的主要工作就是填
      充这个结构。幸运的是,核心通过函数ether_setup填充结构device负责了一些以太网的
      缺省设置。
       
      snull_init的核心是:
       

       
      (代码308)
       
                              /* keep the default flags, just add NOARP */
       
                             dev->flags                      |=IFF_NOARP;
       
      这段代码唯一不寻常的特征是在标志中设置IFF_NOARP。这指明接口不能使用ARP,即“
      地址解析协议”。ARP是一个低级的以太网协议;每个真实的以太网接口都懂得ARP,因
      此不需要设置这个标志。有趣的是注意到一个接口在没有ARP时仍能工作。例如,plip接
      口就是没有ARP支持的以太网类接口,与snull相似。这个主题将在后面的“地址解析”
      中详细讨论,device结构将在下一节肢解。
       
      现在我想介绍结构device的另一个域priv。它的作用类似与我们在字符设备驱动程序中
      用过的private_data指针。与fops->private_data不同的是,priv指针是在初始化时分
      配,而不是在打开时,因为priv所指向的数据项包含有接口的统计信息。有一点很重要
      ,就是统计信息要保证总是可用的,即使在接口宕掉时,因为用户可能在任何时候通过
      调用ifconfig来显式统计信息。在初始化时而不是打开时分配priv浪费的内存是无关紧
      要的,因为多数被探测到的接口保持一直在系统中运行。snull模块为priv声明了一个数
      据结构snull_priv。这个结构包含了结构enet_statistics,它是存放接口信息的标准地
      方。
       
      下面这几条snull_init中的语句分配dev->priv:

      下面这几条snull_init中的语句分配dev->priv:
       
      (代码309 #1)
       
      模块卸载
       
      当模块被卸载时没有什么特殊的事情发生。函数cleanup_module在释放了与私有结构相
      关的内存后,只需将接口从列表中取消即可。
       
      (代码309 #2)
       
      模块化的和非模块化的驱动程序
       
      尽管在对字符设备和块设备来说,模块化和非模块化的驱动程序并没有什么引人注意的
      区别,但对网络驱动程序来说,情况并非如此。
       
      如果一个驱动程序做为主流Linux核心的一部分发行的话,它并不声明自己的device结构
      ,而是使用在drivers/net/Space.c中声明的结构。Space.c声明了所有网络设备的链表
      ,即包括plip1一类驱动程序特定的结构,也包括通用目的的eth设备。以太网根本不关
      心它们的device结构,因为它们使用通用目的的结构。这种通用的eth设备结构声明ethi
      f_probe为它们的init函数。程序员要想在主流核心中插入一个新的以太网接口只需要在
      ethif_probe中加入一个对驱动程序初始化函数的调用。另一方面,非eth驱动程序的作
      者需要在Space.c中插入它们的device结构。在两种情况下,如果驱动程序必须被链到核

      者需要在Space.c中插入它们的device结构。在两种情况下,如果驱动程序必须被链到核
      心,只需要修改源文件Space.c。
       
      在系统引导时,网络初始化代码循环遍历所有的device结构,调用它们的探测函数(dev
      ->init),向它们传递一个指向设备本身的指针。如果探测函数成功了,Space.c初始化
      device结构。这种设置驱动程序的方式允许渐增地将设备赋予名字eth0,eth1,依次类
      推,而不需要改变每个设备的name域。
       
      另一方面,当加载一个模块化的驱动程序时,它声明它自己的device结构(入我们在本
      章中已经看到的那样),即使它控制的接口是以太网接口。
       
      好奇的读者可以查看Space.c和net_init.c来得到更多关于接口初始化的信息。这里对驱
      动程序设置的介绍只是为了强调init设备方法的重要性。如果一个驱动程序模块包含了
      预填好的设备结构,那么它将不适合主流核心的初始化技术,并且如果结构device中引
      入新的域,会使它变的不能向前兼容。
       
       
       
      设备结构的细节
       
      device结构居于网络驱动程序的真正核心,值得完全的描述。第一次阅读本书的读者可
      以跳过本节,因为开始时不需要对这个结构有详细的理解。下面的列表描述所有的域,
      但主要目的是提供一个参考而不是要被记住。本章的其余部分在一个域被示例代码用到

      但主要目的是提供一个参考而不是要被记住。本章的其余部分在一个域被示例代码用到
      的时候会简单地描述一下,所以你不必不停地回头来参考本节。
       
      结构device在结构上可以分为两个部分:“可见的”和“不可见的”。可见部分由那些
      在静态device结构中显式赋值的域组成,象前面给出的在snull中出现的两项。其余的域
      内部使用。有些被驱动程序访问(例如在初始化时被赋值的那些),而有些不能动。本
      章在版本2.0.30前都是完全的。
       
      可见的头
       
      结构device的第一部分由下列域组成,按序为:
       
      char *name;
       
      设备名。如果第一个字符是0(NULL字符)或空格,register_netdev给它分配名字ethn
      ,n取合适的值。
       
      unsigned long rmem_end;
       
      unsigned long rmem_start;
       
      unsigned long mem_end;
       

       
      unsigned long mem_start;
       
      这些域存有设备使用的共享内存的开始和结束地址。如果设备有不同的发送和接收内存
      ,那么mem域就用做发送内存,而rmem用做接收内存。mem_end和mem_start可以在系统引
      导时在核心命令行指定,它们的值由ifconfig获取。Rmem域在驱动程序以外不会被引用
      。一般地,end域被设置成使得end-start为板上可用内存量。
       
      unsigned long base_addr;
       
      I/O基地址。这个域,和前面的一样,在设备检测时被赋值。ifconfig可以用来显示和修
      改当前值。base_addr可以在系统引导或加载时在核心命令行显式赋值。
       
      unsigned char irq;
       
      被赋予的中断号。当接口被列出时dev->irq由ifconfig打印出来。这个值通常在引导或
      加载时被设置,以后可以用ifconfig修改。
       
      unsigned char start;
       
      unsigned char interrupt;
       
      这些域是二进制标志。start通常在设备打开时设置,在关闭时清楚。在接口准备号运行

      这些域是二进制标志。start通常在设备打开时设置,在关闭时清楚。在接口准备号运行
      时它是非零。interrupt是用来告诉代码的高层一个中断到达接口,并正在处理中。
       
      unsigned long tbusy;
       
      这个域表明“传送忙”。当驱动程序不能再接收新的包发送时(既所有的输出缓冲区都
      满了),它应该为非零。使用long类型而不是char是因为有时要使用原子的位操作以避
      免竞争条件。注意在核心1.2,tbusy的确是个八位的域,向后可移植的驱动程序应该注
      意这一点。原子的位操作在第九章的“使用锁变量”一节中介绍过。
       
      struct device *next;
       
      用来维护链表;任何驱动程序都不能动这个域。
       
      int (*init)(struct device *dev);
       
      初始化函数。这个域通常是device结构中显式列出的最后一个域。
       
      隐藏的域
       
      device结构包含几个额外的域,通常在设备初始化时被赋值。这些域中的一些携带了接
      口的信息,一些存在只是为了方便驱动程序(也就是说,核心并不使用它们);还有一
      些域,最引人注意的是一些设备方法,它们是核心和驱动程序的接口。

      些域,最引人注意的是一些设备方法,它们是核心和驱动程序的接口。
       
      我想分别列为三组,与域的实际顺序无关,那并不重要。
       
      接口信息
       
      多数接口信息都由函数ether_setup来正确设置。以太网卡在大部分域都可以依赖这个通
      用目的的函数,但flags和dev_addr域是设备特定的,必须在初始化时显式地赋值。
       
      一些非以太网的接口可以使用类似于ether_setup的助手函数。driver/net/net_init.c
      引出tr_setup(令牌环)和fddi_setup。如果你的设备不属于这些类中的一种,你需要自
      己为所有的域赋值。
       
      unsigned short hard_header_len;
       
      “硬件包头长”。发送包头中IP头(或其它协议信息)之前那部分的八元组个数。对以
      太网接口来说,这个值是14。
       
      unsigned short mtu;
       
      “最大传送单元”。在包传输时,这个域由网络层使用。以太网的MTU为1500个八元组。
       
      __u32 tx_queue_len;

      __u32 tx_queue_len;
       
      在设备传送队列中可以排队的最大祯数。ether_setup将这个值设为100,不过你可以改
      变它。例如,plip使用10以避免浪费系统内存(plip比实际的以太网接口吞吐率要低)
       
      unsigned short type;
       
      接口的硬件类型。这个域被ARP使用以判断接口支持的硬件地址类型。以太网接口把它设
      为ARPHRD_ETHER----ether_setup为你做这件事。
       
      unsigned char addr_len;
       
      unsigned char broadcast[MAX_ADDR_LEN];
       
      unsigned char dev_addr[MAX_ADDR_LEN];
       
      以太网地址长为六个八元组(我们是指接口板的硬件标志),播送地址由六个0xff八元
      组组成;ether_setup负责这些值的正确设置。另一方面,设备地址必须以设备特定的方
      式从接口板中读出,驱动程序应把它复制到dev_addr。这个硬件地址用来在把包交给驱
      动程序传送前产生正确的以太网包头。snull并不使用物理接口,它生成一个它自己的物
      理地址。
       

       
      unsigned short family;
       
      接口的地址族,通常为AF_INET。接口并不常查看这个域或者向其赋值。
       
      unsigned short pa_alen;
       
      协议地址长。对AF_INET来说为四个八元组。接口不需要修改这个数。
       
      unsigned long pa_addr;
       
      unsigned long pa_brdaddr;
       
      unsigned long pa_mask;
       
      刻划接口的三个地址:接口地址,播送地址,及网络掩码。这些值是协议特定的(既它
      们是“协议地址”);如果dev->family是INET,则它们为IP地址。这些域由ifconfig赋
      值,对驱动程序是只读的。
       
      unsigned long pa_dstaddr;
       

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -