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

http://blog.csdn.net/autoca/article/details/1765923

发信人: 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);
    }
--


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值