完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 qq448309212947 于 2015-12-1 08:38 编辑
输入子系统 1.框架:drivers/input/input.c (1)注册驱动: static int __init input_init(void) { class_register(&input_class); register_chrdev(INPUT_MAJOR, "input", &input_fops); 申请字符驱动:INPUT_MAJOR = 13 } static const struct file_operations input_fops = { 定义file_operations 结构体 .owner = THIS_MODULE, .open = input_open_file, 定义open函数 }; static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler; handler = input_table[iminor(inode) >> 5]; 根据所打开驱动次设备号找到新的驱动信息 new_fops = fops_get(handler->fops); 获取新驱动的fop信息,即file_operations 结构体 file->f_op = new_fops; 将当前驱动的fop更新 new_fops->open(inode, file); 调用新驱动的open函数 } (2)注册handler与dev: int input_unregister_handler(struct input_handle *handle) 注册输入处理函数 { input_table[handler->minor >> 5] = handler; 构造input_table数组 list_add_tail(&handler->node, &input_handler_list); 放入input_handler_list中 list_for_each_entry(dev, &input_dev_list, node) 对每一个input_dev调用input_attach_handler函数 input_attach_handler(dev, handler); 根据input_handler的id_table判断是否支持这个input_dev } 无论先注册处理函数还是驱动,都可以调用input_attach_handler函数。 int input_register_device(struct input_dev *dev) 注册输入设备 { list_add_tail(&dev->node, &input_dev_list); 将设备节点加入input_dev_list中 list_for_each_entry(handler, &input_handler_list, node) 对每一个input_handler,都调用input_attach_handler函数 input_attach_handler(dev, handler); 根据input_handler的id_table判断是否支持这个input_dev } static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { id = input_match_device(handler, dev); 将 handler 和 dev 进行比较,查看是否匹配 handler->connect(handler, dev, id); 如果匹配,则调用connect函数 } 注册input_dev和input_handler时,会两两比较input_dev和input_handler, 根据input_handler的id_table判断这个input_handler能否支持这个input_dev; 如果支持,则调用input_attach_handler中的connect函数建立“连接” (3)连接dev与handler(通过handle) static int evdev_connect (struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 分配一个 input_handle (struct input_handle handle)PS:与input_handler不同 evdev->handle.dev = input_get_device(dev); 指向input_dev结构体 evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; 指向input_handler结构体 evdev->handle.private = evdev; input_register_handle(&evdev->handle); 注册handle } int input_register_handle(struct input_handle *handle) { struct input_handler *handler = handle->handler; list_add_tail_rcu(&handle->d_node, &dev->h_list); 将加入input_dev列表中 list_add_tail_rcu(&handle->h_node, &handler->h_list); 将加入input_handler列表中 } (4)app:read的使用(举例:evdev_read) static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { 链表头等于链表尾,即环形链表为空 if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; 如果为非阻塞,则返回错误值 retval = wait_event_interruptible(evdev->wait, client->head = client->tail || evdev->exist); 如果为阻塞,则休眠等待 } static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { wake_up_interruptible(&evdev->wait); 唤醒驱动程序 } 在设备的中断服务程序中,确定事件,然后调用相应的input_handle中的event处理函数 input_event(input, type, button->code, !!state); 上报事件 input_sync(input); void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { input_handle_event(dev, type, code, value); 调用event处理函数 } (5)如何编写符合输入子系统的驱动程序 1.分配input_dev结构体 2.设置 3.注册 4.硬件相关:中断服务函数上报事件 2.驱动程序 static struct input_dev *dev; 定义input_dev 结构体 /*********************************** * struct input_dev { * const char *name; 名称 * const char *phys; * const char *uniq; * struct input_id id; id号 * * unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; 表示能产生哪些事件 * /******************** * /*Event types*/ 事件类型 * #define EV_SYN 0x00 同步类 * #define EV_KEY 0x01 按键类 * #define EV_REL 0x02 相对位移类:鼠标 * #define EV_ABS 0x03 绝对位移:触摸屏 * #define EV_REP 0x14 重复类 * ********************/ * unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; 表示能产生哪些按键 * unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; 表示能产生哪些相对位移事件:x、y坐标,滚轮 * unsigned long ab***it[BITS_TO_LONGS(ABS_CNT)]; 表示能产生哪些绝对位移事件:x、y方向 * } ************************************/ static void button_timer_function(unsigned long data) 定时器处理函数——上报事件 { struct desc* pindesc = (struct desc*)irq_pd; unsigned int pinval; if(!pindesc) return; pinval = gpio_get_value(pindesc->pin); if(pinval) { /*松开: 最后一个参数: 0-松开,1-按下*/ input_event(dev, EV_KEY, pindesc -> value, 0); 上报事件(驱动,类型,键值,事件) input_sync(dev); 上报同步——表示事件已经上报完成 } else { /*按下*/ input_event(dev, EV_KEY, pindesc -> value, 1); input_sync(dev); } } static int __init input_init(void) { /*1. 分配一个input_dev结构体*/ dev = input_allocate_device(); if(!dev){ printk("unable to allocate the input devn"); return -ENOMEM; } /*2. 设置*/ /*2.1 能产生哪类事件*/ set_bit(EV_KEY, dev->evbit); 能产生按键类事件 set_bit(EV_REP, dev->evbit); 能产生重复类事件 /*2.2 能产生这类事件里的哪些事件*/ __set_bit(KEY_1, dev->keybit); 能产生按键1事件 __set_bit(KEY_2, dev->keybit); __set_bit(KEY_3, dev->keybit); __set_bit(KEY_4, dev->keybit); __set_bit(KEY_5, dev->keybit); /*3. 注册*/ err = input_register_device(dev); 将设备放入inpur_dev的链表中 if(err){ input_free_device(dev); return -1; } /*4. 硬件相关的操作*/ init_timer(&button_timer); 初始化定时器 button_timer.function = button_timer_function; 定义定时器中断处理函数 add_timer(&button_timer); 添加定时器 for(i = 0; i < 5; i ++) { gpio_free(my_desc[0].pin); 释放IO } for(i = 0; i < 5; i ++) 注册中断 { iRet=gpio_request(my_desc.pin, my_desc.name); 请求IO if (iRet != 0) { printk("request gpio failed n"); return -EBUSY; } gpio_direction_input(my_desc.pin); 设置IO为输入 irq_no = gpio_to_irq(my_desc.pin); 获取中断号 set_irq_type(irq_no, IRQF_TRIGGER_FALLING); 设置中断类型 iRet = request_irq(irq_no, buttons_irq, IRQF_DISABLED, my_desc.name, &my_desc); if (iRet != 0){ printk("request irq failed!! ret: %d irq:%d gpio:%d n", iRet, irq_no, my_desc.pin); return -EBUSY; } } return 0; } 3.驱动测试方法 (1)hexdump /dev/input/event1 (open(/dev/event1), read()) 打开设备,执行读函数,以十六进制返回数据 struct input_event { read返回值为input_event 结构体中的数据 struct timeval time; 时间:秒/微秒 __u16 type; 类型 __u16 code; 代码 __s32 value; 值 }; 秒 微秒 类 code value 0000000 004f 0000 06f3 0007 0001 0026 0001 0000 按键类 0000010 004f 0000 0712 0007 0000 0000 0000 0000 同步类 (2) cat /dev/tty1 如果没有启动QT,使用此方法按键可以得到结果 tty1:主设备号为4,通过tty_io.c访问keyboard.c keyboard.c: 4.应用程序 struct input_event ev_key; buttons_fd = open("/dev/input/event1", O_RDWR); 打开设备 if (buttons_fd < 0) { perror("open device buttons!!"); exit(1); } for (;;) { count = read(buttons_fd, &ev_key, sizeof(struct input_event)); 读取信息 for(i = 0; i < (int)count/sizeof(struct input_event); i++) if(EV_KEY == ev_key.type) printf("type:%d,code:%d,value:%dn", ev_key.type, ev_key.code, ev_key.value); if(EV_SYN == ev_key.type) printf("syn eventnn"); } close(buttons_fd); |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
1906个成员聚集在这个小组
加入小组我的项目我做主,使用GN+Ninja来完成构建系统(VSCode开发RT106X)
36358 浏览 0 评论
NXP IMX8应用处理器快速入门必备:技巧、使用、设计指南
4395 浏览 0 评论
6050 浏览 1 评论
6763 浏览 0 评论
NXP i.MX6UL开发板(linux系统烧录+规格+硬件+模块移植)使用手册
4212 浏览 0 评论
619浏览 2评论
求助,S32G上Core M启动后如何让Core A在Flash指定位置加载uboot?
614浏览 2评论
ESP32-WROVER-IE + LAN8720以太网,GPIO0电压只有1.6v,无法正常进入spi flash boot模式如何解决?
605浏览 2评论
求分享适用于PN7160 Android的NFC工厂测试应用程序
694浏览 2评论
796浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 01:54 , Processed in 1.125882 second(s), Total 71, Slave 53 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号