10.输入子系统

2021-05-20 15:16:11 浏览数 (1)

在内核写好一些程序的基础上修改!

input_init分析:

代码语言:javascript复制
drivers/input/input.c:
	input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	
static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,
};

问:怎么读按键?

input_open_file
	struct input_handler *handler = input_table[iminor(inode) >> 5];
	new_fops = fops_get(handler->fops)  //  =>&evdev_fops
	file->f_op = new_fops;
	err = new_fops->open(inode, file);

app: read > ... > file->f_op->read  

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
	
	


注册输入设备:
  
	// 放入链表
	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


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函数建立"连接"

 


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);
	
怎么读按键?
app: 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);
   			

谁来唤醒?
evdev_event
	wake_up_interruptible(&evdev->wait);


evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr(查看例子)
	// 上报事件
	input_event(input, type, button->code, !!state);
	input_sync(input);
	
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
	struct input_handle *handle;
	//(h_list中存放的是handle)
	list_for_each_entry(handle, &dev->h_list, d_node)
		if (handle->open)
			handle->handler->event(handle, type, code, value);


怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件

button.c

代码语言:javascript复制
/* 参考driversinputkeyboardgpio_keys.c */
/*板子默认启动QT,装载本驱动前,修改etc/init.d/rcS文件 注释掉/bin/qpe.sh*/
/*或者点开QT的记事本,再按下板子的按键测试,在QT的记事本中就可以看到输入*/
/*
	struct input_event {
	struct timeval time;//时间
	__u16 type;//按键类/相对位移/绝对位移
	__u16 code;//那个按键/X,Y相对位移/X,Y绝对位移
	__s32 value;//左移/右移
};
*/
#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};

struct pin_desc pins_desc[4] = {
	{IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
	{IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
	{IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
	{IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
};

static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	/* 10ms后启动定时器 */
	irq_pd = (struct pin_desc *)dev_id;//记录下按键 ID值
	mod_timer(&buttons_timer, jiffies HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer_function(unsigned long data)
{
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;

	if (!pindesc)
		return;
	
	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if (pinval)
	{
		/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		/*上报事件  .hlist 调用handle*/
		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);
	}
}

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

	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	/*
	#define EV_SYN			0x00  同步类
      #define EV_KEY			0x01	  按键类
      #define EV_REL			0x02  相对位移类事件(鼠标移动)
      #define EV_ABS			0x03  绝对位移(触摸屏)
	unsigned long evbit[NBITS(EV_MAX)]; 表示能产生那类事件
	unsigned long keybit[NBITS(KEY_MAX)];表示能产生那些按键
	unsigned long relbit[NBITS(REL_MAX)];表示能产生那些相对位移事件
	unsigned long absbit[NBITS(ABS_MAX)];表示能产生那些绝对位移事件
	*/
	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);
	
	/* 4. 硬件相关的操作 */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 4; i  )
	{
		request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
	}
	
	return 0;
}

static void buttons_exit(void)
{
	int i;
	for (i = 0; i < 4; i  )
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}

	del_timer(&buttons_timer);
	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);	
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

input驱动的测试方法 1.ls /dev/event* -l 查看现有的/dev/event设备 2.insmod buttons_input.ko 安装驱动 3.ls /dev/event -l 查看buttons_input对应的设备 4.cat /dev/tty1,然后在按键,“l”“s”“ENTER”便会出现ls 5.如果启动了QT,可以点开记事本,按相应的按键“l”“s”“ENTER”便会在记事本上出现ls 6.也可通过执行exec 0</dev/tty1 //标准输入改为tty1,然后重复上述操作即可。

0 人点赞