8、linux下输入子系统
input_sync(button_dev); /*通知接收者,一个报告发送完毕*/
参考:http://www.51hei.com/bbs/dpj-27652-1.html 很详细说明
input.c是输入子系统驱动程序顶层框架文件,是一个通用的文件
在connect函数中
 for (minor = 0; minor < EVDEV_MINORS; minor++)
		  if (!evdev_table[minor])
			    break;
找一个空的evdev_table用来存放evdev,其中minor就是/dev/input/evnetx访问时的x
drivers/input/input.c:
class_register(&input_class)//创建一个类,作用和class_create等价,class_create内部就是调用class_register来实现的
//device_create是在input_handler的connect函数中通过device_add实现的,因为device_create实际上就是调用device_add
	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分为左右两边,一遍调用input_register_handler ,一遍调用input_register_device
调用input_register_handler 的主要有evdev、键盘、鼠标、触摸屏等输入设备,这部分写好了纯软件代码,硬件相关的在input_register_device中,比如触摸屏驱动程序:s3c2410_ts.c
注册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
	
	
注册输入设备:
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
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);
	
怎么读按键?
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
input_handle_event(dev, type, code, value);
input_pass_event(dev, type, code, value);
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
        handler->event(handle, type, code, value);
	          wake_up_interruptible(&evdev->wait);
evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr
	// 上报事件
	input_event(input, type, button->code, !!state);(上报的时候是从input_handle链表取出handle,在调用handler的event,因此可能有多个支持的handle,在input_pass_event函数中)
	input_sync(input);
	
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)
		if (handle->open)
			handle->handler->event(handle, type, code, value);//evdev_event
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
			
struct input_dev {
void *private;
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件,可以看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)];
测试:
1. 
hexdump /dev/event1  (open(/dev/event1), read(), )
               秒               微秒           类     code value(四字节)
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000(在copy_to_user中拷贝input_event结构体数据)
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000(这是同步类事件,每个事件后都要发同步)
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000
2. 如果没有启动QT:
cat /dev/tty1   (原因是在tty下会包括keyboard.c,其也调用了input_register_handler,因此在注册驱动的时候除了和evdev关联起来,还和keyboard关联起来了,而keyboard.c的上层是tty)
按:s2,s3,s4
就可以得到ls
或者:
exec 0</dev/tty1(0表示标准输入,将标准输入文件改为tty1,即从tty1得到输入,以前0是从sh文件得到输入,sh是从串口得到输入)
然后可以使用按键来输入
(ps指令可以看到-sh程序的PID,在执行ls -l /proc/PID/fd可以看到-sh程序将标准输入、标准输出、标准错误都指向了窗口)
3. 如果已经启动了QT:
可以点开记事本
然后按:s2,s3,s4
如果可以参数重复类事件,input_event会启动定时器(通过input_start_autorepeat函数),调用定时器函数input_repeat_key继续上报时间,定时器在input_register_device中注册

 
                    
                 
                
            
         
 浙公网安备 33010602011771号
浙公网安备 33010602011771号