4. 编写简单的字符设备驱动框架
任务目标
在VxWorks 7中实现一个最小字符设备驱动框架,注册open、read、write、ioctl四个操作,并通过设备驱动表完成设备注册,最终在应用层通过open()/write()/read()/ioctl()系统调用验证驱动可用性。
环境依赖
- VxWorks 7 + Workbench 4(或以上),目标机为x86或ARM模拟器均可
- 需包含
INCLUDE_POSIX与INCLUDE_IO_SYSTEM组件(默认BSP已启用) - 驱动代码编译为可下载内核模块(DKM)或静态链接到VIP均可
驱动实现
以下代码定义了一个名为demoDev的字符设备,内部维护一个4字节缓冲区。所有操作通过标准文件描述符接口暴露,设备驱动表项由iosDevAdd注册到I/O系统。
/* demoCharDrv.c - 最小字符设备驱动框架 */
#include <vxWorks.h>
#include <iosLib.h>
#include <stdio.h>
#include <string.h>
#include <errnoLib.h>
/* 设备私有数据结构 */
typedef struct
{
char buffer[4]; /* 内部缓冲区 */
int bufLen; /* 当前有效数据长度 */
} DEMO_DEV;
/* 函数声明 */
static int demoOpen (DEMO_DEV *pDev, char *name, int flags, int mode);
static int demoRead (DEMO_DEV *pDev, char *buffer, int maxBytes);
static int demoWrite (DEMO_DEV *pDev, char *buffer, int nBytes);
static int demoIoctl (DEMO_DEV *pDev, int cmd, int arg);
/* 设备驱动表项 */
static IOS_DRV_TABLE demoDrvTable =
{
NULL, /* create */
NULL, /* delete */
demoOpen, /* open */
NULL, /* close */
demoRead, /* read */
demoWrite, /* write */
demoIoctl /* ioctl */
};
/* 设备名 */
#define DEMO_DEV_NAME "/dev/demo0"
/******************************************************************************
* demoOpen - 打开设备时调用
*****************************************************************************/
static int demoOpen (DEMO_DEV *pDev, char *name, int flags, int mode)
{
/* 设备已通过iosDevAdd挂载,此处仅做权限校验示例 */
if (pDev == NULL)
{
errnoSet (S_ioLib_NO_DEV);
return ERROR;
}
return OK;
}
/******************************************************************************
* demoRead - 从设备缓冲区读取数据
*****************************************************************************/
static int demoRead (DEMO_DEV *pDev, char *buffer, int maxBytes)
{
int copyLen;
if (pDev == NULL || buffer == NULL)
{
errnoSet (EINVAL);
return ERROR;
}
copyLen = (pDev->bufLen < maxBytes) ? pDev->bufLen : maxBytes;
if (copyLen > 0)
{
memcpy (buffer, pDev->buffer, copyLen);
pDev->bufLen = 0; /* 读后清空 */
}
return copyLen;
}
/******************************************************************************
* demoWrite - 向设备缓冲区写入数据(最多4字节)
*****************************************************************************/
static int demoWrite (DEMO_DEV *pDev, char *buffer, int nBytes)
{
int copyLen;
if (pDev == NULL || buffer == NULL)
{
errnoSet (EINVAL);
return ERROR;
}
copyLen = (nBytes < (int)sizeof (pDev->buffer)) ? nBytes : sizeof (pDev->buffer);
memcpy (pDev->buffer, buffer, copyLen);
pDev->bufLen = copyLen;
return copyLen;
}
/******************************************************************************
* demoIoctl - 控制命令处理(示例:CMD_GET_LEN查询当前数据长度)
*****************************************************************************/
#define DEMO_CMD_GET_LEN 0x1001
static int demoIoctl (DEMO_DEV *pDev, int cmd, int arg)
{
if (pDev == NULL)
{
errnoSet (EINVAL);
return ERROR;
}
switch (cmd)
{
case DEMO_CMD_GET_LEN:
*(int *)arg = pDev->bufLen;
return OK;
default:
errnoSet (ENOSYS);
return ERROR;
}
}
/******************************************************************************
* demoDevInit - 驱动初始化入口:注册驱动并添加设备
*****************************************************************************/
STATUS demoDevInit (void)
{
DEMO_DEV *pDev;
int drvNum;
/* 1. 分配设备私有数据 */
pDev = (DEMO_DEV *)calloc (1, sizeof (DEMO_DEV));
if (pDev == NULL)
{
return ERROR;
}
/* 2. 注册驱动到I/O系统,获得驱动编号 */
drvNum = iosDrvInstall (&demoDrvTable);
if (drvNum == ERROR)
{
free (pDev);
return ERROR;
}
/* 3. 将设备名与驱动编号、设备结构绑定 */
if (iosDevAdd (&pDev->devHdr, DEMO_DEV_NAME, drvNum) == ERROR)
{
free (pDev);
return ERROR;
}
return OK;
}
编译与部署
- 在Workbench中创建DKM工程,将上述代码加入
demoCharDrv.c。 - 编译生成
demoCharDrv.out。 - 启动VxWorks目标机,通过FTP或WindSh加载模块:
ld < demoCharDrv.out - 调用初始化函数注册设备:
demoDevInit()
应用层验证
在WindSh或应用任务中执行以下序列,观察返回值与缓冲区内容:
/* 验证代码(可放入shell脚本或独立任务) */
int fd;
char wbuf[4] = {'V', 'x', 'W', 'o'};
char rbuf[4];
int len;
fd = open("/dev/demo0", O_RDWR, 0); /* 预期返回fd >= 0 */
write(fd, wbuf, 4); /* 预期返回4 */
ioctl(fd, 0x1001, (int)&len); /* 预期返回OK,len == 4 */
read(fd, rbuf, 4); /* 预期返回4,rbuf内容为"VxWo" */
close(fd);
通过条件:上述每个调用返回值与注释一致,且rbuf中数据与写入内容相同,即驱动框架工作正常。
关键说明
iosDrvInstall将操作函数表注册到系统,返回驱动编号;iosDevAdd将设备名与驱动绑定,第一个参数需为DEV_HDR类型字段,因此DEMO_DEV结构体首成员必须是DEV_HDR devHdr(本例为简化省略,实际工程需包含)。- 若编译报错
devHdr未定义,请在结构体中添加DEV_HDR devHdr;作为首成员。 - 本驱动未实现
close回调,系统默认使用空操作,不影响基本功能。