您的位置:首页 > 理论基础 > 计算机网络

VxWorks之MUX网络驱动在EasyARM2200和SmartARM2200上的实现zz

2007-08-30 20:39 801 查看
发信人: gdtyy (gdtyy), 信区: Embedded
标 题: VxWorks之MUX网络驱动在EasyARM2200和SmartARM2200上的实现
发信站: 水木社区 (Mon Jun 25 23:25:48 2007), 站内

***********************************************************
* VxWorks之MUX网络驱动在EasyARM2200和SmartARM2200上的实现 *
***********************************************************
------ 浅谈《ecos增值包》辅助开发VxWorks网络驱动
2007/05/11 asdjf@163.com www.armecos.com

又有一些网友购买《ecos增值包》了,下面进一步谈谈VxWorks下网络驱动程序的开发
,目标板为EasyARM2200和SmartARM2200(网卡芯片为RTL8019AS),调试环境是redboot,依
靠打印输出调试。

--------------------
| 网络驱动程序概述 |
--------------------
在VxWorks中,网卡驱动程序分为END(Enhanced Network Driver)和BSD两种。END驱动
程序基于MUX模式,是目前在VxWorks操作系统上应用最广泛的一种网络驱动程序。在该模式
下,网络驱动程序被划分为协议组件和硬件组件,它们之间没有内部交换数据,只通过MUX
间接相互作用。MUX接口的作用是分解协议和硬件网络驱动程序,从而使它们几乎独立,这
种独立使添加新的驱动程序和协议变得简单。例如:添加一个新的网络驱动,则所有现有基
于MUX的协议均可使用新的驱动程序;同样,添加一个新的基于MUX的协议,则任何现有的网
络驱动均可通过MUX接口来访问新的协议。如下图所示:

协议A 协议B ......
| | |
-----------------------
|
MUX
|
-----------------------
| | |
设备1 设备2 ......

通过MUX接口,协议和硬件设备相互独立,任何协议都可以通过MUX统一接口访问任何硬
件设备,任何硬件设备也可以通过MUX接口支持所有协议。

VxWorks MUX驱动调用关系如下图所示:

------------------------ ---------------------
---------------------
| |------>| muxBind() | |
|
| stackTxShutdownRtn() |<------| muxUnbind() | |
|
| | | | |
|
| | | muxDevLoad() |------>| endLoad()
|
| | | muxDevUnload() |------>| endUnload()
|
| | | | |
|
| stackRcvRtn() |<------| muxReceive() |<------|
|
| stackError() |<------| muxError() |<------|
|
| | | | |
|
| |------>| muxSend() |------>| endSend()
|
| stackTxRestartRtn() |<------| muxTxRestartRtn() |<------|
endTxRestartRtn() |
| |------>| muxMCastAddrGet() |------>|
endMCastAddrGet() |
| |------>| muxMCastAddrDel() |------>|
endMCastAddrDel() |
| |------>| muxMCastAddrAdd() |------>|
endMCastAddrAdd() |
| |------>| muxPollSend() |------>| endPollSend()
|
| |------>| muxPollReceive() |------>|
endPollReceive() |
| |------>| muxIoctl() |------>| endIoctl()
|
| | | | |
|
| | | |------>| endStart()
|
| | | |------>| endStop()
|
------------------------ ---------------------
---------------------
协议层接口 MUX接口 END接口

VxWorks启动时执行任务tUsrRoot来完成驱动程序的安装,它产生tNetTask任务来处理
网络任务工作队列中的条目,调用muxDevLoad()来加载网络驱动程序,调用muxDevStart()
来启动驱动程序。协议栈注册了4个回调函数,由MUX接口回调。

基本过程就是:
1、muxDevLoad()调用endLoad()
endLoad()初始化任何特殊设备,填写END_OBJ和NET_FUNCS结构并返回。MUX把
返回的END_OBJ添加到END_OBJ结构的链表中,此链表列出了当前系统所运行的所有网络设备
的信息,此时,用户驱动程序装载完毕以备用。

2、MUX调用endStart()启动网卡,注册中断服务程序并使能中断。

综上,VxWorks的网络驱动只要在endLoad()中填写END_OBJ结构和实现endXXX()函数即
可。当然,内存管理也是一个很重要的方面,VxWorks的内存池API实现了网络数据“零拷贝
”,各协议层仅传递指针而不重复拷贝数据。endXXX()函数主要实现开始、结束、发、收、
IOCTL、多播地址插入/删除/读取等。其中收发既可以基于中断也可以基于查询方式。

-------------------------------------------------
| EasyARM2200和SmartARM2200上网络驱动程序的实现 |
-------------------------------------------------
要在EasyARM2200和SmartARM2200上实现END类型的网卡驱动,需要在BSP目录中增加几
个文件:
BSP目录为c:/Tornado2.2/target/config/zlgarm
增加rtl8019end.c、rtl8019end.h、configNet.h文件,并修改config.h、Makefile配
置。

-----------
configNet.h
-----------
#define ZLGARM7_LOAD_FUNC_0 rtl8019EndLoad

#define ZLGARM7_LOAD_STRING_0 "83400000:17"
IMPORT END_OBJ * ZLGARM7_LOAD_FUNC_0 (char *, void *);

END_TBL_ENTRY endDevTbl [] =
{
#ifdef INCLUDE_RTL8019_END
{ 0, ZLGARM7_LOAD_FUNC_0, ZLGARM7_LOAD_STRING_0, 1 , NULL, FALSE},
#endif /* INCLUDE_RTL8019_END */
{ 0, END_TBL_END, NULL, 0, NULL, FALSE},
};

在configNet.h配置文件中,主要涉及到一个表格(END_TBL_ENTRY)的填写。这个表格的
每一项对应一个网卡的加载信息。
END_TBL_ENTRY结构中各项的含义分别为:
{ 设备编号,加载函数,初始化资源字串,缓冲类型,BSP内部指针,处理完成与
否的标志 }
END_TBL_ENTRY使用END_TBL_END结束表格填写。
由此可见,上面的语句注册了一个网卡驱动rtl8019EndLoad。

资源字串的含义是:网卡基地址:网卡中断号。
这个字串是自行定义的,各项间用冒号分隔,只要能正确分解出各项内容即可,没有统
一规定。例如这里定义资源字串的两项分别为网卡基地址和中断号。

---------
config.h
---------
#define DEFAULT_BOOT_LINE "rtl(0,0)host:vxWorks " /
"h=192.168.0.6 " /
"e=192.168.0.2 " /
"g=192.168.0.1 " /
"u=target " /
"tn=targetname"

#define INCLUDE_NETWORK
#define INCLUDE_END

#ifdef INCLUDE_END
#define INCLUDE_RTL8019_END /* Include Ethernet driver */
#endif /* INCLUDE_END */

#undef WDB_COMM_TYPE /* default WDB agent communication path is
END */
#define WDB_COMM_TYPE WDB_COMM_END

这里设置目标机IP地址(e),使能END驱动,增加8019驱动程序,使用网络连通WDB
agent。

--------
Makefile
--------
在MACH_EXTRA=后面添加rtl8019end.o,以便编译8019驱动。

MACH_EXTRA = rtl8019end.o

------------
rtl8019end.h
------------
这个文件里定义各种与8019有关的宏定义。

typedef struct end_device
{
END_OBJ end; /* The class we inherit from. */
ULONG base; /* base address */
int ivec; /* rtl8019 interrupt vector */
int offset; /* offset */
long flags; /* Our local flags. */
UCHAR enetAddr[6]; /* ethernet address */
char packetBuf[2048]; /* packet buffer */
......
}END_DEVICE;

# define DP_IN(_b_, _o_, _d_) HAL_READ_UINT8 ((_b_)->base + 2*(_o_), (_d_))
# define DP_OUT(_b_, _o_, _d_) HAL_WRITE_UINT8((_b_)->base + 2*(_o_), (_d_))

这里面的END_DEVICE是自定义结构,其中必须包含一个END_OBJ结构,其他内容根据实
际情况自行定义,例如:这里面自行定义了8019网卡驱动程序用到的变量:包缓冲区、标志
、MAC地址、基址、中断号等。各种驱动函数都会传递这个结构体的指针。
DP_IN和DP_OUT定义了8019输入输出函数,其中偏移要乘2,因为A0接到了A1上。

------------
rtl8019end.c
------------
LOCAL NET_FUNCS rtl8019EndFuncTable =
{
rtl8019EndStart,
rtl8019EndStop,
rtl8019EndUnload,
rtl8019EndIoctl,
rtl8019EndSend,
rtl8019EndMCastAdd,
rtl8019EndMCastDel,
rtl8019EndMCastGet,
rtl8019EndPollSend,
rtl8019EndPollRcv,
endEtherAddressForm,
endEtherPacketDataGet,
endEtherPacketAddrGet
};

rtl8019EndFuncTable结构体中填写了8019网卡的所有操作函数,rtl8019end.c中主要
实现这些函数和rtl8019EndLoad、rtl8019EndUnload、rtl8019EndParse、
rtl8019EndMemInit、rtl8019EndInt、rtl8019EndConfig、rtl8019EndReset等。

首先说下内存管理。VxWorks采用一种复杂灵活的内存管理机制来有效减少数据复制。
这种机制能满足数据包头尾添加/删除,可变长内存分配,尽量少的数据复制(“零拷贝”)
等要求。

要实现这种机制,首先在rtl8019EndMemInit中设置内存池的参数,然后申请内存空间
;再使用netPoolInit来形成具体的内存池。
在网卡驱动程序的其他地方,如果需要进行内存分配,则都在这个内存池中申请。申请
步骤为:
1、调用系统函数netClusterGet()预定一块簇缓冲区;
2、调用系统函数netClBlkGet()预定一个clBlk结构;
3、调用系统函数netMblkGet()预定一个mBlk结构;
4、调用系统函数netClBlkJoin()把簇添加到clBlk结构中;
5、调用系统函数netMblkClJoin()把clBlk结构添加到mBlk结构中。
最后将mBlk作为参数传递给处理函数。这种管理方法有很大好处,因为一个clBlk可与
多个mBlk关联,这就使得在不同协议间传递数据变得容易,只须传递指针,而无须拷贝其中
的数据。

当网络设备产生中断时,VxWorks调用驱动程序先前注册的中断服务程序。中断服务程
序应尽可能地短,减少中断阻塞时间。它不应包含耗时的处理工作。为了将一些数据包处理
工作放置在任务级,中断服务程序必须调用netJobAdd()将相应的处理程序作为输入来生成
一个任务级的数据处理任务。例如:netJobAdd ((FUNCPTR)rtl8019EndRcvInt, (int)
pDrvCtrl, 0, 0, 0, 0);。

关于8019的详细工作原理,可以通过购买本站(www.armecos.com)的“51+8019资料”获
得。

---------------------------------------------
| 《ecos增值包》辅助开发VxWorks网络驱动程序 |
---------------------------------------------
为了调试方便,我们使用“驻留ROM”型编译模式,这样就需要把VxWorks烧写到flash
里才能调试,不过,有了redboot,我们可以把RAM虚拟成ROM,让VxWorks在RAM里运行,这
样就可以减少flash擦写次数,加快调试速度。此时,需要修改conig.h中的ROM_XXX_XXX宏
定义,把地址定位到RAM上。
使用:lo -b 0x81010000 -r -h 192.168.0.1 a.bin下载VxWorks程序,
使用 go 0x81010000运行VxWorks程序。
通过打印输出调试信息,有些不适合打印的地方可以采用内存打印技术(在VxWorks里往
内存打印数据,然后通过redboot查看内存),如中断程序里的打印。

----------------------------------------------------------------------------
8019复位
----------------------------------------------------------------------------

LOCAL void rtl8019EndReset
(
END_DEVICE* pDrvCtrl /* device to be reset */
)
{
unsigned int i;
unsigned char tmp;

if(pDrvCtrl->unit != 0)
return;

for(i = 0; i < 250; i++); /* 延时一段时间 */
DP_IN(pDrvCtrl, 0x1F, tmp);
DP_OUT(pDrvCtrl, 0x1F, tmp);
}

----------------------------------------------------------------------------
内存池初始化
----------------------------------------------------------------------------

LOCAL STATUS rtl8019EndMemInit
(
END_DEVICE * pDrvCtrl /* device to be initialized */
)
{
DBG_PRINTF ("InitMem/n");

/*
* Set up an END netPool using netBufLib(1).
*/

endMclConfig.mBlkNum = END_MBLK_NUM;
endClDescTbl[0].clNum = END_CL_NUM;
endMclConfig.clBlkNum = endClDescTbl[0].clNum;

/* Calculate the total memory for all the M-Blks and CL-Blks. */
endMclConfig.memSize = (endMclConfig.mBlkNum * (MSIZE + sizeof (long))) +
(endMclConfig.clBlkNum * (CL_BLK_SZ + sizeof(long)));

if ((endMclConfig.memArea = (char *) memalign (sizeof(long),
endMclConfig.memSize))
== NULL)
return (ERROR);

/* Calculate the memory size of all the clusters. */
endClDescTbl[0].memSize = (endClDescTbl[0].clNum *
(endClDescTbl[0].clSize + 8))
+ sizeof(int); /* +8 is for proper alignment */

/* Allocate the memory for the clusters */
endClDescTbl[0].memArea =
(char *) cacheDmaMalloc (endClDescTbl[0].memSize);

if (endClDescTbl[0].memArea == NULL)
{
DBG_PRINTF ("system memory unavailable/n");
return (ERROR);
}

if ((pDrvCtrl->end.pNetPool = (NET_POOL_ID) malloc (sizeof(NET_POOL)))
== NULL)
return (ERROR);

/* Initialize the memory pool. */
if (netPoolInit(pDrvCtrl->end.pNetPool, &endMclConfig,
&endClDescTbl[0], endClDescTblNumEnt, NULL) == ERROR)
{
return (ERROR);
}

DBG_PRINTF ("InitMem OK!/n");
return OK;
}

----------------------------------------------------------------------------
接收函数
----------------------------------------------------------------------------
LOCAL STATUS rtl8019EndRecv
(
END_DEVICE *pDrvCtrl /* device structure */
)
{
M_BLK_ID pMblk = NULL;
CL_BLK_ID pClBlk = NULL;
UINT32 len;
char * pBuf = NULL;

if (pDrvCtrl->end.pNetPool == NULL)
{
DBG_PRINTF ("rtl8019EndRecv: Illegal pNetPool on entry!/n");
END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);
goto cleanRXD;
}

if ((pMblk = mBlkGet (pDrvCtrl->end.pNetPool, M_DONTWAIT, MT_DATA))
== NULL)
{
DBG_PRINTF ("rtl8019EndRecv: Out of M Blocks!/n");
END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);
goto cleanRXD;
}

pBuf = netClusterGet (pDrvCtrl->end.pNetPool,
pDrvCtrl->end.pNetPool->clTbl[0]);

if (pBuf == NULL)
{
DBG_PRINTF ("rtl8019EndRecv: Out of clusters!/n");
pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
goto cleanRXD;
}

/* 读取数据 */
len = rtl8019PacketGet(pDrvCtrl, pBuf);

if ((pClBlk = netClBlkGet (pDrvCtrl->end.pNetPool, M_DONTWAIT)) == NULL)
{
DBG_PRINTF ("rtl8019EndRecv: Out of Cluster Blocks!/n");
pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
goto cleanRXD;
}

if (netClBlkJoin (pClBlk, pBuf, len, NULL, 0, 0, 0) == NULL)
{
DBG_PRINTF ("rtl8019EndRecv: netClBlkJoin failed/n");
pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
goto cleanRXD;
}

if (netMblkClJoin (pMblk, pClBlk) == NULL)
{
DBG_PRINTF ("rtl8019EndRecv: netMblkClJoin failed/n");
pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
goto cleanRXD;
}

pMblk->mBlkHdr.mFlags |= M_PKTHDR;
pMblk->mBlkHdr.mLen = len;
pMblk->mBlkPktHdr.len = len;
pMblk->mBlkHdr.mData += pDrvCtrl->offset;

END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_UCAST, +1);

END_RCV_RTN_CALL(&pDrvCtrl->end, pMblk);

return (OK);

cleanRXD:

if (pClBlk != NULL)
{
netClBlkFree (pDrvCtrl->end.pNetPool, pClBlk);
}

if (pBuf != NULL)
{
netClFree (pDrvCtrl->end.pNetPool, pBuf);
pBuf = NULL;
}

if (pMblk != NULL)
{
netMblkFree (pDrvCtrl->end.pNetPool, pMblk);
}

END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);

return (ERROR);
}

----------------------------------------------------------------------------
发送函数
----------------------------------------------------------------------------
LOCAL STATUS rtl8019EndSend
(
END_DEVICE *pDrvCtrl, /* device ptr */
M_BLK_ID pNBuff /* data to send */
)
{
int len;
int i;

if (pDrvCtrl->resetting)
{
return ERROR;
}

END_TX_SEM_TAKE (&pDrvCtrl->end, WAIT_FOREVER);

len = netMblkToBufCopy(pNBuff,
(void *)pDrvCtrl->packetBuf, NULL) ;
len = max(len, ETHERSMALL);

END_TX_SEM_GIVE (&pDrvCtrl->end);

page(pDrvCtrl, 0);
DP_OUT(pDrvCtrl, 0x09, 0x40); /* send buffer start address */
DP_OUT(pDrvCtrl, 0x08, 0x00);
DP_OUT(pDrvCtrl, 0x0B, len >> 8); /* send packet len */
DP_OUT(pDrvCtrl, 0x0A, len & 0xFF);
DP_OUT(pDrvCtrl, 0x00, 0x12); /* write dma, page0 */

for(i = 0; i < len; i++){ /* 发送数据到双口RAM */
DP_OUT(pDrvCtrl, 0x10, pDrvCtrl->packetBuf[i]);
printf("%x ", pDrvCtrl->packetBuf[i]);
}
printf("/n");

DP_OUT(pDrvCtrl, 0x04, 0x40); /* 发送缓冲区首址*/
DP_OUT(pDrvCtrl, 0x06, len >> 8); /* 发送包长度 */
DP_OUT(pDrvCtrl, 0x05, len & 0xFF);
DP_OUT(pDrvCtrl, 0x00, 0x3E); /* 发送 */

END_ERR_ADD (&pDrvCtrl->end, MIB2_OUT_UCAST, +1);

netMblkClChainFree (pNBuff);
return (OK);
}
--

※ 来源:·水木社区 http://newsmth.net·[FROM: 61.149.56.*]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: