这一节中,我们重点分析一下驱动安装函数_io_dev_install_ext(xxx),看看驱动安装到底都做了些什么工作,又会遇到哪些新的东西!
文件:Io_instx.c (source\io)中
/*FUNCTION*-------------------------------------------------------------------
*
* Function Name : _io_dev_install_ext
* Returned Value : _mqx_uint a task error code or MQX_OK
* Comments :
* Install a device dynamically, so tasks can fopen to it. Different from
* _io_dev_install since this function also installs an unstall function.
*
*END*----------------------------------------------------------------------*/
_mqx_uint _io_dev_install_ext
(
/* [IN] A string that identifies the device for fopen */
char_ptr identifier,
/* [IN] The I/O open function */
_mqx_int (_CODE_PTR_ io_open)(MQX_FILE_PTR, char _PTR_, char _PTR_),
/* [IN] The I/O close function */
_mqx_int (_CODE_PTR_ io_close)(MQX_FILE_PTR),
/* [IN] The I/O read function */
_mqx_int (_CODE_PTR_ io_read)(MQX_FILE_PTR, char _PTR_, _mqx_int),
/* [IN] The I/O write function */
_mqx_int (_CODE_PTR_ io_write)(MQX_FILE_PTR, char _PTR_, _mqx_int),
/* [IN] The I/O ioctl function */
_mqx_int (_CODE_PTR_ io_ioctl)(MQX_FILE_PTR, _mqx_uint, pointer),
/* [IN] The I/O un-install function */
_mqx_int (_CODE_PTR_ io_uninstall)(IO_DEVICE_STRUCT_PTR),
/* [IN] The I/O initialization data */
pointer io_init_data_ptr
)
{ /* Body */
KERNEL_DATA_STRUCT_PTR kernel_data;
IO_DEVICE_STRUCT_PTR dev_ptr;
#if MQX_CHECK_ERRORS
_mqx_uint i;
_mqx_uint found = 0;
#endif
_GET_KERNEL_DATA(kernel_data);
#if MQX_CHECK_ERRORS
if ((io_open == NULL) || (io_close == NULL)){
return(MQX_INVALID_PARAMETER);
} /* Endif */
/* Search for delimiter */
for (i = 0; i < IO_MAXIMUM_NAME_LENGTH; i++)
{
if (identifier[i] == IO_DEV_DELIMITER)
{
found++;
}
else if (identifier[i] == '\0')
{
break;
} /* Endif */
} /* Endfor */
/*
** Return an error if more than 1 delimiter found, no delimiter was found
** or the identifier was composed of a single delimiter only.
*/
if ((found != 1) || (i == 1)) {
return(MQX_INVALID_PARAMETER);
} /* Endif */
/* START CR-169 */
#endif
/* Check to see if device already installed */
_lwsem_wait((LWSEM_STRUCT_PTR)&kernel_data->IO_LWSEM);
if (kernel_data->IO_DEVICES.NEXT == NULL) {
/* Set up the device driver queue */
_QUEUE_INIT(&kernel_data->IO_DEVICES, 0);
} /* Endif */
#if MQX_CHECK_ERRORS
dev_ptr = (IO_DEVICE_STRUCT_PTR)((pointer)kernel_data->IO_DEVICES.NEXT);
while (dev_ptr != (pointer)&kernel_data->IO_DEVICES.NEXT) {
if (!strncmp(identifier, dev_ptr->IDENTIFIER, IO_MAXIMUM_NAME_LENGTH)) {
_lwsem_post((LWSEM_STRUCT_PTR)&kernel_data->IO_LWSEM);
return(IO_DEVICE_EXISTS);
} /* Endif */
dev_ptr = (IO_DEVICE_STRUCT_PTR)((pointer)dev_ptr->QUEUE_ELEMENT.NEXT);
} /* Endwhile */
#endif
_lwsem_post((LWSEM_STRUCT_PTR)&kernel_data->IO_LWSEM);
/* END CR-169 */
dev_ptr = (IO_DEVICE_STRUCT_PTR)_mem_alloc_system_zero((_mem_size)
sizeof(IO_DEVICE_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
if (dev_ptr == NULL) {
return(MQX_OUT_OF_MEMORY);
}/* Endif */
#endif
_mem_set_type(dev_ptr, MEM_TYPE_IO_DEVICE);
dev_ptr->IDENTIFIER = identifier;
dev_ptr->IO_OPEN = io_open;
dev_ptr->IO_CLOSE = io_close;
dev_ptr->IO_READ = io_read;
dev_ptr->IO_WRITE = io_write;
dev_ptr->IO_IOCTL = io_ioctl;
dev_ptr->IO_UNINSTALL = io_uninstall;
dev_ptr->DRIVER_INIT_PTR = io_init_data_ptr;
_lwsem_wait((LWSEM_STRUCT_PTR)&kernel_data->IO_LWSEM);
_QUEUE_ENQUEUE(&kernel_data->IO_DEVICES, dev_ptr);
_lwsem_post((LWSEM_STRUCT_PTR)&kernel_data->IO_LWSEM);
return MQX_OK;
} /* Endbody */
分析:
首先传递参数有gpio的驱动标示符identifier,一般是“gpio:”,还有驱动操作函数指针,这个上次已经讲过了,剩下的那个就是初始化数据的指针,一般是传递一些特殊数据,这里设为NULL。
然后定义了两个变量:内核数据kernel_data和io设备指针dev_ptr,内核数据指针指向的数据主要包含系统内核状态信息,是一个全局变量,供各个模块使用。设备指针主要包含我们马上就要初始化的一些数据信息,下面会看到初始化的过程就是初始化这个dev_ptr指针,然后把这个指针链接进kernel_data的过程,这样系统就获得了GPIO的操作函数,完成初始化。
紧接着是宏定义MQX_CHECK_ERRORS,用户可以打开这个宏,进行参数的有效性校验,对不符合的参数进行错误处理,一般这个宏都是打开的,这才符合高质量编程的规范嘛!
下一步,获得信号量来保证初始化操作的互斥性,进一步检测kernel_data中的io设备结构体队列是否初始化,如果没有初始化(IO_DEVICES.NEXT = NULL),则先进行io设备队列的初始化。下面这个while循环,主要是判断在IO列表上是否已经存在初始化好的的io设备结构体,判断的根据就是传递过来的identifier标示符“GPIO:”。如果存在,就返回错误信息。
信号量使用完成后一定要释放,这是特别要注意的。当入口校验完毕,下一步就是进行dev_ptr结构体的初始化了,首先以初始化为0的方式分配内存空间,空间IO设备结构体大小。注意,一般在分配内存空间时,都需要进行空间分配是否成功的判断,因为分配内存会有分配失败的可能存在。但是我记得在学习linux时,linux也需要判断,而且linux返回的值也都是几乎都正确,因为linux在分配时,分配的并不是实际地址,而是一个逻辑地址,当对该空间操作时,才进行逻辑地址向屋里地址的映射。这里不是mqx是不是这样,不管是不是都需要判断,这是少不了的。
最后,进行dev_ptr结构体的初始化,给里边的各个变量赋值,把相关的io操作函数进行绑定。绑定完成后,再把设备结构体链接到内核数据结构上,这样内核数据结构就可以根据一个io结构体,找到对应的io操作函数,这样就完成了GPIO驱动函数与MQX内核之间的数据映射,下一步要做的工作就是根据GPIO硬件属性,完成对应驱动函数的功能实现了。
至于MQX内核是怎样进行相关函数调用,组合内部逻辑的,这个我们暂时还看不到迹象,或许以后可以看到!另:该函数里边用到了许多队列相关知识,而且大概看了一下,队列的使用中有好多技巧值得我们学习,故下一步要详细分析一下啦!