VxWorks驱动编写流程(非VxBus模式下)

原文地址:https://blog.csdn.net/weixin_44048491/article/details/113701183
整理了一下排版,方便学习参考代码。

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, char name, int drvnum);// drvnum是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) return (OK);//如果 xxxDrvNum 不等于-1,则表示驱动已经注册,此时直接返回。
	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(){
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值