4. 实现网络接口驱动

任务目标

为PowerPC目标板上的板载以太网控制器(以Intel 82559/82551为例,PCI接口)编写VxWorks网络驱动,使目标板能够通过TFTP从主机下载VxWorks镜像并启动。本案例聚焦于驱动核心的注册、收发路径与中断处理,确保tftp命令可用。

环境与依赖

  • VxWorks 5.5/6.x(BSP开发环境),Tornado或Workbench
  • PowerPC目标板(PCI总线,板载Intel PRO/100系列网卡)
  • 主机TFTP服务器(IP:192.168.1.100,镜像文件vxWorks)
  • 目标板IP规划:192.168.1.50

驱动实现要点

本驱动基于VxWorks END(Enhanced Network Driver)模型,核心工作包括:PCI设备探测与寄存器映射、描述符环初始化、中断服务程序(ISR)对接、以及endDevInit回调实现。驱动通过pciConfigInLong读取BAR地址完成MMIO映射,收发采用环形描述符(每个描述符对应一个缓冲块)。

关键完整代码


/* endDev.c - Intel 82559 END driver for VxWorks (PowerPC) */
#include <vxWorks.h>
#include <end.h>
#include <pci.h>
#include <intLib.h>
#include <netBufLib.h>

/* 寄存器偏移 */
#define CSR_CMD     0x0000
#define CSR_STATUS  0x0004
#define CSR_TPT     0x0038  /* 发送描述符尾 */
#define CSR_RDT     0x0028  /* 接收描述符尾 */
#define CSR_IMASK   0x00D0

/* 描述符结构(简化) */
typedef struct {
    UINT32 status;
    UINT32 cmd;
    UINT32 link;
    UINT32 bufAddr;
} DESC;

#define NUM_TX_DESC 16
#define NUM_RX_DESC 16
#define PKT_BUF_SIZE 2048

typedef struct {
    DESC txDesc[NUM_TX_DESC];
    DESC rxDesc[NUM_RX_DESC];
    char *txBuf[NUM_TX_DESC];
    char *rxBuf[NUM_RX_DESC];
    int txHead, txTail;
    int rxHead, rxTail;
    volatile UINT32 *regs;
    END_OBJ end;
} I82559_DEV;

static I82559_DEV i82559;

/* 中断处理 */
static void i82559Isr(void *arg)
{
    I82559_DEV *dev = (I82559_DEV *)arg;
    UINT32 status = dev->regs[CSR_STATUS/4];
    if (status & 0x01) {  /* RUS: 接收单元已停止或空闲 */
        /* 处理接收包 */
        while (dev->rxDesc[dev->rxTail].status & 0x8000) {
            int len = dev->rxDesc[dev->rxTail].status & 0x3FFF;
            END_OBJ *pEnd = &dev->end;
            M_BLK_ID pMblk = netBufLibAlloc(dev->rxBuf[dev->rxTail], len);
            if (pMblk) {
                pEnd->pReceiveRtn(pEnd, pMblk);
            }
            dev->rxDesc[dev->rxTail].status = 0;
            dev->rxTail = (dev->rxTail + 1) % NUM_RX_DESC;
            dev->regs[CSR_RDT/4] = dev->rxTail;
        }
    }
    if (status & 0x02) {  /* TUS: 发送完成 */
        dev->txHead = (dev->txHead + 1) % NUM_TX_DESC;
    }
}

/* 发送包 */
static int i82559Send(END_OBJ *pEnd, M_BLK_ID pMblk)
{
    I82559_DEV *dev = (I82559_DEV *)pEnd->pDevice;
    int len = pMblk->mLen;
    char *buf = (char *)pMblk->mData;
    int idx = dev->txHead;

    if (dev->txDesc[idx].status & 0x8000) return ERROR;  /* 描述符忙 */
    memcpy(dev->txBuf[idx], buf, len);
    dev->txDesc[idx].bufAddr = (UINT32)dev->txBuf[idx];
    dev->txDesc[idx].cmd = len | 0x01000000;  /* 发送+中断 */
    dev->txDesc[idx].status = 0x8000;
    dev->regs[CSR_TPT/4] = idx;
    return OK;
}

/* 设备初始化 */
static STATUS i82559Init(END_OBJ *pEnd)
{
    I82559_DEV *dev = (I82559_DEV *)pEnd->pDevice;
    int i;

    /* 复位网卡 */
    dev->regs[CSR_CMD/4] = 0x8000;  /* 软件复位 */
    taskDelay(1);
    dev->regs[CSR_CMD/4] = 0x0000;

    /* 初始化RX描述符 */
    for (i = 0; i < NUM_RX_DESC; i++) {
        dev->rxBuf[i] = (char *)memalign(8, PKT_BUF_SIZE);
        dev->rxDesc[i].bufAddr = (UINT32)dev->rxBuf[i];
        dev->rxDesc[i].status = 0x8000;  /* 就绪 */
    }
    dev->rxHead = dev->rxTail = 0;
    dev->regs[CSR_RDT/4] = NUM_RX_DESC - 1;

    /* 使能中断 */
    dev->regs[CSR_IMASK/4] = 0x0003;  /* RUS+TUS */
    intConnect(INUM_TO_IVEC(pEnd->pDevContext->intNum), i82559Isr, dev);
    intEnable(pEnd->pDevContext->intNum);
    return OK;
}

/* END设备创建 */
END_OBJ *i82559EndCreate(int unit, char *pName, void *pBSP)
{
    I82559_DEV *dev = &i82559;
    UINT32 bar0, bar1;
    int pciBus, pciDev, pciFunc;

    /* 查找PCI设备(Vendor 0x8086, Device 0x1229) */
    if (pciFindDevice(0x8086, 0x1229, 0, &pciBus, &pciDev, &pciFunc) != OK)
        return NULL;

    pciConfigInLong(pciBus, pciDev, pciFunc, PCI_CFG_BASE_ADDRESS_0, &bar0);
    pciConfigInLong(pciBus, pciDev, pciFunc, PCI_CFG_BASE_ADDRESS_1, &bar1);
    dev->regs = (volatile UINT32 *)(bar0 & 0xFFFFFFF0);

    /* 注册END对象 */
    dev->end.pDevice = dev;
    dev->end.pInit = i82559Init;
    dev->end.pSend = i82559Send;
    dev->end.pDevContext = (void *)pBSP;
    return &dev->end;
}

BSP集成配置


/* configNet.h 中添加 */
#define INCLUDE_END
#define I82559_LOAD_FUNC i82559EndCreate
#define I82559_LOAD_STRING "0,0:0"

运行与验证

  1. 将endDev.c编译链接到VxWorks镜像,确保INCLUDE_END和INCLUDE_TFTP已定义。
  2. 启动目标板,在shell中执行:
    ipAttach("i82559", "0", "192.168.1.50", "255.255.255.0")
  3. 测试网络连通性:
    ping "192.168.1.100"
    预期输出包含icmpEchoRequest的回复确认。
  4. 执行TFTP下载:
    tftp "192.168.1.100", "vxWorks", "0x100000"

通过条件:TFTP命令返回value = 0,且内存地址0x100000处可观察到有效镜像数据(前几个字节为0x00 0x00 0x00 0x00或PowerPC异常向量)。若ping不通,检查PCI设备枚举是否成功(pciFindDevice返回值)及中断号配置。

常见问题

  • PCI设备未找到:确认板载网卡为Intel 82559/82551,或修改pciFindDevice中的Vendor/Device ID。
  • 中断不触发:检查BSP中sysIntNum映射,确保PCI中断线正确连接。
  • 收发超时:增大PKT_BUF_SIZE或检查描述符环是否溢出(观察CSR_STATUS的RNR/TNO位)。