欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux input framework(1) -- 框架概述

程序员文章站 2022-05-31 23:29:40
...
  • 了解linux input framework

1.input输入子系统框架

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

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

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

  • 事件处理层,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

linux input framework(1) -- 框架概述

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

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

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

2.linux input 事件处理框架
linux input framework(1) -- 框架概述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层解析

3.1.Input输入子系统数据结构关系图
linux input framework(1) -- 框架概述
3.2.input_handler结构体

  以evdev.c中的evdev_handler为例:

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.input字符设备注册过程

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,
};

Input_dev和input_handler匹配后调用input_handler的connect。以evdev_handler为例:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
                struct evdev *evdev; 
                struct class_device *cdev;
                dev_t devt;
                int minor;
                int error;

        	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
                if (minor == EVDEV_MINORS) {
                        printk(KERN_ERR "evdev: no more free evdev devices\n");
                        return -ENFILE;
                }

        		evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//为每个匹配evdev_handler的设备创建一个evdev。
                if (!evdev)
                        return -ENOMEM;

        		INIT_LIST_HEAD(&evdev->client_list);
                init_waitqueue_head(&evdev->wait);

		        evdev->exist = 1;
                evdev->minor = minor;
                evdev->handle.dev = dev;
                evdev->handle.name = evdev->name;
                evdev->handle.handler = handler;
                evdev->handle.private = evdev;
                sprintf(evdev->name, "event%d", minor);

        		evdev_table[minor] = evdev;//记录evdev的位置,字符设备/dev/input/evnetx访问时根据次设备号及EVDEV_MINOR_BASE终在evdev_open中找到对应的evdev
                devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
                cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);//创建了event字符设备节点
                ……
}

4.输入设备驱动案例

  案例代码描述了一个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);
相关标签: input