linux驱动--输入子系统input

参考:https://blog.csdn.net/lizuobin2/article/details/51508839

目录

 

什么是输入设备

框架:

输入子系统的工作流程(以按键为例):

1、开机,执行input.c    input_init函数

2、应用程序打开输入设备input_open_file,通过input_table找到事件处理器

3、应用程序调用read函数  也就是等同于调用handler->fops-read

4、有按键按下,该做的事情(上报事件):

4、事件上报过程,唤醒进程

5、事件处理层处理相应的事件,执行相应的read,write.....接口函数

输入子系统实现原理:

1、问题:

1、执行input_open_file   input_table由谁来构造?

2、read函数是怎么找到的?是在哪里定义的?

2、 事件处理层handler  以evdev_handler事件处理器为例调用 input_register_handler

3、设备层驱动层dev, 真正需要的工作驱动编写步骤

input_register_device解析

1. 分配一个input_dev结构体input_allocate_device()

2. 设置事件类型和按键代码

3. 注册 调用input_register_device

4. 硬件相关的代码,比如在中断服务程序里上报事件

4、设备层 dev   和  事件处理层 handler  匹配建立连接


 

什么是输入设备

框架:

主要关注两个层

设备层:就是我们需要自己编写的,包括输入设备的注册,事件的设置,事件的上报

处理层handler  :  这一般是系统定义的不需要自己编写,例如 evdev_handler,里面接受来自设备层的事件,定义file_operations结构体,提供read  write..............接口函数

输入子系统的工作流程(以按键为例):

1、开机,执行input.c    input_init函数


static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,   //input_open_file其实不是正真的open 在它里面会调用正真的open
};

static int __init input_init(void)
{
	int err;



	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);   //主设备号为13
	
}

2、应用程序打开输入设备input_open_file,通过input_table找到事件处理器

  //简化版

static int input_open_file(struct inode *inode, struct file *file)
{
	
            // 根据次设备号,从 input_table 数组中取出对应的 handler
        struct input_handler *handler = input_table[iminor(inode) >> 5];

       err = file->f_op->open(inode, file);


}

3、应用程序调用read函数  也就是等同于调用handler->fops-read

一般是以阻塞方式读,没有读到值则休眠进程

   		evdev_read
   			// 无数据并且是非阻塞方式打开,则立刻返回
		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);

4、有按键按下,该做的事情(上报事件):

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

/**
 * input_event() - 函数功能:上报事件
 * @dev: 输入子系统设备
 * @type: 事件类型
 * @code: event code  键的编号比如  #define KEY_ENTER        28  enter键是28号
 * @value: value of the event    键值  比如,比如按下是读出的键值为0  松开为1
 *
 */

static struct input_dev *buttons_dev;  //定义一个输入设备

		
input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
input_sync(buttons_dev);

4、事件上报过程,唤醒进程

input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{	
        struct input_handle *handle;

	list_for_each_entry(handle, &dev->h_list, d_node) //通过之前匹配好的链表找到handle
		if (handle->open)
		handle->handler->event(handle, type, code, value); //通过handle找到handler->event

}

//handler->event 函数做了哪些事?
//evdev_handler 为例

// evdev_event函数  也就是  handler->event
{
	wake_up_interruptible(&evdev->wait);  //唤醒刚才休眠的进程
}

5、事件处理层处理相应的事件,执行相应的read,write.....接口函数

 

输入子系统实现原理:

1、问题:

1、执行input_open_file   input_table由谁来构造?

input_register_handler 解析:

注册input_handler:
input_register_handler
	// 放入数组
	input_table[handler->minor >> 5] = handler;
	
	// 放入链表
	list_add_tail(&handler->node, &input_handler_list);

	// 对于每个input_dev,调用input_attach_handler
	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev

2、read函数是怎么找到的?是在哪里定义的?

在input_open_file  中找的 

在evdev_handler 构造的

    

static int input_open_file(struct inode *inode, struct file *file)
{
	
.....................
        
   const struct file_operations *old_fops, *new_fops = NULL;

        // 将 handler 的fops赋值给file->f_op,并用调用新的open函数
        old_fops = file->f_op;
   //以后用户调用read函数就是调用 file->f_op->read  也就是等于调用handler->fops-read
      file->f_op = fops_get(handler->fops); 


...........................
}
static struct input_handler evdev_handler = {

	.fops =		&evdev_fops,  //文件操作
 
};

static const struct file_operations evdev_fops = {
	.owner =	THIS_MODULE,
	.read =		evdev_read,  //读
	.write =	evdev_write,  //写
	.poll =		evdev_poll,
	.open =		evdev_open,
	.release =	evdev_release,
	.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl =	evdev_ioctl_compat,
#endif
	.fasync =	evdev_fasync,
	.flush =	evdev_flush
};

2、 事件处理层handler  evdev_handler事件处理器为例调用 input_register_handler

这个一般是系统自带的,不需要我们编写,了解原理即可


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

3、设备层驱动层dev 真正需要的工作驱动编写步骤

input_register_device解析

注册输入设备:
input_register_device
	// 放入链表
	list_add_tail(&dev->node, &input_dev_list);
	
	// 对于每一个input_handler,都调用input_attach_handler
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev

1. 分配一个input_dev结构体input_allocate_device()

struct input_dev {

	void *private;

	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;

	unsigned long evbit[NBITS(EV_MAX)];   // 表示能产生哪类事件
	unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
	unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
	unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
	unsigned long mscbit[NBITS(MSC_MAX)];
	unsigned long ledbit[NBITS(LED_MAX)];
	unsigned long sndbit[NBITS(SND_MAX)];
	unsigned long ffbit[NBITS(FF_MAX)];
	unsigned long swbit[NBITS(SW_MAX)];


2. 设置事件类型和按键代码

事件类型:

Linux中输入设备的事件类型有
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件,如KEY_VOLUMEDOWN
EV_REL 0x02 相对坐标,   如shubiao上报的坐标
EV_ABS 0x03 绝对坐标,如触摸屏上报的坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈 
~~~~~~~~~~~~~~~~~~~~~~~~
EV_PWR       电源
EV_FF_STATUS   状态

按键编号:

KEY_L  : 代表L按键

KEY_S   代表S按键

KEY_ENTER   代表enter按键

...........................等等


3. 注册 调用input_register_device


4. 硬件相关的代码,比如在中断服务程序里上报事件

static struct input_dev *buttons_dev;   //定义输入设备

static int buttons_init(void)
{
	..............................
	
	/* 1. 分配一个input_dev结构体 */
	buttons_dev = input_allocate_device();;

	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);
	set_bit(EV_REP, buttons_dev->evbit);
	
	/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
	set_bit(KEY_L, buttons_dev->keybit);
	set_bit(KEY_S, buttons_dev->keybit);
	set_bit(KEY_ENTER, buttons_dev->keybit);
	set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

	/* 3. 注册 */
	input_register_device(buttons_dev);
	
    .......................
	
	return 0;
}

//上报事件
	if (pinval)
	{
		/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(buttons_dev);
	}
	else
	{
		/* 按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}

4、设备层 dev   和  事件处理层 handler  匹配建立连接

先匹配,看两者是否匹配

在建立连接,其实就是挂到一个链表上去

input_attach_handler
	id = input_match_device(handler->id_table, dev);
	
	error = handler->connect(handler, dev, id);


注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"

怎么建立连接?
1. 分配一个input_handle结构体
2. 
	input_handle.dev = input_dev;  // 指向左边的input_dev
	input_handle.handler = input_handler;  // 指向右边的input_handler
3. 注册:
   input_handler->h_list = &input_handle;
   inpu_dev->h_list      = &input_handle;


evdev_connect
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle
	
	// 设置
	evdev->handle.dev = dev;  // 指向左边的input_dev
	evdev->handle.name = evdev->name;
	evdev->handle.handler = handler;  // 指向右边的input_handler
	evdev->handle.private = evdev;
	
	// 注册
	error = input_register_handle(&evdev->handle);
发布了135 篇原创文章 · 获赞 112 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/88976404