MQX3.8源代码分析:GPIO(5)驱动安装函数 _io_dev_install_ext()
这一节中,我们重点分析一下驱动安装函数_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内核是怎样进行相关函数调用,组合内部逻辑的,这个我们暂时还看不到迹象,或许以后可以看到!另:该函数里边用到了许多队列相关知识,而且大概看了一下,队列的使用中有好多技巧值得我们学习,故下一步要详细分析一下啦!