linux input (1) - Framework

  • 了解linux input framework

1.input framework

  输入子系统由 Input Core ,Device drivers和Event Handler 三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

  • 输入子系统设备驱动层,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

  • 核心层,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。

  • 事件处理层,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。
    在这里插入图片描述

  • /dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。

  • 事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。

  • 输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

1.1. Input Core

  The core of the input subsystem is the input module, which must be loaded before any other of the input modules - it serves as a way of communication between two groups of modules:

  • Device drivers
    These modules talk to the hardware (for example via USB), and provide
    events (keystrokes, mouse movements) to the input module.

  • Event handlers
    These modules get events from input core and pass them where needed via various interfaces - keystrokes to the kernel, mouse movements via a simulated PS/2 interface to GPM and X, and so on.

1.2.Event handlers

  Event handlers distribute the events from the devices to userspace and in-kernel consumers, as needed.

2.input 事件处理框架
在这里插入图片描述
2.1.分配、注册、注销input设备

struct input_dev *input_allocate_device(void)
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)

2.2.设置input设备支持的事件类型、事件码、事件值的范围、input_id等信息

usb_to_input_id(dev, &input_dev->id);//设置bustype、vendo、product等
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件码
for (i = 0; i < 255; i++)
     set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件码

  include/linux/input.h定义支持的类型:

#	Event code	Specifies
0x00	EV_SYN	Separate/synchronize other events (e.g. SYN_REPORT/SYN_MT_REPORT), or report events lost (SYN_DROPPED)
0x01	EV_KEY	Key press (KEY_*) or touch (BTN_TOUCH)
0x02	EV_REL	Relative changes to a property. Changes relayed through REL_[XYZ] values.
0x03	EV_ABS	Absolute coordinates for an event. Values are usually ABS_[XYZ], or ABS_MT for multi-touch
0x04	EV_MSC	Miscellaneous codes
0x05	EV_SW	Binary switches. E.g. SW_JACK_PHYSICAL_INSERT for headphone insertion
0x11	EV_LED	Used for device LEDs, if any
0x12	EV_SND	Used for sound devices
0x14	EV_REP	Used for auto-repeating events
0x15	EV_FF	Used for force-feedback capable devices (e.g. joysticks). An EVIOCSFF ioctl may be used to upload force feedback effects
0x16	EV_PWR	Reserved for power events. Largely unused
0x17	EV_FF_STATUS	Used for force-feedback capable devices.

  一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如:EV_KEY事件,需要定义其支持哪些按键事件码。

2.3.如果需要,设置input设备的打开、关闭、写入数据时的处理方法

input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
input_dev->event = usb_kbd_event;

2.4.在发生输入事件时,向子系统报告事件

用于报告EV_KEY、EV_REL、EV_ABS等事件的函数有:
void input_report_key(struct input_dev *dev, unsigned int code, int value)
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)

3.Event Handler层分析

数据结构关系图
在这里插入图片描述
如上所示:

  对于handler和device,分别用链表input_handler_list和input_device_list进行维护,当handler或者device增加或减少的时候,分别往这两链表增加或删除节点,这两条都是全局链表。

36 static LIST_HEAD(input_dev_list);
37 static LIST_HEAD(input_handler_list);   

3.1.input_dev

     struct input_dev {
      ...
      unsigned long evbit[NBITS(EV_MAX)];  //表示支持哪类事件,常用有以下几种事件(可以多选)
       //EV_SYN      同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
       //EV_KEY       键盘事件
       //EV_REL       (relative)相对坐标事件,比如鼠标
       //EV_ABS       (absolute)绝对坐标事件,比如摇杆、触摸屏感应
       //EV_MSC      其他事件,功能
       //EV_LED       LED灯事件
       //EV_SND      (sound)声音事件
       //EV_REP       重复键盘按键事件
       //(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件)  

       //EV_FF         受力事件
       //EV_PWR      电源事件
       //EV_FF_STATUS  受力状态事件

       unsigned long keybit[NBITS(KEY_MAX)];   //存放支持的键盘按键值
       //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)、BTN_TOUCH(触摸屏的按键)

       unsigned long relbit[NBITS(REL_MAX)];    //存放支持的相对坐标值
       unsigned long absbit[NBITS(ABS_MAX)];   //存放支持的绝对坐标值,存放下面4个absxxx[]
       unsigned long mscbit[NBITS(MSC_MAX)];   //存放支持的其它事件,也就是功能
       unsigned long ledbit[NBITS(LED_MAX)];    //存放支持的各种状态LED
       unsigned long sndbit[NBITS(SND_MAX)];    //存放支持的各种声音
       unsigned long ffbit[NBITS(FF_MAX)];       //存放支持的受力设备
       unsigned long swbit[NBITS(SW_MAX)];     //存放支持的开关功能
       ...
    }

3.1.1.注册input_dev

  input_register_device :该函数将input_dev结构体注册到输入子系统核心中,input_dev结构体必须由input_allocate_device()函数来分配。

int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_handler *handler;
	const char *path;
	int error;

	/* 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);

	/*
	 * 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.
	 */
	init_timer(&dev->timer);
	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;
	}

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	dev_set_name(&dev->dev, "input%ld",
		     (unsigned long) atomic_inc_return(&input_no) - 1);

	error = device_add(&dev->dev);
	if (error)
		return error;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	printk(KERN_INFO "input: %s as %s\n",
		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error) {
		device_del(&dev->dev);
		return error;
	}

	list_add_tail(&dev->node, &input_dev_list);
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler); 

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	return 0;
}

  遍历input_handler_list链表,对每一个handler调用input_attach_handler函数,看是否匹配。如果匹配成功,则调用 handler->connect,连接 handler 和 input_dev。

   976 static const struct input_device_id *input_match_device(struct input_handler *handler,
   977                             struct input_dev *dev)
   978 {
   979     const struct input_device_id *id;                                                                 
   980 
   981     for (id = handler->id_table; id->flags || id->driver_info; id++) {
   982         if (input_match_device_id(dev, id) &&
   983             (!handler->match || handler->match(handler, dev))) {
   984             return id;
   985         }
   986     }
   987 
   988     return NULL;
   989 }
   
   991 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
   992 {
   993     const struct input_device_id *id;
   994     int error;
   995 
   996     id = input_match_device(handler, dev);
   997     if (!id)
   998         return -ENODEV;
   999      //匹配成功则调用 handler->connect,连接 handler 和 input_dev
  1000     error = handler->connect(handler, dev, id);
  1001     if (error && error != -ENODEV)
  1002         pr_err("failed to attach handler %s to device %s, error: %d\n",
  1003                handler->name, kobject_name(&dev->dev.kobj), error);                                   
  1004 
  1005     return error;
  1006 }

3.2.input_handler

285 struct input_handler {
  286 
  287     void *private;
  288 
  289     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  290     void (*events)(struct input_handle *handle,
  291                const struct input_value *vals, unsigned int count);
  292     bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  293     bool (*match)(struct input_handler *handler, struct input_dev *dev);
  294     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *
  295     void (*disconnect)(struct input_handle *handle);
  296     void (*start)(struct input_handle *handle);
  297                                                                                                        
  298     bool legacy_minors;
  299     int minor;
  300     const char *name;
  301 
  302     const struct input_device_id *id_table;                                                                                                      
  304     struct list_head    h_list;
  305     struct list_head    node;
  306 };

3.2.1.注册input_handler

int __must_check input_register_handler(struct input_handler *);                                       
void input_unregister_handler(struct input_handler *);

input_register_handler:

  2213 int input_register_handler(struct input_handler *handler)
  2214 {
  2215     struct input_dev *dev;
  2216     int error;
  2217 
  2218     error = mutex_lock_interruptible(&input_mutex);
  2219     if (error)
  2220         return error;
  2221 
  2222     INIT_LIST_HEAD(&handler->h_list);
  2223 
  2224     list_add_tail(&handler->node, &input_handler_list);
  2225 
  2226     list_for_each_entry(dev, &input_dev_list, node)
  2227         input_attach_handler(dev, handler);                                                           
  2228 
  2229     input_wakeup_procfs_readers();
  2230 
  2231     mutex_unlock(&input_mutex);
  2232     return 0;

  遍历input_dev_list链表,对每一个dev调用input_attach_handler函数,看是否匹配。如果匹配成功,则调用 handler->connect,连接 handler 和 input_dev。
在这里插入图片描述

3.3.以evdev.c中的evdev_handler(drivers/input/evdev.c)为例:

static struct input_handler evdev_handler = {
        .event = evdev_event, //向系统报告input事件,系统通过read方法读取
        .connect = evdev_connect, //和input_dev匹配后调用connect构建
        .disconnect = evdev_disconnect,
        .fops = &evdev_fops, //event设备文件的操作方法
        .minor = EVDEV_MINOR_BASE, //次设备号基准值
        .name = "evdev",
        .id_table = evdev_ids, //匹配规则
};

3.3.1.evdev_handler 注册

static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },    /* Matches all devices */
    { },            /* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
    .event        = evdev_event,
    .connect    = evdev_connect,
    .disconnect    = evdev_disconnect,
    .fops        = &evdev_fops,
    .minor        = EVDEV_MINOR_BASE,
    .name        = "evdev",
    .id_table    = evdev_ids,
};

static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

  匹配成功后,调用evdev_connect,创建input_handle结构体,该结构体代表一个成功配对的input_dev和input_handler。input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_device_list和 input_handler_list的h_list上了;同时,input_handle的成员.*dev,关联到input_dev结构,.*handler关联到input_handler结构 。从此,建立好了三者的铁三角关系,通过任何一方,都可以找到彼此。

  320 struct input_handle {
  321 
  322     void *private;
  323                                                                                                        
  324     int open;
  325     const char *name; 
  326 
  327     struct input_dev *dev;
  328     struct input_handler *handler;
  329 
  330     struct list_head    d_node;
  331     struct list_head    h_node;
  332 };  
    
  1402     evdev->handle.dev = input_get_device(dev);
  1403     evdev->handle.name = dev_name(&evdev->dev);
  1404     evdev->handle.handler = handler;
  1405     evdev->handle.private = evdev;
  1413     error = input_register_handle(&evdev->handle);   
  1416     
  1417     cdev_init(&evdev->cdev, &evdev_fops);
  1419     error = cdev_device_add(&evdev->cdev, &evdev->dev);

input_register_handle:

/**
 * input_register_handle - register a new input handle
 * @handle: handle to register
 *
 * This function puts a new input handle onto device's
 * and handler's lists so that events can flow through
 * it once it is opened using input_open_device().
 *
 * This function is supposed to be called from handler's
 * connect() method.
 */
int input_register_handle(struct input_handle *handle)
{
    struct input_handler *handler = handle->handler;
    struct input_dev *dev = handle->dev;
    int error;

    /*
     * We take dev->mutex here to prevent race with
     * input_release_device().
     */
    error = mutex_lock_interruptible(&dev->mutex);
    if (error)
        return error;

    /*
     * Filters go to the head of the list, normal handlers
     * to the tail.
     */
    if (handler->filter)
        list_add_rcu(&handle->d_node, &dev->h_list);
    else
        list_add_tail_rcu(&handle->d_node, &dev->h_list);

    mutex_unlock(&dev->mutex);

    /*
     * Since we are supposed to be called from ->connect()
     * which is mutually exclusive with ->disconnect()
     * we can't be racing with input_unregister_handle()
     * and so separate lock is not needed here.
     */
    list_add_tail_rcu(&handle->h_node, &handler->h_list);

    if (handler->start)
        handler->start(handle);

    return 0;
}

注册字符设备cdev_init:

 1323 static const struct file_operations evdev_fops = {
  1324     .owner      = THIS_MODULE,
  1325     .read       = evdev_read,
  1326     .write      = evdev_write,
  1327     .poll       = evdev_poll,
  1328     .open       = evdev_open,
  1329     .release    = evdev_release,
  1330     .unlocked_ioctl = evdev_ioctl,
  1331 #ifdef CONFIG_COMPAT                                                                                  
  1332     .compat_ioctl   = evdev_ioctl_compat,
  1333 #endif
  1334     .fasync     = evdev_fasync,
  1335     .flush      = evdev_flush,

详见下图:
在这里插入图片描述

4.输入子系统分析

drivers/input/input.c中:
static int __init input_init(void)
{
      int err;
      err = class_register(&input_class);
      ……
      err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
      ……
}

input_fops定义:


static const struct file_operations input_fops = {
     .owner = THIS_MODULE,
     .open = input_open_file,
};

5.从应用层的角度出发看input子系统

  在应用层调用read函数,将调用到evdev_read:

static ssize_t evdev_read(struct file *file, char __user *buffer,
              size_t count, loff_t *ppos)
{
    struct evdev_client *client = file->private_data;
    struct evdev *evdev = client->evdev;
    struct input_event event;
    size_t read = 0;
    int error;

    if (count != 0 && count < input_event_size())
        return -EINVAL;

    for (;;) {
        if (!evdev->exist || client->revoked)
            return -ENODEV;

        /*如果client的环形缓冲区中没有数据并且是非阻塞的,那么返回-EAGAIN,也就是try again*/
        if (client->packet_head == client->tail &&
            (file->f_flags & O_NONBLOCK))
            return -EAGAIN;

        /*
         * count == 0 is special - no IO is done but we check
         * for error conditions (see above).
         */
        if (count == 0)
            break;

            /*调用evdev_fetch_next_event,如果获得了数据则取出来*/ 
        while (read + input_event_size() <= count &&
               evdev_fetch_next_event(client, &event)) {

            /*input_event_to_user调用copy_to_user传入用户程序中,这样读取完成*/  
            if (input_event_to_user(buffer + read, &event))
                return -EFAULT;

            read += input_event_size();
        }

        if (read)
            break;

        /*如果没有数据,并且是阻塞的,则在等待队列上等待*/  
        if (!(file->f_flags & O_NONBLOCK)) {
            error = wait_event_interruptible(evdev->wait,
                    client->packet_head != client->tail ||
                    !evdev->exist || client->revoked);
            if (error)
                return error;
        }
    }

    return read;
}

  当read函数调用 wait_event_interruptible进入了休眠状态时,通过evdev_event函数唤醒:

static void evdev_event(struct input_handle *handle,
            unsigned int type, unsigned int code, int value)
{
    struct input_value vals[] = { { type, code, value } };
    evdev_events(handle, vals, 1);
         ---> evdev_pass_values(client, vals, count, ev_time);
        	 ---> __pass_event
                ---> client->buffer[client->head++] = *event; //将input输入事件数据存放在evdev_client结构体中的缓冲区中)
            ---> wake_up_interruptible(&evdev->wait);
}

  而evdev_event()是evdev_handler->event成员,谁调用evdev_event()这个evdev_handler->event事件驱动函数,回溯函数调用:

handler->event(handle, v->type, v->code, v->value)
     ---> input_to_handler
         ---> input_pass_values
            ---> input_handle_event
                 ---> input_event   //向事件处理层上报数据并唤醒

6.Input APIs

  • struct input_dev *input_allocate_device(void); //向内存中分配input_dev结构体

  • input_set_capability; //设置设备可以产生哪些事件

  • input_free_device(struct input_dev *dev); //释放内存中的input_dev结构体

  • input_register_device(struct input_dev *dev); //注册一个input_dev,若有对应的驱动事件, 则在/sys/class/input下创建这个类设备

  • input_unregister_device(struct input_dev *dev); //卸载/sys/class/input目录下的
    input_dev这个类设备

  • set_bit(nr,p); //设置某个结构体成员p里面的某位等于nr,支持这个功能

    • set_bit(EV_KEY,buttons_dev->evbit); //设置input_dev结构体buttons_dev->evbit支持EV_KEY
    • set_bit(KEY_S,buttons_dev->keybit); //设置input_dev结构体buttons_dev->keybit支持按键”S”
  • input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat); //设置绝对位移的支持参数

    • dev: 需要设置的input_dev结构体
    • axis : 需要设置的数组号,常用的有: ABS_X(X坐标方向), ABS_Y(Y坐标方向), ABS_PRESSURE(压力方向)
    • min: axis方向的最小值, max:axis方向的最大值, fuzz: axis方向的干扰值, flat:axis方向的平焊位置
  • input_report_abs(struct input_dev *dev, unsigned int code, int value); //上报EV_ABS事件

    • *dev :要上报哪个input_dev驱动设备的事件
    • code: EV_ABS事件里支持的哪个方向,比如X坐标方向则填入: ABS_X
    • value:对应的方向的值,比如X坐标126
  • input_report_key(struct input_dev *dev, unsigned int code, int value); //上报EV_KEY事件

  • input_sync(struct input_dev *dev); //同步事件通知,通知系统有事件上报

7.输入设备驱动案例

  案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:

 #include <linux/input.h>                                                                                                        
 #include <linux/module.h>
 #include <linux/init.h>
 
 #include <asm/irq.h>
 #include <asm/io.h>
 
 static struct input_dev *button_dev;
 
 static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)
 {
        input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);
        input_sync(button_dev);
 }      
 
 static int __init button_init(void)
 {
        int error;
        
        if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) {
                 printk(KERN_ERR"button.c: Can't allocate irq %d\n", button_irq);
                 return -EBUSY;
        }      
        
         button_dev = input_allocate_device();
        if (!button_dev) {
                 printk(KERN_ERR"button.c: Not enough memory\n");
                 error = -ENOMEM;
                 goto err_free_irq;
        }
 
        button_dev->evbit[0] = BIT(EV_KEY);
        button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
 
        error = input_register_device(button_dev);
        if (error) {
                 printk(KERN_ERR"button.c: Failed to register device\n");
                 goto err_free_dev;
        }
 
        return 0;
 
 err_free_dev:
        input_free_device(button_dev);
 err_free_irq:
        free_irq(BUTTON_IRQ, button_interrupt);
        return error;
 }
 
 static void __exit button_exit(void)
 {
       input_unregister_device(button_dev);
        free_irq(BUTTON_IRQ, button_interrupt);
}
 
module_init(button_init);
module_exit(button_exit);

8.台式机触摸屏驱动

  • drivers/hid/hid-multitouch.c

refer to

  • https://www.jianshu.com/p/e9cfae59e3df
  • https://blog.csdn.net/zifehng/article/details/70169512
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值