本文章中与input子系统相关的结构体可参考input子系统结构体解析
input函数路径:drivers/input/input.c
input子系统初始化
// linux-3.18.24.x/drivers/input/input.c
static int __init input_init(void)
{
int err;
err = class_register(&input_class); /* 注册类,放在sys/class下 */
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init(); /* 在proc目录下建立相关目录 ---------下面详解 -----------*/
if (err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input"); /* 注册字符设备编号,INPUT_MAJOR永远是13 */
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
subsys_initcall(input_init);
1、input_init用subsys_initcall修饰,因此启动会直接调用执行
2、所有的输入设备的主设备号都是13,input-core通过次设备号来进行输入设备进行分类
input_proc_init :在proc目录下建立相关目录
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
/* 在/proc/bus目录下创建input */
proc_bus_input_dir = proc_mkdir("bus/input", NULL);
if (!proc_bus_input_dir)
return -ENOMEM;
/* 在/proc/bus/input目录下创建devices文件 */
entry = proc_create("devices", 0, proc_bus_input_dir,
&input_devices_fileops);
if (!entry)
goto fail1;
/* 在/proc/bus/input目录下创建handlers文件 */
entry = proc_create("handlers", 0, proc_bus_input_dir,
&input_handlers_fileops);
if (!entry)
goto fail2;
return 0;
fail2: remove_proc_entry("devices", proc_bus_input_dir);
fail1: remove_proc_entry("bus/input", NULL);
return -ENOMEM;
}
初始化input设备内存
input_allocate_device:为新设备提供input_dev结构体内存空间
struct input_dev *input_allocate_device(void)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
init_timer(&dev->timer);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
dev_set_name(&dev->dev, "input%lu",
(unsigned long) atomic_inc_return(&input_no) - 1);
__module_get(THIS_MODULE);
}
return dev;
}
EXPORT_SYMBOL(input_allocate_device);
input设备注册
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;
}
/* 每个input设备都会产生 EV_SYN/SYN_REPORT 事件。将evbit的第0位置1,表示支持所有的事件 */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED 不能传到用户层 */
__clear_bit(KEY_RESERVED, dev->keybit);
/* 确保dev->evbit没有涉及到的位为0 */
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 + 2;
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}
/* 如果延时周期是程序预先设定的,那么是由驱动自动处理,主要是为了处理重复按键 */
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
/* 检查getkeycode函数有没有被定义。 没有定义使用默认的函数input_default_getkeycode */
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
/* 检查setkeycode函数有没有被定义。 没有定义使用默认的函数input_default_setkeycode */
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
/* 任何一个设备的注册都会经过这个函数,设备驱动模型中的核心函数 */
error = device_add(&dev->dev);
if (error)
goto err_free_vals;
/* 生成并返回设备的路径,后必需使用kfree()来释放结果 */
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;
/* 将input_dev添加到input_dev_list链表 */
list_add_tail(&dev->node, &input_dev_list);
/* 遍历input_handler链表上,对链表中的每一个input_handler执行input_attach_handler函数*/
list_for_each_entry(handler, &input_handler_list, node) // -------- 下面详解 -------
input_attach_handler(dev, handler);
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 0;
err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);
1、设置input_devce结构体中的事件标志位
2、设置input_devce结构体中有关处理重复按键的成员
3、设置input_devce结构体中 获取和设置按键的函数
4、添加到设备驱动模型中
5、将input_dev添加到input_dev_list链表
6、遍历input_handler链表上,对链表中的每一个input_handler执行input_attach_handler函数
两条链表都是设置为全局变量
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
list_for_each_entry是一个宏:遍历input_handler_list上的所有handler结构体
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
// list_for_each_entry展开:
for (handler = list_first_entry(input_handler_list, &handler, node);
&handler->node != (input_handler_list);
&handler = list_next_entry(&handler, node))
input_attach_handler(dev, handler); // -------- 下面详解 -------
input_attach_handler:此函数用于匹配input_dev和input_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
/* input 匹配设备 */
id = input_match_device(handler, dev); // -------- 下面详解 -------
if (!id)
return -ENODEV;
/* 匹配成功,调用handler->connect函数 这个函数在事件处理器中定义 */
error = handler->connect(handler, dev, id);
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;
}
input_dev和input_handler匹配函数实现
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
/* id_table 表示驱动支持的设备列表 */
for (id = handler->id_table; id->flags || id->driver_info; id++) {
// 匹配总线类型
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype) // id_table[i]中支持的总线类型 与设备的标识中的总线类型比较
continue;
// 匹配制造商id
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
//匹配产品id
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
//匹配版本号
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
continue;
if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
continue;
if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
continue;
if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
continue;
if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
continue;
if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
continue;
if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
continue;
if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
continue;
if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
continue;
// 调用匹配函数
if (!handler->match || handler->match(handler, dev))
return id;
}
return NULL;
}
int __bitmap_subset(const unsigned long bitmap1,const unsigned longbitmap2, int bits)
功能: 判断bitmap2是否是bitmap1的子集,是返回1,不是返回0
id_table[i]中支持相关的匹配项就匹配:成功就跳出;不成功匹配失败执行continue循环检测下一个
设备向input子系统注册实例
驱动里很多外设都向input子系统注册,这里介绍usb鼠标设备注册input子系统。结合上面介绍的函数了解在具体设备驱动中的使用。
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
// USB 接口描述符被当成参数传入(USB 的一个接口表示一个功能)
// 获取USB 设备描述
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;
// 判断接口是否合法,根据HID规范,鼠标只有一个端点(且不包含端点0)
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
// 获取端点0的描述符
endpoint = &interface->endpoint[0].desc;
// 判断端点类型是否合法,HID 规范,鼠标唯一的端点为中断端点,因为鼠标是中断控制
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
// 创建中断管道(in),鼠标属于中断控制
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/* 返回该端点能够传输的最大的包长度,鼠标的返回的最大数据包为4个字节。*/
//初始化URB的时候会用到这个长度,缓冲区的长度要依照maxp来决定,最大不能超过8
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
// 为mouse 申请内存
//mouse结构的主要作用是赋值给usb_interface中的一个属性
//以便于触发其它函数的时候通过usb_interface中的这个属性就可以知道相关信息
//usb_interface中的这个属性是专门为了储存用户需要的数据的
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
// 创建input设备
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
// 为urb 传输申请内存,data 指向该地址空间,初始化urb 缓存区,第四个参数为dma相关
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1;
// 申请urb
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
goto fail2;
// 为mouse 的usbdev,dev 赋值
mouse->usbdev = dev;
mouse->dev = input_dev;
// 为mouse 那么赋值制造商名称
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;
// 从dev 设备中获取总线类型,设备id,厂商id,版本号,设置父设备
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
// 设置输入设备所支持的事件信息
// 支持相对坐标和事件
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
// 记录支持的按键值
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
// 支持的相对坐标为鼠标移动坐标和滑轮坐标
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
// 将mouse 传入input_dev,方便通过input_dev 获取全部的mouse 信息
input_set_drvdata(input_dev, mouse);
// 设置输入设备的open,close函数
input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
// 填充urb 模块,mouse 作为上下文被设置下去,另外usb_mouse_irq函数为回调
// 当usb mouse 有事件产生时,回调被调用
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
// mouse->irq 就是urb,如下设置DMA传输相关,当flag为URB_NO_TRANSFER_DMA_MAP时
// 表示优先使用transfer_dma,而不是transfer buffer
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
// 注册input 设备
error = input_register_device(mouse->dev);
if (error)
goto fail3;
// 设置mouse 到 usb interface 中
usb_set_intfdata(intf, mouse);
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
usb鼠标检测函数
- 获取接口; // 判断端点是否为中断模式
- 申请一个input 设备并填充; ---- input_allocate_device
- 设置事件和事件值;
- 创建管道,设置大小,再申请缓存区;
- 申请一个urb 用于与usb 设备通信;
- 注册input 设备; ----- input_register_device
- 设置接口数据;
设置事件和事件值有三种方式:
/* 第一种方法 /
__set_bit(EV_KEY, inputdev->evbit); / 设置产生按键事件 /
__set_bit(EV_REP, inputdev->evbit); / 重复事件 /
__set_bit(KEY_0, inputdev->keybit); / 设置产生哪些按键值 /
/ 第二种方法 /
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=BIT_MASK(KEY_0);
/ 第三种方法 */
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);