输入子系统

1、输入子系统主要分层

(1) 输入子系统事件处理层
(2) 输入子系统核心层(input.c就是核心层)
(3) 输入子系统设备驱动层

输入子系统主要为了简化开发过程,驱动的编写者只需要进行硬件底层的代码编写,上层的事件驱动由系统提供,不需要我们再去编写从上到下分别是事件驱动层,核心层,硬件驱动层,硬件驱动层代码由自己进行编写。当发生硬件上的改变的时候由硬件驱动层进行上报,上报给事件驱动层(事件处理层),通过input.c(核心层)进行事件的上报,最终事件处理层得到信息之后再进行进一步的处理


evdev.c keyboard.c mousedev.c都是处于事件处理层的
input.c 核心层
button.c(自己编写)设备驱动层


一个设备可能会与多个事件处理对应,此时设备发生的消息会传给所有相关联的事件处理
evdev的handler可与任意的dev进行连接,所以第二期视频中第一个驱动最终处理是由evdev的事件驱动进行处理的,这一点也可以在装载驱动之后看出来,装载驱动之后会有主次设备号,ls之后显示出event1主次设备号为13 65,而evdev.c中标明了次设备号从64开始

2、输入子系统驱动程序之按键驱动的编写

2.1、申请相关的结构体

static struct *input_dev buttons_dev;
static struct timer_list buttons_timer;
//下面分配了input_dev的空间,timer的初始化在2.5里面的init_timer中,申请空间
buttons_dev = input_allocate_device();

2.2、填充结构体

set_bit(EV_KEY, buttons_dev->evbit);
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);
set_bit(EV_REP, buttons_dev->evbit);

2.3、注册设备

int error = input_register_device(buttons_dev);

2.4、按键中断的初始化申请

for(error = 0; error < 4; error ++)
{
	if(request_irq(key_struct[error].irq, buttons_interrupt, IRQT_BOTHEDGE, key_struct[error].name, &key_struct[error]))
	{
		printk("Can't allocate irq\n");
		return -EBUSY;
	}
}

2.5、定时器的初始化

init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);

2.6、按键中断函数编写

static irqreturn_t buttons_interrupt(int irq, void *dev_desc)
{
	key_stmod = (struct key_st *)dev_desc;
	mod_timer(&buttons_timer, jiffies + 10);
	return IRQ_RETVAL(IRQ_HANDLED);
}

2.7、定时器中断函数

static void buttons_timer_function(unsigned long dat)
{
	unsigned long pinval;

	if(!key_stmod)
		return;

	pinval = s3c2410_gpio_getpin(key_stmod->pin);
	if(pinval)
	{
		input_report_key(buttons_dev, key_stmod->key_val, 0);
		input_sync(buttons_dev);
	}
	else
	{
		input_report_key(buttons_dev, key_stmod->key_val, 1);
		input_sync(buttons_dev);
	}

}

3、输入子系统实现过程

3.1、整体

整体上来讲,就是由用户自己建立 input_dev结构体,然后与特定的 input_handler进行匹配连接,两者通过 input_handle进行联系

3.2、基于代码

buttons_dev = input_allocate_device();
这句是向系统申请input_dev结构体内存

set_bit(EV_KEY, buttons_dev->evbit); 	等效于buttons_dev->keybit[0] = BIT(KEY_L);
set_bit(KEY_L, buttons_dev->keybit);	
上面两句是置位相应的选项,对于本驱动只有按键事件会被响应

error = input_register_device(buttons_dev);
注册设备判断返回值
input_register_device(buttons_dev);
	->set_bit(EV_SYN, dev->evbit);	//所有设备都支持同步事件
	->list_add_tail(&dev->node, &input_dev_list);	//将buttons_dev加入链表
	->list_for_each_entry(handler, &input_handler_list, node)	//列出所有的handler,目的是与buttons_dev进行匹配
		-->input_attach_handler(dev, handler);	
			--->id = input_match_device(handler->id_table, dev);	//前面排除黑名单之后进行id号的匹配
			--->error = handler->connect(handler, dev, id);		//调用handler的connect函数进行双方的连接
			对于本例来说connect函数就是evdev.c里面的connect函数
			connect函数里面
			devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
			cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);	//为设备创建设备节点
			error = input_register_handle(&evdev->handle);	//此处进行dev与handler的连接
				---->list_add_tail(&handle->d_node, &handle->dev->h_list);
				---->list_add_tail(&handle->h_node, &handler->h_list);	//两边互联,通过h_list链表
				
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,
};	//evdev.c里面的handler结构体,与本例的buttons_dev相结合,在evdev_handler结构体里面由新的evdev_fops,读写都会调用到这里面的读写函数			


struct input_device_id {  
 
    kernel_ulong_t flags;           /*标志信息*/  
    __u16 bustype;                  /*总线类型*/  
    __u16 vendor;                   /*制造商ID*/  
    __u16 product;                  /*产品ID*/  
    __u16 version;                  /*版本号*/  
    ...  
    kernel_ulong_t driver_info;     /*驱动额外的信息*/  
};	//id_table的内容

消息的上报,另外还有input_report_abs等等消息上报函数,每个函数都对应相应的消息
但是通用的接口事件函数都是input_event,此函数可用于处理任意一种linux已定义的消息上报,内部使用switch-case的结构进行消息选择上报
input_report_key之后还需要使用input_sync进行事件的同步,即通知接收到消息的事件处理模块有消息到达,在多变量的传送过程中有很重要
的作用


input_report_key(buttons_dev, key_stmod->key_val, 0);
input_sync(buttons_dev);

按键触发之后上传消息,到input_sync的时候会有

dev->event(dev, type, code, value);调用到设备对应事件处理函数进行处理,对于按键驱动使用到的evdev事件来说
	->static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)	//上面的dev->event
		--> kill_fasync(&client->fasync, SIGIO, POLL_IN);	//同步机制
			wake_up_interruptible(&evdev->wait);			//唤醒中断,之后会有相应的处理函数将消息发给特定的pid号所在的程序
接下来的动作可以参考另一篇文章: 目录里面有记录fasync机制

4、层次,最重要的input_dev和input_handler以及input_handle

4.1、input_dev


struct input_dev {

	void *private;		//私有指针,以供用户进行一些私有功能扩展

	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;	//input_handle主要就是通过id来进行识别连接的

	/* 各种事件标志,按键,相对位移,绝对位移等等,相应的位置位代表这个设备支持相应的事件类型 */
	unsigned long evbit[NBITS(EV_MAX)];
	unsigned long keybit[NBITS(KEY_MAX)];
	unsigned long relbit[NBITS(REL_MAX)];
	unsigned long absbit[NBITS(ABS_MAX)];
	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)];

	/* .................. */
	
	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*flush)(struct input_dev *dev, struct file *file);
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

	struct input_handle *grab;

	/* .................. */
	
	struct list_head	h_list;	//所有的东西初始化完毕之后会指向input_handle结构体,input_handle结构体负责input_dev与input_handler进行联系
	struct list_head	node;
};

4.2、input_handler

/**
 * struct input_handler - implements one of interfaces for input devices
 * @private: driver-specific data
 * @event: event handler
 * @connect: called when attaching a handler to an input device
 * @disconnect: disconnects a handler from input device
 * @start: starts handler for given handle. This function is called by
 *	input core right after connect() method and also when a process
 *	that "grabbed" a device releases it
 * @fops: file operations this driver implements
 * @minor: beginning of range of 32 minors for devices this driver
 *	can provide
 * @name: name of the handler, to be shown in /proc/bus/input/handlers
 * @id_table: pointer to a table of input_device_ids this driver can
 *	handle
 * @blacklist: prointer to a table of input_device_ids this driver should
 *	ignore even if they match @id_table
 * @h_list: list of input handles associated with the handler
 * @node: for placing the driver onto input_handler_list
 */
struct input_handler {

	void *private;

	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
	void (*disconnect)(struct input_handle *handle);
	void (*start)(struct input_handle *handle);

	const struct file_operations *fops;
	int minor;
	const char *name;

	const struct input_device_id *id_table;
	const struct input_device_id *blacklist;

	struct list_head	h_list;
	struct list_head	node;
};

4.3、input_handle

struct input_handle {

	void *private;

	int open;
	const char *name;

	struct input_dev *dev;			//指向事件的硬件设备结构体
	struct input_handler *handler;	//指向事件的处理结构体

	struct list_head	d_node;
	struct list_head	h_node;
};

4.4、input_event

上报事件数据格式都会采用这种方式进行上传
struct input_event {
	struct timeval time;	//时间结构体,包含当前系统的秒数与微秒
	__u16 type;				//事件类型。比如按键类事件或者绝对位移类事件
	__u16 code;				//码。比如按键类的shift键
	__s32 value;			//值,按键会有按下或者松开,0或者1,绝对位移会有x与y方向上的大小
};

在linux系统中使用hexdump /dev/event0可以查看到事件返回的值

//事件类型如下
#define EV_SYN			0x00	//同步
#define EV_KEY			0x01    //按键
#define EV_REL			0x02    //相对坐标(例如鼠标的位置移动,报告相对坐标)
#define EV_ABS			0x03    //绝对坐标(例如触摸屏的位置点击,报告绝对坐标)
#define EV_MSC			0x04    //其他
#define EV_SW			0x05	//开关设备
#define EV_LED			0x11	//LED灯,(例如键盘LED)
#define EV_SND			0x12	//声音
#define EV_REP			0x14	//警报
#define EV_FF			0x15	//力反馈
#define EV_PWR			0x16	//电源
#define EV_FF_STATUS	0x17	//力反馈状态
#define EV_MAX			0x1f	//时间类型最大个数以及提供掩码支持
*******************以上是linux内核提供的事件类型*********************

你可能感兴趣的:(s3c2440,linux驱动,输入子系统)