在嵌入式系统中,设备驱动程序提供了对底层设备的可访问性和可操作性,应用程序通过设备驱动才能与底层设备进行交互。MQX使用了兼容POSIX标准的统一设备管理模型管理设备驱动程序,要求设备驱动函数的设计必须按照标准的调用接口进行,这样的驱动函数专用于MQX系统,不具备良好的可移植性。但事实上,对底层设备的访问与是否使用操作无关,MQX设备驱动对底层设备的访问与NOS(无操作系统)的底层驱动构件是相同的。
MQX统一设备模型开发框架
编写MQX驱动程序,就是建立这些标准的调用接口到作用于具体硬件设备底层驱动函数的映射,实现操作系统对设备具体控制。
MQX的驱动管理体系结构分为三层:应用输入输出层、输入输出子系统层和设备驱动。
在应用输入输出层,MQX为用户提供兼容POSIX标准的文件式操作界面,包括fopen、fclose、read、write、ioctl等函数调用。MQX内核的输入输出子系统负责对设备驱动进行统一调配管理。设备驱动直接访问物理设备,控制底层硬件。其中,设备驱动是驱动程序设计的主要内容。
设备驱动程序实际上是一系列与控制硬件设备相关的驱动函数及数据结构。设备驱动的工作方式存在共性,所有设备驱动的设计与使用也大致相同:
1、初始化/停用。为设备正常工作做好准备并启动设备功能;关闭设备功能释放硬件资源。
2、输入/输出。调用驱动与外部环境进行交互。
3、控制设备。配置设备自身的工作属性。
MQX将这些功能以文件操作的方式进行抽象,使用统一的设备节点类型IO_DEVICE_STRUCT保存驱动信息,定义如下:
typedef struct io_device_struct
{
char_ptr IDENTIFIER; //设备名
_mqx_int (_CODE_PTR_ IO_OPEN)(MQX_FILE_PTR,char_ptr,char_ptr);//初始化设备
_mqx_int (_CODE_PTR_ IO_CLOSE)(MQX_FILE_PTR);//关闭设备功能
_mqx_int (_CODE_PTR_ IO_READ)(MQX_FILE_PTR,char_ptr,_mqx_int);//从设备读
_mqx_int (_CODE_PTR_ IO_WRITE)(MQX_FILE_PTR,char_ptr,_mqx_int);//向设备写
_mqx_int (_CODE_PTR_ IO_IOCTL)(MQX_FILE_PTR,_mqx_uint,pointer);//控制设备
pointer DRIVER_INIT_PTR;//驱动属性信息
。。。
} IO_DEVICE_STRUCT, _PTR_ IO_DEVICE_STRUCT_PTR;
打开设备文件后,fopen函数向用户返回已经与节点建立关联的设备文件的句柄MQX_FILE_PTR,除fopen函数在内部可以定位设备驱动节点外,fclose、read、write、ioctl等函数均可通过传入的文件句柄获取对关联设备节点的访问,通过其中相应的驱动调用接口调用设备驱动函数。
通过这种方式,MQX得以将对设备驱动的反问抽象成文件操作,用户通过文件名(设备标识名)标识文件对象,而不直接调用具体的底层设备驱动,此时,将数据写入文件的操作意味着该信息可能被发送到与该文件建立关联的输出终端或是通信总线上,这取决于驱动开发者将这个设备标识名映射到哪个具体的硬件设备上。
MQX驱动开发的基本方法:
用户使用设备驱动服务时,调用fopen对设备进行初始化并获得与设备节点建立关联的设备文件句柄,其它文件操作函数接收设备文件句柄作为传入参数,通过文件句柄定位管理器中的设备节点,进而执行相应驱动调用接口的设备驱动程序。在MQX的统一设备驱动管理框架下,为目标设备编写驱动程序,主要是实现设备节点的相应驱动函数接口的功能,就是要编写初始化设备、关闭设备功能、从设备读信息、向设备发送信息及控制设备属性的驱动函数,并同设备节点建立映射。需要具体实现的底层驱动函数与统一设备驱动管理框架下的文件操作对应表如下:其中device指代目标设备名称:
文件操作函数 设备节点接口 目标设备驱动函数 驱动函数功能
fopen IO_OPEN io_device_open 初始化设备
fclose IO_CLOSE io_device_close 关闭设备功能
read IO_READ io_device_read 从设备读信息
write IO_WRITE io_device_write 向设备发送信息
ioctl IO_IOCTL io_device_ioctl 控制设备属性
根据设备节点接口的定义对具体的设备驱动函数分别予以实现。
1、io_device_open
这个函数是必须实现的,用户使用fopen打开设备时此函数被调用,是初始化设备驱动函数,函数声明如下:
int_32 io_device_open(MQX_FILE_PTR fd_ptr, char_ptr open_name_ptr,char_ptr flags);//
功能:初始化设备驱动函数,由fopen函数调用。
函数返回:函数执行状态,MQX_OK执行正常;IO_ERROR执行出错。
函数参数:fd_ptr设备文件句柄;
open_name_ptr设备标识名,由用户通过fopen传入;
flags:初始化属性,由用户通过fopen传入。
2、io_device_close
这个函数也是必须实现的,用户使用fclose关闭设备时此函数被调用,实现关闭设备功能并释放驱动程序占用资源,函数声明如下:
int_32 io_device_close(MQX_FILE_PTR fd_ptr);
功能:关闭设备,释放驱动资源,由fclose函数调用;
函数返回:函数执行状态。MQX_OK执行正常;IO_ERROR执行出错;
函数参数:fd_ptr设备文件句柄。
如果在关闭设备时不需要底层驱动执行任何操作,在实现该函数时,可不在其中添加任何语句。
3、io_device_read
该函数不是必须实现的,只有为支持读操作的设备编写驱动时才实现此函数,例如串行总线通信或模拟/数字转换模块等。用户使用read通过设备获取数据时此函数被调用,函数声明如下:
int_32 io_device_read(MQX_FILE_PTR fd_ptr, char_ptr data_ptr, int_32 num);
功能:通过设备获取数据,由read函数调用;
函数返回:读到的字节数----执行正常;IO_ERROR执行出错;
函数参数:fd_ptr设备文件句柄,由用户通过read传入;
data_ptr读数据缓冲区,由用户通过read传入;
num读数量,由用户通过read传入。
4、io_device_write
该函数不是必须实现的,只有为支持写操作的设备编写驱动时才实现此函数,例如串行总线通信或数字/模拟转换模块等。用户使用write通过设备获取数据时,此函数被调用,函数声明如下:
int_32 io_device_write(MQX_FILE_PTR fd_ptr ,char_ptr data_ptr,int_32 num);
功能:通过设备获取数据,由write函数调用。
函数返回:写入的字节数;IO_ERROR执行出错。
函数参数:fd_ptr设备文件句柄,由用户通过write传入;
data_ptr写数据缓冲区,由用户通过write传入;
num写字节数,由用户通过write传入。
5、io_device_ioctl
该函数不是必须实现的。在可能更改设备属性时才实现此函数,当调用ioctl函数对更改设备属性配置时,此函数便被调用。函数声明如下:
int_32 io_device_ioctl(MQX_FILE_PTR fd_ptr,int_32 cmd,pointer param_ptr);
功能:控制设备属性,由ioctl函数调用;
函数返回:MQX_OK执行正常;IO_ERROR执行出错;
函数参数:fd_ptr 设备文件句柄,由用户通过ioctl传入;
cmd命令码,由用户通过write传入;
param_ptr命令参数,由用户通过write传入。
其中,命令码是由设备驱动开发者自行设计的,同时由于命令参数的定义为通用指针类型pointer,可以实现灵活传参。
6、io_device_install
MQX为加载设备驱动函数提供了专门的API------io_dev_install,用于注册设备标识名、设备驱动函数及其数据结构,函数接口如下:
uint_32 _io_dev_install(char_ptr identifier,IO_OPEN_FPTR io_open,IO_CLOSE_FPTR io_close,IO_READ_FPTR io_read,IO_WRITE_FPTR io_write,IO_IOCTL_FPTR io_ioctl,pointer io_init_data_ptr);
功能:加载设备驱动;
函数返回:MQ_OK执行正常;其它值---执行出错;
函数参数:identifier设备标识名;
io_open初始化设备驱动函数
io_close关闭设备驱动函数;
io_read从设备读驱动函数;
io_write向设备写驱动函数;
io_ioctl控制设备属性函数;
io_init_data_ptr设备驱动数据结构。
在_io_dev_install函数内部,创建一个设备节点,传入的设备驱动信息填入后,将其添加到设备管理器的设备队列中去。
为了实现对设备驱动函数的封装,通常要编写io_device_install函数,根据设计需要为系统调用提供设备标识名及可选择使用的驱动数据结构。例如:
uint_32 io_device_install(char_ptr identifier)
{
return _io_dev_install(identifier,io_decice_open,io_decice_close,io_decice_read,io_decice_write,io_decice_ioctl,NULL);
}
当在系统启动过程中调用io_devcie_install("device:")时,自定义名称为"device:"的设备驱动就加载到系统中了。