VxWorks的内核驱动层次:

所有类型(包括网络设备)的设备都必须向 IO 子系统进行注册方可被内核访问。

ioLib.c 文件称为上层接口子系统,上层接口子系统直接对用户层可见
iosLib.c 文件称为 IO 子系统,而 IO 子系统则一般不可见,其作为上层接口子系统与下层驱动系统的中间层而存在。由于 IO 子系统在整个驱动层次中起着管理的功能,其维护着系统设备和驱动的关键的三张表
1、系统设备表
Vxworks 内核对每个设备使用 DEV_HDR 数据结构进行表示,该结构定义如下。
/h/iosLib.h/
typedef struct /* DEV_HDR - device header for all device structures /
{
DL_NODE node; /
device linked list node /
short drvNum; /
driver number for this device /
char * name; /
device name */
} DEV_HDR;
底层驱动对其驱动的设备都有一个自定义数据结构表示,其中包含了被驱动设备寄存器基地址,中断号,可能的数据缓冲区,保存内核回调函数的指针,以及一些标志位。最为关键的一点是 DEV_HDR 内核结构必须是这个自定义数据结构的第一个成员变量,因为这个用户自定义结构最后需要添加到系统设备队列中,故必须能够在用户自定义结构与 DEV_HDR 结构之间进行转换,而将 DEV_HDR 结构设置为用户自定义结构的第一个成员变量就可以达到这个目的。
typedef struct xxDev
{
DEV_HDR devHdr; //内核提供的结构,必须是自定义结构的第一个成员变量。
UINT32 regBase; //设备寄存器基地址。
UINT32 buffPtr; //数据缓冲区基地址。
BOOL isOpen; //设备已打开标志位。
UINT8 intLvl; //设备中断号。 FUNCPTR putData; //内核回调函数指针,该指针指向的函数向内核提供数据。
FUNCPTR getData; //内核回调函数指针,该指针指向的函数从内核获取数据。
… //其他设备参数。
}

驱动程序必须将设备注册到 IO 子系统中,这个过程也被称为创建设备节点。
IO 子系统提供的 iosDevAdd 函数用以被驱动程序调用注册一个设备。该函数调用原型如下。
STATUS iosDevAdd
(
DEV_HDR pDevHdr, / pointer to device’s structure */
char name, / name of device /
int drvnum /
no. of servicing driver, /
/
returned by iosDrvInstall() */
);
参数 1 是一个 DEV_HDR 结构类型,一般我们将用户自定义结构作为第一个参数传入,这也是必须将 DEV_HDR 结构类型的成员变量作为用户自定义结构的第一个成员的原因所在;
参数 2 表示设备节点名,这个名称将被用户程序调用作为打开设备时的路径使用。
参数 3 是设备对应的驱动程序索引号。这个驱动号是 iosDrvInstall 函数的返回值。
在设备初始化函数中,我们首先调用 iosDrvInstall 注册驱动,然后使用 iosDrvInstall 函数返回的驱动号调用 iosDevAdd 添加设备到系统中,这两步完成之后,设备就可以被用户程序使用了。

iosDevAdd 函数将一个设备添加到由 IO 子系统维护的系统设备列表中,该列表是一个队列,队列中成员通过指针链接在一起,这是由 DEV_HDR 结构中 node 成员变量完成的。系统设备列表由 iosDvList 内核变量指向。

系统设备列表中第一个设备是内核本身添加的,这是一个 null 设备,所有写入 null 设备的数据都将被直接丢弃,这种机制对于屏蔽一些输出十分有效。

用户可在命令行下输入 iosDevShow 或 devs,显示系统设备中的所有设备。

2、系统驱动表
IO 子系统维护的系统驱动表包含了当前注册到 IO 子系统下的所有驱动。
系统驱动表底层实现是一个数组,数组元素数目在 Vxworks 内核初始化过程中初始化 IO 子系统时指定。iosInit 函数用以初始化 IO 子系统,iosInit 函数调用原型如下。
STATUS iosInit
(
int max_drivers, /* maximum number of drivers allowed /
int max_files, /
max number of files allowed open at once */
char nullDevName / name of the null device (bit bucket) */
);
参数 1 指定系统驱动表元素数目,即系统最多支持的驱动数。
参数 2 指定系统同时打开的最大文件数,这个参数实际上指定了系统文件描述符表的元素数目。
参数 3 指定了 null 设备的设备节点名,一般为“/null”。

系统驱动表在内核中由 drvTable 表示,其声明如下:
DRV_ENTRY * drvTable; /* driver entry point table /
在 iosInit 函数根据传入的最大驱动数目对 drvTable 进行初始化,如以下代码所示。
/
allocate driver table and make all entries null */
size = maxDrivers * sizeof (DRV_ENTRY);
drvTable = (DRV_ENTRY *) malloc ((unsigned) size);

系统驱动表中每个表项都是一个 DRV_ENTRY 类型的结构,该结构定义在 h/private/iosLibP.h文件中,如下:
typedef struct /* DRV_ENTRY - entries in driver jump table */
{
FUNCPTR de_create;
FUNCPTR de_delete;
FUNCPTR de_open;
FUNCPTR de_close;
FUNCPTR de_read;
FUNCPTR de_write;
FUNCPTR de_ioctl;
BOOL de_inuse;
} DRV_ENTRY;
可以看出 DRV_ENTRY 实际上就是一个函数指针结构,结构中每个成员都指向一个完成特定功能的函数,这些函数与用户层提供标准函数接口一一对应。成员 de_inuse 用以表示一个表项是否空闲。

IO 子系统提供 iosDrvInstall 供驱动程序注册用,iosDrvInstall 函数调用原型如下。
int iosDrvInstall
(
FUNCPTR pCreate, /* pointer to driver create function /
FUNCPTR pDelete, /
pointer to driver delete function /
FUNCPTR pOpen, /
pointer to driver open function /
FUNCPTR pClose, /
pointer to driver close function /
FUNCPTR pRead, /
pointer to driver read function /
FUNCPTR pWrite, /
pointer to driver write function /
FUNCPTR pIoctl /
pointer to driver ioctl function */
);
iosDrvInstall 函数基本实现即遍历drvTable 数组,查询一个空闲表项,用传入的函数地址对表项中各成员变量进行初始化,并将 de_inuse 设置为 TRUE,最后返回该表项在数组中的索引作为驱动号,这个驱动号将被作为iosDevAdd的传入参数。

用户可在命令行下输入 iosDrvShow,显示系统驱动表中当前存储的所有驱动。

3、系统文件描述符表

系统文件描述附表,即当前系统范围内打开的所有文件描述符都将存储在该表中。

对于文件描述符有一点需要注意:标准输入,标准输出,标准错误输出虽然使用 0,1,2 三个文件描述符,但是可能在系统文件描述附表中只占用一个表项,即都使用同一个表项。

当使用一个文件描述符进行操作时,如调用 write 函数,内核首先检查文件描述符是否是 0,1,2 标准输入输出描述符,如是,则依次为索引查询 ioStdFd,以 ioStdFd[fd]作为索引查询系统文件描述符表,获得驱动号,进而索引系统驱动表,调用对应表项 de_write 指向的函数,完成对设备的写入操作;如果文件描述符大于 2,表示这是一个普通的文件描述符,那么就直接以该描述符作为索引查询系统文件描述表,获得驱动号,进而索引系统驱动表,调用相关函数。

系统文件描述符表中每个表项都是一个 FD_ENTRY 类型的结构,该结构定义在
h/private/iosLibP.h 中,如下所示。
typedef struct /* FD_ENTRY - entries in file table /
{
DEV_HDR * pDevHdr;/
device header for this file /
int value; /
driver’s id for this file /
char * name; /
actual file name /
int taskId; /
task to receive SIGIO when enabled /
BOOL inuse; /
active entry /
BOOL obsolete; /
underlying driver has been deleted /
void * auxValue;/
driver specific ptr, e.g. socket type /
void * reserved; /
reserved for driver use */
} FD_ENTRY;
注意 FD_ENTRY 结构的第一个成员就是 DEV_HDR 结构类型,该结构中存储了设备节点名和驱动号。
FD_ENTRY 结构中 value 成员表示驱动附加信息,并非驱动号,实际上这个字段被用以保存底层驱动中 open 实现函数的返回值. 这个返回值意义重大,因为其后驱动中read,write 等实现函数被调用时,IO 子系统就以这个返回值作为这些函数的第一个参数。

系统文件描述符表有内核变量 fdTable 指向,该变量声明如下。
FD_ENTRY * fdTable; /* table of fd entries */
fdTable 的初始化在 iosInit 函数中完成,该函数调用原型如前文所示,传入该函数的第二个
参数指定了 fdTable 数组的大小,该变量的初始化代码示例如下。
size = maxFiles * sizeof (FD_ENTRY);
fdTable = (FD_ENTRY *) malloc ((unsigned) size);

用户程序每调用一次 open 函数,系统文件描述符表中就增加一个有效表项,直到数组满,此时 open 函数调用将以失败返回

用户可在命令行下输入 iosFdShow,显示系统文件描述附表中当前所有有效表项。

4、三张表之间的联系
用户调用 open 函数打开”/xx0”文件,Vxworks 内核 IO 子系统将进行如下一系列响应。
[1] 其使用文件路径名匹配系统设备表,查询一个匹配设备。此处在设备列表中找到了一个匹配的设备。
[2] 其在系统文件描述符表中预留一个空闲项,用以创建一个文件描述符,如果后续调用成功,将以这个空闲项对应的索引(偏移 3)值返回给用户,作为文件描述符使用。
[3] 其根据设备列表匹配项中信息得到驱动号,进而以此驱动号为索引从系统驱动表中获取底层设备驱动对应用户层 open 调用的响应函数 x_open。x_open 的第一个参数被设置为对应 硬件设备结构,第二个参数为除去设备名本身余下的部分,此处为 NULL,第三,四个参数为用户传入的权限和模式参数。x_open 将完成硬件设备的配置,使能工作,注册中断等等,为用户接下来可能的读写设备操作做好准备。x_open 同时对传入的第一个参数:设备结构进行初始化,这个结构将在后续操作中一直被底层驱动使用。

[4] 底层驱动返回设备结构,表示底层打开设备成功,否则返回 NULL 或 ERROR 表示调用失败。
[5] IO 子系统对文件描述符表中预留的空闲项进行初始化,填入驱动号和设备结构。
[6] 最后 open 函数调用返回一个文件描述符,这个描述符是文件描述符之前被预留空闲项(现在已得到初始化,被占用使用)在表中的索引值(偏移 3)。此处即文件描述符表中的第一个表项,即 fd=0+3=3.

一般情况下的编写流程:
1、自定义一个驱动设备结构体
typedef struct xxx_dev{
DEV_HDR pDevHdr; //必须定义且放在最开始

}xxx_DEV
2、注册驱动
创建xxxDrv()函数,这个函数调用iosDrvInstall 函数将设备驱动添加到系统驱动表中,完成注册。
//注意:Vxworks 下 LOCAL=static
LOCAL int xxxDrvNum=-1; //定义一个整型全局变量,保存 iosDrvInstall 返回的驱动号。
STATUS xxxDrv()
{
if(xxxDrvNum!=-1) //如果 xxxDrvNum 不等于-1,则表示驱动已经注册,此时直接返回。
return (OK);
xxxDrvNum=iosDrvInstall(
xxxOpen, /creat/
xxxDelete, /delete/
xxxOpen, /open/
xxxClose, /close/
xxxRead, /read/
xxxWrite, /write/
xxxIoctl); /ioctl/
return (xxxDrvNum==ERROR?ERROR:OK);
}
3、设备的创建
创建xxxDevCreate 函数,这个函数在调用 iosDevAdd函数添加设备时,需要指定设备对应的驱动程序驱动号,就是 spiDrvNum 中存储的驱动号。
devName = “/xxxdev”; //也可以通过参数传入
STATUS xxxDevCreate(参数1 ,参数2,…)
{
xxx_DEV *pxxxDev;
pxxxDev=(xxx_DEV *)malloc(sizeof(xxx_DEV));
bzero(pxxxDev, sizeof(xxx_DEV));
… //设置其他参数
//将设备添加到系统设备列表中。
if(iosDevAdd(&pxxxDev->pDevHdr, devName, xxxDrvNum) == ERROR){
free((char *)pxxxDev);
return (ERROR);
}
return (OK);
}
4、具体实现底层驱动服务函数
… xxx_open(… ){
}
… xxx_close(… ){
}
… xxx_write(… ){
}
… xxx_read(… ){
}
… xxx_ioctl(… ){
}
… xxx_delete(… ){
}

VxWorks驱动编写流程(非VxBus模式下)相关推荐

  1. connect函数在阻塞和非阻塞模式下的行为

    connect函数在阻塞和非阻塞模式下的行为 当socket使用阻塞模式时,connect函数会阻塞到有明确结果才会返回,如果网络环境较差,可能要等一会,影响体验, 为了解决这个问题,我们使用异步co ...

  2. socket的阻塞模式和非阻塞模式(send和recv函数在阻塞和非阻塞模式下的表现)

    socket的阻塞模式和非阻塞模式 无论是Windows还是Linux,默认创建socket都是阻塞模式的 在Linux中,可以再创建socket是直接将它设置为非阻塞模式 int socket (i ...

  3. Qt:Qt实现Winsock网络编程—非阻塞模式下的简单远程控制的开发(WSAAsyncSelect)

    Qt实现Winsock网络编程-非阻塞模式下的简单远程控制的开发(WSAAsyncSelect) 前言 这边博客应该是 Qt实现Winsock网络编程-TCP服务端和客户端通信(多线程) 的姐妹篇,上 ...

  4. 网桥接口非混杂模式下数据包转发

    网桥接口处在非混杂模式下,只能接收目的MAC地址为自身的数据包,也就是说如果数据包的目的MAC为其它地址,将会被丢弃掉.对于单网口的设备这样没有问题,但是对于存在多个网卡的交换设备,如果从一个网口接收 ...

  5. oracle非归档模式下如何备份,Oracle之RMAN数据库在非归档模式下的备份和恢复

    1.数据库在非归档模式下的备份 SQLgt; archive log list;数据库日志模式 非存档模式自动存档 禁用存档终点 USE_DB_RECOVERY_FIL 1.数据库在非归档模式下的备份 ...

  6. oracle在非归档模式下,Oracle在非归档模式下不能更改表空间为备份模式

    Oracle表空间设置为备份模式后,便可以联机对表空间下数据文件进行文件系统级别的copy备份操作,因为期间对表空间的修改都记录到数据库的重做日志文件中. 由此想到数据库如果是非归档模式,那么这个表空 ...

  7. 非归档模式下重做日志覆盖后的rman恢复

    非归档模式下重做日志覆盖后的rman恢复 实验原理:在非归档模式下,数据库的重做日志不会写入归档日志中,对数据库的恢复只能依靠3个联机重做日志.当第一个重做日志满了,就切换第二个重做日志中,以此类推, ...

  8. Oracle10g数据库归档与非归档模式下的备份与恢复

    一.总述 1.数据库归档模式: * 非归档模式:当数据库数据只读不会改变时,数据不会改变,数据库适合用非归档模式,  这样提高机能 * 归档模式:对于数据库数据经常变换,数据库最好用归档模式,这样可以 ...

  9. oracle11 rman全备,Oracle 11g非归档模式下mount状态RMAN究竟能不能进行全备?

    Oracle 11g非归档模式下mount状态RMAN究竟能不能进行全备? 网上很多博客说可以备份,但是我做出的实验都是失败的,请教大家究竟Oracle 11g非归档模式下mount状态RMAN究竟能 ...

最新文章

  1. 【数据结构-查找】1.通俗易懂讲解 —— 顺序-折半-分块查找
  2. zxing android最新下载,Zxing简单集成
  3. SAP 电商云 UI 服务器端渲染的建议架构
  4. 数码管和573锁存器的细节问题
  5. 电脑公司Win11 64位全新旗舰版镜像V2021.08
  6. 当Elasticsearch遇见智能客服机器人
  7. SpringMvc-NativeWebRequest接口
  8. java 压缩包添加文件,如何在Java中向现有zip文件添加条目?
  9. JSP 获取Request 经常使用參数
  10. linux服务器上使用apache部署静态html网页
  11. python 3d大数据可视化软件_4个最受欢迎的大数据可视化工具
  12. 湘潭比赛有感---铩羽之行
  13. Smarty中文手册
  14. cab文件介绍及制作方法 1
  15. Gtalk怎么样绑定MSN帐号,在Gtalk里面聊MSN的方法
  16. 移动端字体加粗的解决方案
  17. 这一份最全的TCP总结,请务必收下
  18. 写的非常不错的一篇阻塞与非阻塞、同步与异步套接字之间的区别
  19. 微信小程序云开发初步上手
  20. Titan框架入门指南:Titan如何工作

热门文章

  1. 程序员看过都说好的资源网站,你值得拥有。
  2. 按月收费,Win 10 将变成 Win 365 吗;苹果推出 Swift 资格认证
  3. 前端微信公众号开发总结(准备篇)
  4. FixedRate与FixedDelay的区别
  5. 青龙脚本(中国电信,附脚本)
  6. C语言蓝桥杯刷题:删除字符
  7. 大疆无人机二次开发简介-引入DJI mobile SDK 4.8
  8. 深度学习神经网络入门案例详细解析-鸢尾花案例
  9. SAP EPIC 银企直连 农业银行 Socket 接口项目实践
  10. 机器人自己造自己,究竟是怎么办到的?