Linux驱动
Linux字符设备驱动框架字符设备以字节流方式访问的设备块设备以块为单位读写的设备网络设备用于网络通讯的设备应用层序访问驱动驱动是为了制定规则统一内核内部访问硬件的方法mknod创建设备节点然后文件系统会创建对应的struct inode当open设备文件时VFS创建了一个struct fileVFS通过chrdev_open根据inode中的i_rdev查找cdev_map并用i_cdev记录找到的struct cdev同时让file指针的file_ops指向cdev中的file_ops地址。由于open后struct inode保存了struct cdev地址所以可以通过cdev使用container_of获取其他设备资源这是file结构体也传进来了所以可以使用file.private_data来传递设备资源#include linux/init.h #include linux/module.h MODULE_LICENSE(GPL v2); static int hello_init(void){return 0;} static void hello_exit(void){return;} module_init(hello_init); module_exit(hello_exit);cdev_init()初始化cdev结构体绑定file_operationsalloc_chrdev_region()自动注册设备号cdev_add()将cdev和设备号绑定在cdev_map上class_create()创建设备类/sys/classdevice_create()通过struct class发送uevent事件通知mdev创建设备platform子系统如果把设备硬件和设备驱动写在一起那么这一个设备驱动只能在这一个板子上运行换一个板子它的硬件信息就有改变也就驱动的移植性差所以linux提供了一个总线的概念将设备的硬件信息和驱动提供的操作方法分离platform总线提供两个链表只需在总线上注册设备和驱动设备和驱动就可以通过platform总线进行双向匹配插入设备便会遍历驱动的链表编入驱动模块便会遍历设备的链表当匹配完成后就会进入platform_driver的probe函数通过其参数获取设备信息并进行初始化。基于总线写驱动流程根据设备确定总线类型根据总线类型确定设备在总线上如何描述struct platform_device根据总线类型确定驱动在总线上如何描述struct platform_driver根据总线类型确定如何在总线上注册设备platform_device_register根据总线类型确定如何在总线上注册驱动platform_driver_register确定设备和驱动匹配原则name、id_table、of_match_table)设备和驱动匹配后linux调用probe函数获取硬件资源、注册字符设备struct platform_devicename设备名字num_resources资源个数resource资源数组platform_get_resource获取成员包含资源的开始资源的结束与资源类型寄存器地址、中断号、DMA资源id_entry当与driver的id_table匹配上时就会记录其地址用来让驱动知道其是什么设备struct platform_driverprobe设备和驱动匹配上后调用remove设备和驱动分离时调用id_table当前驱动支持设备的名字driver.通用驱动描述设备树属性设备信息是针对于特定平台若linux内核包含太多设备信息Linux内核移植性就会变差通过引入设备树将设备的信息从内核中独立出来不再以代码的形式存在于linux内核源码中compatible驱动通过of_match_table中的compatible与设备树匹配reg表示设备占用的寄存器地址与长度驱动通过platform_get_resource获取statusdisable不注册okay注册my-gpio gpx3 4 0表示引用节点并根据节点提示cells提供参数值和数量gpx2:标签名后跟节点名gpio-controller;说明为gpio控制器节点#xxx-cells 2;表示需要两个32bit值表示of_get_named_gpio_flags()从设备树获取gpio信息devm_gpio_request()申请占用gpio资源gpio_direction_output()设置gpio输出中断子系统devm_request_irq()注册中断号、触发方式和注册中断回调函数中断上下部上半部先执行不可打断下半部后执行可打断软中断中断上半部处理结束返回执行irq_exit时判断是否有软中断触发如果有则读取软中断并开启中断使能根据读取结果循环执行所有已触发软中断的处理函数然后关闭中断使能判断是否有新的软中断产生如果有则重复循环执行已触发软中断的处理函数最多重复max_restart之后若还有新的软中断产生就唤醒ksoftirqd线程交由其处理软中断。tasklettasklet_init()注册tasklet回调函数tasklet_schedule()调用软中断struct tasklet_struct.state表示状态在tasklet_schedule调用后会改变其值表示被调用在该软中断执行前如果再次tasklet_schedule并不会重复执行。workqueue把工作推后交由内核线程处理INIT_WORK注册回调函数schedule_work放入工作队列链表并唤醒线程如果一个工作没有执行后续重复提交的工作就不会执行input子系统分为设备驱动层、核心层、事件层linux内核注册了多个handler驱动模块并通过input_handler_list链表维护devm_input_allocate_device申请input_dev资源并在remove时自动释放资源input_register_device注册input_dev设备并创建一个input设备节点给应用层访问input_report_key向核心层上报按键值存入事件缓存input_sync发送同步事件表示应用层可读取一组输入事件注册后会核心层会遍历所有handler查找支持对应事件类型然后创建一个handle将input_dev和input_handler连接起来然后讲handle放入handler和dev的h_list当再次产生事件后通过h_list的handle查找input_handler。