1.介绍

增强型网络驱动(END)是SOI 模型中数据链路层的实现,它通过MUX 函数和网络服务通信。

2.驱动操作

2.1 添加驱动到VxWorks

添加驱动到VxWorks系统就像添加其他的应用一样。第一步是编译并把驱动代码包括在VxWorks镜像中。

由于VxWorks允许创建一个以上的网络设备,所以必须需要对配置设备的相关的#define 进行分组。定义在target/config/bspname/configNet.h 里面。以下是网络设备描述的一个例子。在系统中应该添加类似的描述到configNet.h 文件中。

#define MOT_FCC_LOAD_FUNC sysMotFccEndLoad
#define MOT_FCC_LOAD_STRING ""
IMPORT END_OBJ* MOT_FCC_LOAD_FUNC (char *, void*);

在每一个网络设备的配置中,应该定义以上两个常量,以下对着两个常量进行说明:

LOAD_FUNC:

规定驱动endLoad()函数的入口点。例如,如果驱动的endLoad()入口点是sysMotFccEndLoad(),那么编辑configNet.h 包括以下的定义:

#define MOT_FCC_LOAD_FUNC sysMotFccEndLoad

LOAD_STRING:

在网络初始化过程中,作为initString参数,传递给muxDevLoad()的初始化参数。这个字符串也一并传给endLoad()函数,它的内同取决于驱动的需要。

必须编辑endDevTbl()的定义(在configNet.h中规定包括在镜像中的ENDS)从而包含被加载的每一个设备的入口。

例如:

END_TBL_ENTRY endDevTbl [] =
{
{ 0, LOAD_FUNC_0, LOAD_STRING_0, BSP_0, NULL, FALSE },
{ 1, LOAD_FUNC_1, LOAD_STRING_1, BSP_1, NULL, FALSE },
{ 0, END_TBL_END, NULL, 0, NULL, FALSE },
};

上面的第一个参数规定设备号。在最后的FALSE表示入口还没有被处理。在系统成功的加载驱动后,这个值变为TRUE。如果想要禁止系统自动加载驱动,那么可以把这个值设为TRUE。

这样,就准备好重新编译VxWorks从而包括新的END驱动。当新编译的VxWorks启动时,系统给在table中每一个设备按照列出来的顺序调用muxDevLoad()。

2.2 网络初始化步骤

在系统启动后,VxWorks产生tUsrRoot任务初始化网络。usrRoot()调用usrNetInit(); 在usrNetInit()中调用sockLibAdd(),sockLibAdd()再调用bsdSockLibInit来添加BSD socket 库接口。在usrNetInit()中还调用usrNetProtoInit()来初始化各种协议。usrNetInit()还调用muxDevLoad()和ipAttach()。muxDevLoad()然后调用驱动中的endLoad()函数。

在muxDevLoad()加载驱动之后,调用muxDeStart()函数,它调用驱动中的endStart()函数。endStart()函数应该激活驱动并且用相应的中断连接程序把中断服务程序连接到相应的结构和BSP中。

下图简单的说明了初始化的过程

VxWorks END Network Initialization

2.3 接收帧

当有中断发生的时候,VxWorks 调用endStart()函数注册的中断服务程序。中断服务程序应该完成把帧从本地硬件传到可存取的存储器中所需的最少工作。为了最小化中断封锁时间,仅仅那些需要最少执行时间的程序在中断级处理,像错误检查或是设备状态改变。这个程序应该以任务级在所有耗时工作中排队。为了以任务级处理在排队中等待接收的帧,用netJobAdd()函数。这个函数接受一个函数指针以及最多5个参数(给函数指针指向的函数提供参数)。netJobAdd()函数原型如下:

STATUS netJobAdd
(
FUNCPTR routine,
int param1,
int param2,
int param3,
int param4,
int param5
)

在这里的routine 应该是在驱动中以任务级执行帧处理的函数。netJobAdd()函数把这个函数放在tNetTask 工作队列中,并且提供合适的信号量唤醒tNetTask。

在唤醒之后,tNetTask 把函数调用和相应的参数从它的工作队列中取出。然后在它的上下文中执行这些函数直到队列为空。

3.编写 END 驱动

3.1 驱动用的END 数据结构
END_OBJ
END所用的核心的数据结构是END_OBJ。驱动应该指定这个结构并在endLoad()函数中初始化它的一些元素。此结构定义在target/h/end.h中。这个结构中的一些元素是由MUX管理的。但是驱动应该设定和管理其他的元素。
3.2 MUX所用的END函数

下表是MUX所用的和驱动之间通信的函数。在本文中,函数用一个一般的“end”前缀,但是在实际的驱动中,此前缀通常被能识别驱动的表示所代替,例如“motFcc”代表MPC8260的快速以太网驱动。

函数 描述
endLoad() 把一个设备加载到MUX中,并把驱动和这个设备结合在一起
endUnload() 从MUX中释放一个设备或者设备上的一个端口
endSend() 从MUX层接收数据并把它发送到物理层。
endMCastAddrDel() 从一个设备中注册的广播列表中移出一个广播地址。
endMCastAddrGet() 检索注册在一个设备中的广播列表
endMCastAddrAdd() 向注册在一个设备中的列表中增加一个广播地址
endPollSend() 在查询模式而不是在中断模式下发送帧
endPollReceive() 在查询模式而不是在中断模式下接收帧
endStart() 连接设备中断并激活接口
endStop() 停止或者释放一个网络结构或设备
endAddressForm() 向一个包中添加地址信息
endAddrGet() 从一个包中提取地址信息
endPacketDataGet() 分离包中的地址信息和数据信息
endIoctl() 支持多种ioctl命令

endLoad()

在网络接口被用于发送和接收帧之前,这个适当的设备必须被加载到MUX中并被配置。当函数endLoad()在系统启动时被任务tRootTask用于加载设备时,是由muxDevLoad()调用的。

此函数接收一个初始化字符串,它的内容一般来说是用户定义的,但是总的来说需要包括能够识别物理接口的设备号,中断向量号,以及存储器映射寄存器的地址。endLoad()必须被写成two-pass算法。在加载过程中,MUX调用此函数两次。在第一次初始化字符串是空的(全0)。endLoad()期望能检测到空字符并返回拷贝到字符串的设备名。

第二次调用传给endLoad()函数的是提供给muxDevLoad()的实际的初始化字符串。endLoad()函数必须返回它所分配的指向END_OBJ结构的指针或是当加载失败时返回NULL。

一般来说,endLoad( )函数在第二次调用的时候,将主要完成以下功能:

  • 初始化设备和接口
  • 分配并给END_OBJ赋值
  • 初始化一些主要的结构
  • 分析并处理初始化字符串
  • 创建并填充MIB II接口表
  • 用netBufLib分配一个或是多个网络缓冲池
  • 根据END_OBJ机构中的pNetFuncs来填充NET_FUNCS表

endLoad()函数基于以下的框架

END_OBJ * endLoad
(
char * initString, /* defined in endTbl */
void * pBsp /* BSP-specific information (optional) */
)
{
END_OBJ * newEndObj;
if( !initString ) /* initString is NULL, error condition */
{
/* set errno perhaps */
return( (END_OBJ *) 0 );
}
else if( initString[0] == 0 ) /* initString[0] is NULL, pass one */
{
strcpy( initString, "foo" );
return( (END_OBJ *) 0 );
}
else /* initString is not NULL, pass two */
{
/* initialize device */
newEndObj = (END_OBJ *) malloc( sizeof(END_OBJ) );
/* fill newEndObj and newEndObj->pFuncTable */
/* create and populate the MIB2 interface table */
/* initialize any needed private structures */
/* parse and process initString, and pBsp if necessary */
/* create a private pool of memory using netBufLib API */
/* create network buffer pools using netBufLib API */
return( newEndObj );
}
}

endUnload()

当muxDevUnload( )被调用的时候,endUnload( )函数执行。在这个程序中,它负责完成释放设备所需的所有工作。这个函数用于那些已经被endLoad( )激活的端口。如果一个设备由多个端口,那么直到所有加载的端口都接受到卸载请求之前,驱动并不释放全局资源。endUnload( )函数当卸载设备的时候并不需要通知服务。在调用endUnload( )函数之前,NPT给连接到此设备的每一个服务发送一个关闭通知。

endUnload( )函数的原形如下:

STATUS endUnload
(
END_OBJ * pEND /* END object */
)

endSend()

网络驱动发送函数是由在加载过程中创建的NET_FUNCS表所定位的。当网络服务执行发送请求的时候,MUX调用此函数。此发送程序接收表示被发送帧的mBlk链。

endSend( )函数原形如下:

STATUS endSend
(
END_OBJ * pEND, /* END object */
M_BLK_ID pPkt /* mBlk chain containing the frame */
)

如果发送成果那个,此函数返回OK。如果由于象资源不足等的错误发送没完成,它返回END_ERR_BLOCK。

endMCastAddrAdd()

此函数给设备注册一个物理层的广播地址。他接收指向endLoad()函数返回的END_OBJ结构的指针以及包含被添加的物理地址的字符串。此函数应该按照硬件的规定方式从新配置接口,从而让驱动接收来自规定地址的帧。

endMCastAddrAdd( )原形如下:

STATUS endMCastAddrAdd
(
END_OBJ * pEND, /* END object */
char * pAddress /* physical address, or a reference thereto */
)

此函数返回OK或者ERROR。

endMCastAddrDel()

此函数从一个设备的广播列表中移出一个以前注册的广播地址。它的参数是指向endLoad()函数返回的END_OBJ结构指针,以及需要被移出的包含物理地址的字符串。

endMCastAddrDel()原形如下:
STATUS endMCastAddrDel
(
END_OBJ * pEND, /* END object */
char * pAddress /* physical address, or a reference thereto */
)

此函数返回OK或者ERROR。

endMCastAddrGet()

此函数在设备中有效的包含所有广播地址的列表中检索。它的参数是指向endLoad()函数返回的END_OBJ结构指针,以及指向接收列表的MULTI_TABLE结构。

endMCastAddrGet()原形如下:

STATUS endMCastAddrGet
(
END_OBJ * pEND, /* driver's control structure */
MULTI_TABLE * pMultiTable /* container for address list */
)

此函数返回OK或者ERROR。

endPollSend()

此程序提供查询模式的相当于驱动中断驱动的发送程序。它或者把帧直接传送到下级设备中,或者立即退出在设备忙或者资源不可用时。

注意:当系统调用endPollSend( )程序时,系统可能处于不能响应核调用的模式下。因此,程序不必执行任何的核操作,例如取得信号量,或者分配存储器。同样,此程序不必阻塞或者延迟,由于系统可能暂停。

在endPollSend()中,要检验设备已经被设定在查询模式下。endPollSend( )应该直接把输出帧放到网络上,而不把帧排在输出队列中。它的参数是指向endLoad()函数返回的END_OBJ结构指针,以及包含输出帧的mBlk或者mBlk链的识别号。

endPollSend( ) 原形如下:

STATUS endPollSend
(
END_OBJ * pEND, /* END object*/
M_BLK_ID pMblk /* mBlk chain: data to be sent */
)

endPollReceive()

此函数用查询模式而不是中断模式接收帧。此函数必须直接从网络中检索帧并把它拷贝到提供给函数的mBlk中。如果没有帧可接收,返回ERROR。

注意:当系统调用endPollReceive()程序时,系统可能处于不能响应核调用的模式下。因此,程序不必执行任何的核操作,例如取得信号量,或者分配存储器。同样,此程序不必阻塞或者延迟,由于系统可能暂停。

在endPollReceive中,要检验设备已经被设定在查询模式下。然后此函数直接从网络中检索帧并把它拷贝到提供给函数的mBlk中。它的参数是指向endLoad()函数返回的END_OBJ结构指针,以及包含输入帧应该被接收到的mBlk或者mBlk链的识别号。

endPollReceive原形如下:

STATUS endPollReceive
(
END_OBJ * pEND, /* returned from endLoad() */
M_BLK_ID pPkt /* mBlk chain: data being received */
)

此函数返回OK,或者当接收数据太大而不能放在mBlk中以及没有数据时,返回EAGAIN的错误值。

endStart()

endStart()函数连接设备中断,使接口被激活可用。此函数唯一的参数是由endLoad()调用返回的唯一的接口标识号。随着endLoad() 被调用,此函数被每一个需要在驱动中被激活的接口执行。

endStart()函数原形如下:

STATUS endStart
(
END_OBJ * pEND, /* END object */
)

此函数应该返回OK或者ERROR。

endStop()

endStop()函数暂停一个网络设备,一般是通过断开相应的中断。它不是通过释放分配的数据结构来移出设备。此函数唯一的参数是由endLoad()调用返回的唯一的接口标识号。

endStop()函数原形如下:

STATUS endStop
(
END_OBJ * pEND, /* END object */
)

此函数应该返回OK或者ERROR。

endAddressForm()

endAddressForm()程序产生一个和帧相关的头,用来预先挂起包含输出数据的mBlk链。在把地址段添加到mBlk之后,程序相应的应该调整mBlk.mBlkHdr.mLen和mBlk.mBlkHdr.mData 数据成员。

如果接收的mBlk不能容下增加的地址信息,那么另一个mBlk/cBlk簇必须被创建用来接收地址信息,并把它插在mBlk链的开始。

网络协议的类型可以在pDst.mBlkHdr.reserved区中找到。endAddressForm()将返回mBlk链的识别号。

endAddressForm()函数原形如下:

M_BLK_ID endAddressForm
(
M_BLK_ID pData /* mBlk chain containing outgoing data */
M_BLK_ID pSrc, /* source address, in an mBlk */
M_BLK_ID pDst, /* destination address, in an mBlk */
)

endAddrGet()

此函数在mBlk链提供的输入帧中检索地址值。如果额外的mBlk参数不是NULL,它会设定mBlk.mBlkHdr.mData 和 mBlk.mBlkHdr.mLen来表示相应的数据链路层地址的位置和大小。额外的mBlk的参数是:

  • pSrc: 帧的本地源地址。
  • pDst: 帧的本地目标地址。
  • pESrc: 初始的链路级源地址或者如果没有是pSrc设定。
  • pEDst: 最后的链路级源地址或者如果没有是pDst设定。

注意:endLib库包含从以太网帧的头中检索地址值的一个程序。

endAddrGet()函数原形如下:

STATUS endAddrGet
(
M_BLK_ID pData, /* mBlk chain containing frame */
M_BLK_ID pSrc, /* local source address, in an mBlk */
M_BLK_ID pDest, /* local destination address, in an mBlk */
M_BLK_ID pESrc, /* actual source address, in an mBlk */
M_BLK_ID pEDest /* actual destination address, in an mBlk */
)

此函数应该返回OK或者ERROR。

endPacketDataGet( )

此函数分析在mBlk链中提供的输入帧。它把地址信息,包括地址信息的大小和偏移量,帧负载的偏移量,帧的类型,放在作为参数传给它的LL_HDR_INFO结构中。

注意:endLib库包含从以太网帧中检索地址、数据偏移量、大小和类型信息的程序。

endPacketDataGet()函数原形如下:

STATUS endPacketDataGet
(
M_BLK_ID pData, /* mBlk chain containing packet */
LL_HDR_INFO * pHeader /* structure to hold header info */
)

此函数应该返回OK或者ERROR。

endIoctl()

END可能需要支持ioctl命令,尤其是如果它用在已经存在的IP网络服务子层中。

警告:如果从BSD4.3模型中移植驱动,你可能尝试用已经存在的xxIoctl()程序作为endIoctl()程序,略过支持广播地址列表的单独函数的创建。千万别这样做!你的驱动必须要实现广播地址列表支持程序。

endIoctl()函数有三个参数:由muxDevLoad()返回的唯一的接口识别号、需要执行的ioctl命令、以及在命令中为存储额外的数据的缓冲或者是为存储命令执行之后返回的数据。

下表是Ioctl命令和数据类型。

VxWorks END Ioctl

注意:在0-128之间的命令常数(例如EIOCGFBUF)被Wind River所保留。如果想用自定义的Ioctl命令,那么应该定以它们的命令常数在以上范围之外。

endIoctl( )原形如下:

int endIoctl
(
END_OBJ * pEND, /* END Object */
int command, /* ioctl command */
caddr_t buffer /* holds response from command */
)

如果成功,函数返回0,否则返回相应的错误值或者如果命令不支持返回EINVAL。