Linux input子系统学习总结(三)----Input设备驱动
Input 设备驱动 ---操作硬件获取硬件寄存器中设备输入的数据,并把数据交给核心层;
一 、设备驱动的注册步骤:
1、分配一个struct input_dev :
struct input_dev *input_dev;
2、 初始化 input_dev 这个结构体 :
3、 注册这个input_dev 设备:
Input_register_device(dev);
4、 在input设备发生输入操作时,提交所发生的事件及键值、坐标等信息:
Input_report_abs()///报告X,y的坐标值
Input_report_key()///报告触摸屏的状态,是否被按下
Input_sync () ///表示提交一个完整的报告,这样linux内核就知道哪几个数据组合起来代表一个事件
二、Linux 中输入事件的类型:
EV_SYN 0X00 同步事件
EV_KEY 0X01 按键事件
EV_REL 0X02 相对坐标
EV_ABS 0X03 绝对坐标
....
三、关键程序片段(以USB鼠标为例:drivers\hid\usbhid\usbmouse.c)
1、 module_usb_driver(usb_mouse_driver);///系统启动时注册usb_mouse_driver (在usb架构中实现)
2、 usb_mouse_probe (设备初始化,usbmouse 属于usb设备,匹配成功后注册input设备)
3、 input_register_device(mouse->dev); 注册设备驱动
4、 input_attach_handler(dev, handler);///遍历所有的input_handler,并与 dev 进行匹配
usbmouse除了可以和evdev_handler 匹配成功,还和mousedev_handler 匹配成功,所以会分别调用evdev_handler的connect 函数创建event 节点和 mousedev_handler 的connect创建mouse节点
5、 input_match_device(handler, dev);///---->handler 和 device 进行真正的匹配(通过id_table 进行匹配)
6、 handler->connect(handler, dev, id);///匹配成功调用handler的connect 函数
7、 evdev_connect()///将创建 event(0、1、2…)节点
8、 mousedev_connect()///将创建 mouse(0、1、2…)节点
9、 mousedev_create()
10、cdev_init(&mousedev->cdev, &mousedev_fops);
11、usb_mouse_irq()///最终的数据在中断里面获取到,并保存到mouse –>data 里面
12、input_report_key\ input_report_rel\ input_sync ///报告按键信息
13、input_event
14、input_handle_event(dev, type, code, value)
15、input_pass_values(dev, dev->vals, dev->num_vals);
16 、input_to_handler(handle, vals, count);
17、handler->event(handle, v->type, v->code, v->value);
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,///当有新的usbmouse加入时,系统会进行匹配,匹配成功后则调用probe函数,完成设备的初始化
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
}; module_usb_driver(usb_mouse_driver);///系统启动时注册usb_mouse_driver
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM; interface = intf->cur_altsetting; if (interface->desc.bNumEndpoints != )
return -ENODEV; endpoint = &interface->endpoint[].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1; mouse->data = usb_alloc_coherent(dev, , GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1; mouse->irq = usb_alloc_urb(, GFP_KERNEL);
if (!mouse->irq)
goto fail2; mouse->usbdev = dev;
mouse->dev = input_dev; if (dev->manufacturer)
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); if (dev->product) {
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
} if (!strlen(mouse->name))
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev; ////evbit 关于设备支持事件类型的 bitmap
input_dev->evbit[] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); ///BIT_MASK 找到参数值所在的 bit位
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | ///鼠标支持左键、右键、中键三个按键
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); ///REL_X REL_Y 表示鼠标的位置信息
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | ///在已有按键的基础上加上一个边键和一个而外的键
BIT_MASK(BTN_EXTRA);
input_dev->relbit[] |= BIT_MASK(REL_WHEEL);///给相对事件加上滚轮的事件 input_set_drvdata(input_dev, mouse); input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close; usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > ? : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; error = input_register_device(mouse->dev);
if (error)
goto fail3; usb_set_intfdata(intf, mouse);
return ; fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, , mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error; if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(struct input_devres), GFP_KERNEL);
if (!devres)
return -ENOMEM; devres->input = dev;
} /* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev); packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size; dev->max_vals = dev->hint_events_per_packet + ;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
} /*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = ;
dev->rep[REP_PERIOD] = ;
} if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; error = device_add(&dev->dev);
if (error)
goto err_free_vals; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path); error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del; list_add_tail(&dev->node, &input_dev_list); list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);////遍历所有的input_handler,并与 dev 进行匹配 input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); if (dev->devres_managed) {
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return ; err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error; id = input_match_device(handler, dev);////---->handler 和 device 进行真正的匹配
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id);///如果返回id不为空就执行handler 的 connect ---> 调用 evdev.c 的 connect 函数
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error); return error;
}
查看设备在系统中对应的节点信息(包括设备名称ID对应的handler等),例如usb鼠标对应的节点信息如下:
鼠标对应的事件驱动为mouse1 和event3,因为它和两个事件驱动匹配成功;
Event 节点里面存放的数据都是没有经过处理的原始数据流,通过命令$cat event3可以看到鼠标输入的数据,但是mouse 节点里面的数据是经过处理的有结构的数据,直接对应鼠标点击或滑动的具体坐标,应用程序可以直接读取使用,可以通过命令$cat mouse1可以看到鼠标输入的数据
Linux input子系统学习总结(三)----Input设备驱动的更多相关文章
- input子系统学习笔记六 按键驱动实例分析下【转】
转自:http://blog.chinaunix.net/uid-20776117-id-3212095.html 本文接着input子系统学习笔记五 按键驱动实例分析上接续分析这个按键驱动实例! i ...
- Linux时间子系统之(三):用户空间接口函数
专题文档汇总目录 Notes:用户空间时间相关接口函数: 类型 API 精度 说明 时间 time stime time_t 精度为秒级 逐渐要被淘汰.需要定义__ARCH_WANT_SYS_TIME ...
- Linux内核分析(五)----字符设备驱动实现
原文:Linux内核分析(五)----字符设备驱动实现 Linux内核分析(五) 昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷, ...
- Linux input子系统学习总结(一)---- 三个重要的结构体
一 . 总体架构 图 上层是图形界面和应用程序,通过监听设备节点,获取用户相应的输入事件,根据输入事件来做出相应的反应:eventX (X从0开始)表示 按键事件,mice 表示鼠标事件 Input ...
- Linux input子系统学习总结(二)----Input事件驱动
Input 事件驱动: (主要文件 :drivers/input/evdev.c . drivers/input/input.h)基于kernel 4.0 一. 关键函数调用顺序: 1.inp ...
- linux输入子系统(6)-input子系统介绍及结构图
注:本系列转自: http://www.ourunix.org/post/290.html input子系统介绍 输入设备(如按键,键盘,触摸屏,鼠标,蜂鸣器等)是典型的字符设备,其一 ...
- linux 输入子系统(4)---- input子系统的初始化
Input子系统的初始化函数为input_init(),如下: static int __init input_init(void) { int err; input_init_abs_bypass( ...
- Html5学习进阶三 Input 类型
HTML5 新的 Input 类型 HTML5 拥有多个新的表单输入类型.这些新特性提供了更好的输入控制和验证. 本章全面介绍这些新的输入类型: email url number range Date ...
- linux内核I2C子系统学习(三)
写设备驱动: 四部曲: 构建i2c_driver 注册i2c_driver 构建i2c_client ( 第一种方法:注册字符设备驱动.第二种方法:通过板文件的i2c_board_info填充,然后注 ...
随机推荐
- C语言中变量名及函数名的命名规则与驼峰命名法
一.C语言变量名的命名规则:(可以字母,数字,下划线混合使用) 1. 只能以字母或下划线开始:2. 不能以数字开始:3. 一般小写:4. 关键字不允许用(eg:int float=2//error ...
- c++11 闭包的实现
c++11 闭包的实现 什么是闭包 闭包有很多种定义,一种说法是,闭包是带有上下文的函数.说白了,就是有状态的函数.更直接一些,不就是个类吗?换了个名字而已. 一个函数,带上了一个状态,就变成了闭包了 ...
- BZOJ2436 [Noi2011]Noi嘉年华 【dp】
题目链接 BZOJ2436 题解 看这\(O(n^3)\)的数据范围,可以想到区间\(dp\) 发现同一个会场的活动可以重叠,所以暴力求出\(num[l][r]\)表示离散化后\([l,r]\)的完整 ...
- bzoj 4519: [Cqoi2016]不同的最小割 最小割树
怎么求一张无向图中任意两点之间的最小割? http://fanhq666.blog.163.com/blog/static/8194342620113495335724/ 一张无向图不同的最小割最多有 ...
- 【agc019C】Fountain Walk
Portal --> agc019C Description 有一个\(10^8*10^8\)的网格图,一格距离为\(100\),第\(x\)条竖线和第\(y\)条横线的交点记为\((x,y)\ ...
- mac、linux 查看端口占用程序
lsof -i:80 列出占用 80 端口的程序 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mysqld 672 ruby 42u IPv4 ...
- 时间序列HW
https://www.cnblogs.com/sylvanas2012/p/4328861.html写得特别好,推荐阅读 Holt-Winters: 三阶指数平滑 Holt-Winters的思想是 ...
- C\C++中 fopen中文件打开方式的区别:
在C语言中,大家常用到fopen打开文件,准备进行写操作,再用fwrite把数据写入文件,最后用fclose关闭文件. 如以下C代码: #include <stdio.h> char ...
- clock()、time()、clock_gettime()和gettimeofday()函数的用法和区别
1. clock_gettime( ) 提供了纳秒的精确度 int clock_gettime(clockid_t clk_id, struct timespect *tp); clockid_t c ...
- vue 打印页面部分区域
1. vue项目打印页面部分区域 2. 原生js实现页面局部打印功能 3. vue项目中将table组件导出Excel表格以及打印页面内容