添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
网络驱动程序有一个非常复杂结构,里面包含了各种各样的网络协议。就目前最为常用的 internet 网络协议来说就包含了应用层、传输层、网络层、链路层以及物理层等 5 个层次,每个层次必须遵守各自的协议。就 AM79C97X 通信芯片来说,与之相关的主要是链路层,它负责为网络层提供服务。因此本章研究的重点在于链路层的驱动。 Internet 网络协议的一个明显的特征,就是它的各个层次是严格分明的,链路层通过通用的函数接口为网络层提供服务。图 5.1 则说明了链路层驱动函数库的层次关系。 其中函数库 muxLib 和函数库 endLib 与硬件无关,主要实现了初始化过程以及一些链路层协议,而函数库 ln97xEnd 主要用于 AM79C97X 通信芯片的控制。网络层和链路层之间虽然在操作上层次比较分明,但是其内部的数据结构比较复杂,因此本章会在最后简单的介绍一个 ioProto 函数库,说明网络层和链路层在数据结构上的关联。 5.2.1 函数库 muxLib 函数库 muxLib 为网络层提供了一组链路层的通用接口,用于处理数据链路层和网络层的通信从而将网络层和链路层隔离开来。 muxLib 函数库在网络驱动中的位置如图 5.1 所示。 该函数库主要提供了两个功能:一是对于系统中所有网卡的管理,主要的核心在于 endList 链的管理;另一个在于对每个 END ,管理其网络服务链即 END_OBJ.protocols ,这也就是为什么该函数库多采用数据结构 MUX_BIND_ENTRY 作为各个函数处理中的主要参数,该结构包含一个 END 设备和一个网络服务: typedef struct muxBindEntry     int unitNo;                 /* 设备的 unit */ char devName[END_NAME_MAX]; /* 设备的名称 */ long netSvcType; /* 网络服务类型 */ long netDrvType; /* 网络驱动类型 */ UINT32 flags;               /* BIB 入口标识 */ END_OBJ* pEnd;              /* END 对象指针 */ void* pNetCallbackId;       /* 网络服务回调 Id */ FUNCPTR addrResFunc;        /* 地址解析函数指针 */ FUNCPTR netStackRcvRtn;    /* network service stackRcvRtn 指针 */ }MUX_BIND_ENTRY, *MUX_ID; vxWorks 系统中通常会遇到一个缩写 END END 的全称是 Enhanced Network Drivers ,即增强型网络驱动,它的作用主要是为了处理链路层的一些协议,并不涉及具体的网络接口设备的控制。 在研究函数库 endLib 之前还需要理解另外一个术语: NPT NPT 的全称是 Network Protocol ToolKit ,网络协议工具组件,它在保留 END 全部功能的基础上进一步的扩展。它们的不同之处它们处理链路层帧结构的时候有所不同:对于 END 类型的网络设备,在网络层调用 muxLib 库函数发送数据之前,所有的链路层数据包都已经封装好了,而对于 NPT 类型的网络设备,网络层在调用 MUXtk 层时还没有形成完整的数据包,需要在 MUXtk 根据目标 IP 地址组织完整的数据包。参见图 5.2 的结构。 如果一个网络设备被定义成一个 END 类型那么就不能被定义为一个 NPT 类型,反之亦然。无论 END 类型还是 NPT 类型,它们的都采用 END_OBJ 的数据结构。由于么全 END 类型比较完整,条理也比较清晰,因此在后面的分析中我们将更多的精力放在 END 类型的设备中,对 mux 层的分析也仅限于函数库 muxLib 而不再分析 muxTkLib 由于在网络驱动中轮询模式实际上很少使用,本章将不再对轮询模式的相关函数进行分析。需要注意的是这个函数库中的一些轮询函数还存在这不少的 bug ,完全没有办法正常执行。一个最直观的例子就是函数 muxPollStart 中分配并初始化了 ppMuxPollDevices pMuxPollMblkRing 数组之后就马上启动 muxPollTask 进行,这时候 ppMuxPollDevices pMuxPollMblkRing 数组中的元素都是 NULL muxPollTask 函数可以对数值为 NULL pMuxPollMblkRing 数组元素进行重新分配,可是对 ppMuxPollDevices 就没有判断直接使用,这样就造成了内存的泄露。这是一个十分严重的错误。如果需要使用到轮询模式,那么这一点尤其需要注意。 函数 muxDevLoad 用于装载网卡驱动。该过程主要分几步:第一步,调用特定网卡的驱动函数 (*endLoad) ,如果 endDevTbl 数组中正好试探到了 am97x 的驱动,那么 endLoad 函数指定的函数为 config/pcpentium/sysLn97xEndLoad ,参数为空白字符串(字符串地址存在,但长度为 0 ),深入研究一下 sysLn97xEndLoad sysLn97xEndLoad 函数发现为空白字符串,则调用 ln97xEnd.c/ln97xEndLoad (pParamStr) ln97xEndLoad 发现参数为空字符串,自动将字符串 "lnPci" 复制到参数中,并返回到 muxDevLoad ;第二步,调用函数 endFindByName endList 链表中查找是否存在 "lnPci" unit 号与 pDevTbl->unit 相同这个设备,如果有的话说明已经安装过了,否则需要在 endList 添加这个设备结构 END_TBL_ROW ,其名字为 "lnPci" ;第三步,以字符串参数 paramStr 调用函数 sysLn97xEndLoad 试探性的 load 。一旦成功,就将其挂载到 endlist 中。 关于该函数的执行过程,还会在后面关于网卡驱动的动态安装过程分析中做进一步详细分析。 注意 endList 的结构如图 5.4 所示。注意在计算机系统中可能会有多块网卡,这些网卡的类型也并不一定相同, endList 记录了系统中网卡的的情况,同一类型的网卡形成一个 END_OBJ 结构链表,而不同类型的网卡分属于不同的链,这些链又通过 END_TBL_ROW 结构链在一起。 当装载一个网卡驱动时,首先要检查该类型及标号为 unit 的网卡有没有装过,如果装过则不需要安装,直接退出;否则还要明确是该类型的网卡有没有安装,如果没有安装则需要重新分配一个 END_TBL_ROW 结构变量并链接到 endList 链中,如果该类型的网卡驱动已经装过,只是没有安装标号为 unit 的网卡驱动,那么只需要分配一个 END_OBJ 结构挂在相应的 END_TBL_ROW 结构变量为链头的链上即可。 l MUX_PROTO_SNARF 类型的网络服务结构总是保存在 protocols 链表的头部位置, pEnd->snarfCount 记录了 MUX_PROTO_SNARF 类型的网络服务的数量; l END_OBJ 结构的 protocols 链中除 MUX_PROTO_SNARF MUX_PROTO_PROMISC 之外,其他类型的网络服务每种最多只能有一个; l 所有类型的网络服务都要指定一个 stackRcvRtn 函数; l 除 MUX_PROTO_OUTPUT 以外,其它类型的网络服务都要指定 stackShutdownRtn stackTxRestartRtn stackErrorRtn 函数; l 所有绑定网络服务结构的 nptFlag 元素为 FALSE ,表明是 MUX 类型; l 除 MUX_PROTO_OUTPUT 网络服务以外都要指定服务的名称,如果参数 pProName 指定了名称,就采用指定的名称,否则名称为 Protocol %d ,这里 %d 为协议的编号; l 如果新绑定的网络服务是 MUX_PROTO_OUTPUT 以及 MUX_PROTO_PROMISC 类型的网络服务结构,则直接存放在 protocols 链的尾部;否则要存放在 protocols 链中的 MUX_PROTO_SNARF 类型的网络服务结构之后的第一个位置; 参数说明: char * pName :网卡的名字如 ln ei 等等; int    unit :某一类网卡的序号,和 pName 一起确定一个 END 设备; BOOL   (*stackRcvRtn) (void*, long, M_BLK_ID, LL_HDR_INFO *, void*) :回调函数, MUX 可以在收到数据包时调用该函数; STATUS (*stackShutdownRtn) (void*, void*) :回调函数, MUX 可以通过这个函数关闭网络服务; STATUS (*stackTxRestartRtn) (void*, void*) :回调函数, MUX 可以通过这个函数通知网络服务可以发送报文; void   (*stackErrorRtn) (END_OBJ*, END_ERR*, void*) :回调函数, MUX 可以通过这个函数向网络服务报告错误; long   type RFC1700 规定的网络服务类型。如 0x800 表示 IP 协议; char * pProtoName :协议名称; void * pSpare :指向网络服务的数据区域, MUX 调用回调函数时可用于传递参数。 l 对于标准类型的(非 MUX_PROTO_SNARF MUX_PROTO_PROMISC MUX_PROTO_OUTPUT 类型)网络服务只能处理与之类型相同的数据包; l 函数 muxReceive 将收到的数据包通过回调函数 stackRcvRtn 交给所有的网络服务进行处理,如果某一网络服务需要对数据包进行处理,那么回调函数将返回 TRUE ,否则返回 FALSE l END 收到一个数据包,将根据 protocols 链的顺序交给各个数据包进行处理,如果由其中的任何一个网络服务处理后将直接返回,因此后面的网络服务不再处理,处理的优先级从高到底依次为: MUX_PROTO_SNARF 、标准型、 MUX_PROTO_PROMISC 类型。当然从 muxBind 函数中 END_OBJ.protocols 链的存放顺序也可以看出其存放顺序: MUX_PROTO_SNARF 存放在链表前端, MUX_PROTO_PROMISC MUX_PROTO_OUTPUT 存放在链表尾端。 l END_OBJ.pMib2Tbl 中存放的是收发报文的统计。 从一个数据包中获取其地址信息。通过调用函数 pEnd->pFuncTable->addrGet (pMblk, pSrcAddr, pDstAddr,pESrcAddr, pEDstAddr) 直接实现。 参数说明: void *   pCookie :一个 MUX_BIND_ENTRY 结构中间包含一个 ENDshebei 和一个网络服务; M_BLK_ID pMblk M_BLK 结构指针,其中存放了数据; M_BLK_ID pSrcAddr M_BLK 结构指针,其中存放了源地址; M_BLK_ID pDstAddr M_BLK 结构指针,其中存放了目的地址; M_BLK_ID pESrcAddr M_BLK 结构指针,其中存放了源地址; M_BLK_ID pEDstAddr M_BLK 结构指针,其中存放了目的地址。 muxAddrResFuncAdd muxAddrResFuncGet muxAddrResFuncDel 这三个函数的主要作用是对 LIST addrResList[MUX_MAX_IFTYPE + 1] 的管理。 addrResList 是一个数组,它中间存放了 MUX_MAX_IFTYPE 个链表( MUX_IFTYPE 是从 1 开始的, addrResList[0] 未使用),元素 addrResList[ifType] 是一个链头,该链中保存的是 IFT_xxx 对应的地址解析协议函数,该解析函数能够将一个网络服务的地址翻译成一个链路层协议地址。一个常见的例子就是 arpresolve 函数,它能够将一个 IP 地址翻译成一个链路层地址。 muxAddrResFuncAdd 函数就是在链 addrResList[ifType] 中增加一个 MUX_ADDR_REC 结构 typedef struct mux_addr_rec     NODE node;     long protocol;     FUNCPTR addrResFunc;     } MUX_ADDR_REC

mail_xuan 2013-12-11 18:51:46

您好,博主,我看了您的文章觉得写得非常好。我现在正在学习,想请教一下您,NPT驱动平时用的多么,我只了解了muxLib中关于END的源码,是否需要了解NPT方面的呢?