4月「掘金·日新计划」第21天
2.3、平台总线(驱动模型)
- 平台总线的设计是基于设备模型的
- 实现device(中断/地址)和driver(操作逻辑)分离
- 实现一个driver代码能够驱动多个平台相似的模块
2.3.1、平台总线工作流程
- bus中注册platform
- 提供platform_device和platform_driver结构体
- platform的match函数匹配设备和驱动 匹配成功,driver的probe函数初始化驱动和安装
- platform的注册是通过下面的注册安装函数完成
- platdata设备注册时有关数据,如gpio等 这些数据在match后由设备提供给驱动 驱动是没有数据的,算法数据分离 保持驱动的独立和适应性
2.3.2、平台总线三要素
设备,驱动分离 算法,数据分离 为了设备驱动相遇probe执行
-
bus总线对象
-
不需要自己创建,开机的时候自动创建。结构体对象
struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; -
linux内核自动匹配设备驱动,匹配规则
- 优先匹配pdriver中的id_table,里面包含了支持不同的平台的名字
- 直接匹配driver中名字和device中名字
-
-
device设备对象
-
为驱动提供硬件信息,描述性内容。结构体对象
struct platform_device { const char *name; //用于做匹配 int id; // 一般都是直接给-1 struct device dev; // 继承了device父类 u32 num_resources; // 资源的个数 struct resource *resource; // 资源:包括了一个设备的地址和中断 } -
注册和注销
int platform_device_register(struct platform_device * pdev); void platform_device_unregister(struct platform_device * pdev);
-
-
driver驱动对象
-
实现功能接口,动作性描述。结构体对象
struct platform_driver { int (*probe)(struct platform_device *); //匹配成功之后被调用的函数 int (*remove)(struct platform_device *);//device移除的时候调用的函数 struct device_driver driver; //继承了driver父类 const struct platform_device_id *id_table; //如果driver支持多个平台,在列表中写出来 } -
注册和注销
int platform_driver_register(struct platform_driver *drv); void platform_driver_unregister(struct platform_driver *drv)
-
2.3.3、使用(input子系统)
2.3.3.1、概念
-
platform 驱动框架分为总线、设备和驱动
- 总线在Linux内核提供,不需要我们进行考虑,所以只需考虑设备和驱动。
- 因为目前的Linux内核都支持设备树了,所以platform_device设备信息都通过设备树进行描述了,因此只需要在代码中编写platform_driver驱动代码(我实现的还是老方法,设备驱动编写)
-
应用层 + input event + input core + 硬件驱动
- 当按下鼠标,硬件中断执行判断是什么设备
- 然后上报core(负责封装为input_event结构体)
- 应用层解析事件结构体处理
-
结构体struct input_event,事件
-
time时间
-
type驱动类型,例如鼠标
- type EV_SYN 同步类型,一般3个数据包,最后一个就是0,0,0 EV_KEY 按键 EV_REL 相对的,鼠标移动 EV_ABS 绝对的,触摸屏 EV_MSC 键盘
-
code鼠标左键
- 哪一个按键
-
value按下了
- 非0按下
-
2.3.3.2、代码流程分析
-
应用层操作驱动有2条路,/dev设备文件,和属性文件/sys,input使用的是/dev
-
核心层
- input.c 主要创建类,注册主设备驱动(提供了第一种驱动的结构体file_operations)
- class_register 创建类
- input_proc_init /proc/bus/input
- register_chrdev 注册设备驱动,主设备号
-
设备驱动层
- drivers/input/xxx
- input_allocate_device,申请设备内存
- input_set_capability,输入设备的能力set_bit
- input_register_device,注册
-
事件驱动层
-
evdev.c
input_report_key,上报应用层
-
input_register_handler
-
input_register_handle
-
设备和handler匹配 成功注册驱动,生成文件/dev/input/xxx
-
2.3.3.3、使用input
-
应用层
- 打开文件
- 读取结构体变量,解析结构体变量
-
驱动
-
定时器轮询
-
platform和input platform设备驱动注册,相遇调用probe
-
probe
-
申请,设置上拉,输入模式gpio
-
input_dev结构体指针 动态分配内存input_allocate_device 填充结构体set_bit 注册设备input_register_device
-
struct timer_list结构体 内核定时器初始化init_time 设置绑定函数 .function 时间 .expires 启动定时器 add_time del_timer删除定时器
- 定时器处理函数time_handler 读取gpio值gpio_get_value 判断值和上次是否一样if 上报应用层input_report_key 更新定时器mod_timer
-
-
-
验证使用
-
/dev/input/下多出event0 /sys/bus/platform/devices出现设备fire /sys/bus/platform/drivers出现驱动fire
-
数据+心跳包
-
驱动
#include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/gpio.h> #include <linux/platform_device.h> #define GPIO_FIRE 17 //io口号,可以使用命令gpio read static struct input_dev *input; //指针,后面动态分配内存 static struct timer_list timer; //定时器结构体 static int history; //记录上次io值 //定时器处理函数 static void fire_timer_handler(unsigned long data) { int flag; flag = gpio_get_value(GPIO_FIRE); if(flag != history){ //和上次值比较 if(flag){ input_report_key(input, KEY_OK, 1); //上报应用层 }else{ input_report_key(input, KEY_OK, 0); } history = flag; } input_sync(input); //同步包 mod_timer(&timer, jiffies + HZ/100); //更新定时器 } //匹配成功注册设备 static int fire_button_probe(struct platform_device *pdev) { int error; error = gpio_request(GPIO_FIRE, "GPIO_0_FIRE"); //申请io if(error){ printk("fire.c: request gpio GPIO_0 fail"); } gpio_direction_input(GPIO_FIRE); //输入模式 history = gpio_get_value(GPIO_FIRE); input = input_allocate_device(); //输入设备结构体,实例化 if (!input) { printk(KERN_ERR "fire.c: Not enough memory\n"); error = -ENOMEM; goto err_free_mem; } //填充结构体 set_bit(EV_KEY, input->evbit); //按键类型 set_bit(KEY_OK, input->keybit); //哪一个按键 //注册 error = input_register_device(input); if (error) { printk(KERN_ERR "fire.c: Failed to register device\n"); goto err_free_device; } //定时器 init_timer(&timer); timer.function = fire_timer_handler; //处理函数 timer.expires = jiffies + (HZ/100); //定时时间 add_timer(&timer); 启动 return 0; //倒影式处理错误 err_free_device: input_free_device(input); err_free_mem: gpio_free(GPIO_FIRE); return error; } //注销 static int fire_button_remove(struct platform_device *dev) { del_timer(&timer); input_unregister_device(input); gpio_free(GPIO_FIRE); return 0; } //设备 static struct platform_device fire_device_button = { .name = "fire", .id = -1, }; //驱动 static struct platform_driver fire_button_device_driver = { .probe = fire_button_probe, .remove = fire_button_remove, .driver = { .name = "fire", .owner = THIS_MODULE, }, }; //平台总线 static int __init button_init(void) { platform_device_register(&fire_device_button); return platform_driver_register(&fire_button_device_driver); } static void __exit button_exit(void) { platform_driver_unregister(&fire_button_device_driver); platform_device_unregister(&fire_device_button); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xiaowei"); MODULE_DESCRIPTION("fire"); -
应用层
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <string.h> #define DEVICE_KEY "/dev/input/event1" #define DEVICE_MOUSE "/dev/input/event3" #define X210_KEY "/dev/input/event1" int main(void) { int fd = -1, ret = -1; struct input_event ev; // 打开驱动文件 fd = open(X210_KEY, O_RDONLY); if (fd < 0) { perror("open"); return -1; } while (1) { // 初始化事件结构体 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror("read"); close(fd); return -1; } // 输出解析 printf("-------------------------\n"); printf("type: %hd\n", ev.type); //按键类型 printf("code: %hd\n", ev.code); //哪一个按键 printf("value: %d\n", ev.value); //值 printf("\n"); } // µÚ4²½£º¹Ø±ÕÉ豸 close(fd); return 0; }
-