linux设备模型之input子系统

==================================
本文系本站原创,欢迎转载!

转载请注明出处:http://blog.csdn.net/gdt_A20

==================================


摘要:

input子系统是kernel中比较简单的一个子系统,主要用来管理输入设备(触摸屏,键盘等等),个人感
觉可以作为driver的起点,输入设备不明思议,要提供输入信息,input子系统将这些信息当作事件,进行区分,实
时的上传!input中不是那么规矩,并没有抽象出来总线的概念(硬件上不存在),而是用一个结构struct input_handle充当了总线

的结构,struct input_handle挂接了操作结构以及设备,input设备用struct input_dev结构来表示,操作函数用struct input_handler

来表示,注意这里input_handle和input_handler很相似,存在全局的input设备链表input_dev_list,用于连接所有input devices,

存在全局的input操作链表input_handler_list,用于挂接所有handler.


主要涉及目录:drivers/input

struct input_dev


一、相关数据结构

1.input device

   

struct input_dev {
	const char *name;               //名字
	const char *phys;               //物理地址
	const char *uniq;               //设备特殊标识码
	struct input_id id;             //设备id

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];  //设备属性相关位图

	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];            //支持的事件
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];          //key,button位图
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];          //相对坐标
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];          //绝对坐标
	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];          //杂项事件支持
	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];          //led相关
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];          //音响效果相关
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

	unsigned int hint_events_per_packet;

	unsigned int keycodemax;                              //键码表大小
	unsigned int keycodesize;                             
	void *keycode;                                        //按键扫描码

	int (*setkeycode)(struct input_dev *dev,              //老式扫描方法
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;

	unsigned int repeat_key;
	struct timer_list timer;                             //定时器

	int rep[REP_CNT];

	struct input_mt_slot *mt;
	int mtsize;
	int slot;
	int trkid;

	struct input_absinfo *absinfo;

	unsigned long key[BITS_TO_LONGS(KEY_CNT)];
	unsigned long led[BITS_TO_LONGS(LED_CNT)];
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];

	int (*open)(struct input_dev *dev);                 //open回调函数
	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 __rcu *grab;

	spinlock_t event_lock;
	struct mutex mutex;

	unsigned int users;
	bool going_away;

	bool sync;

	struct device dev;                                  //devices 模型

	struct list_head	h_list;                    //连接相应的handle
	struct list_head	node;                      //挂接到全局input_dev_list
};
2.操作函数结构input_handler

struct input_handler {

	void *private;                            //私有属性结构

	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	bool (*match)(struct input_handler *handler, struct input_dev *dev);
	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;    //支持的devices,id表

	struct list_head	h_list;   //用于连接到对应的handle的
	struct list_head	node;     //用于连接到全局的input_handler_list链表
};
3.bus的充当者总体管理结构

struct input_handle {

	void *private;                     //私有数据结构

	int open;                          //open计数
	const char *name;                  //名字

	struct input_dev *dev;             //连接设备
	struct input_handler *handler;     //连接driver的操作函数

	struct list_head	d_node;    //设备挂接点
	struct list_head	h_node;    //handle挂接点
};

二、相关的操作函数

1.关心一下核心文件input.c,input子系统的初始化:

static int __init input_init(void)
{
	int err;

	err = class_register(&input_class);                          //注册input类,出现在sys/class下       
	if (err) {
		pr_err("unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init();                                    //在proc下添加相关信息(bus/input)
	if (err)
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);  //注册成字符设备,设备号13
	if (err) {
		pr_err("unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}
貌似戛然而止了,^.^!,
2.向input子系统添加设备的函数,

int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_handler *handler;
	const char *path;
	int error;

	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	if (!dev->hint_events_per_packet)
		dev->hint_events_per_packet =
				input_estimate_events_per_packet(dev);

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	init_timer(&dev->timer);
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		dev->timer.data = (long) dev;
		dev->timer.function = input_repeat_key;
		dev->rep[REP_DELAY] = 250;
		dev->rep[REP_PERIOD] = 33;
	}

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;                //如果没有设置,设置成默认值

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	dev_set_name(&dev->dev, "input%ld",                               //name
		     (unsigned long) atomic_inc_return(&input_no) - 1);

	error = device_add(&dev->dev);                                   //标准设备的加入,这个,,加哪去了,没bus的野设备??
	if (error)                                                       //直接扔到系统全局总设备链表了
		return error;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error) {
		device_del(&dev->dev);
		return error;
	}

	list_add_tail(&dev->node, &input_dev_list);                   //加入全局input device链表

	list_for_each_entry(handler, &input_handler_list, node)       //遍历handler链表,匹配合适的handler
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	return 0;
}
继续跟一下input_attach_handler函数,

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

	id = input_match_device(handler, dev);                                  //具体的匹配函数
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id);                              //匹配后调用connect函数
	if (error && error != -ENODEV)
		pr_err("failed to attach handler %s to device %s, error: %d\n",
		       handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}
还有个函数
static const struct input_device_id *input_match_device(struct input_handler *handler,
							struct input_dev *dev)
{
	const struct input_device_id *id;
	int i;

	for (id = handler->id_table; id->flags || id->driver_info; id++) {

		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
			if (id->bustype != dev->id.bustype)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
			if (id->vendor != dev->id.vendor)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
			if (id->product != dev->id.product)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
			if (id->version != dev->id.version)
				continue;

		MATCH_BIT(evbit,  EV_MAX);
		MATCH_BIT(keybit, KEY_MAX);
		MATCH_BIT(relbit, REL_MAX);
		MATCH_BIT(absbit, ABS_MAX);
		MATCH_BIT(mscbit, MSC_MAX);
		MATCH_BIT(ledbit, LED_MAX);
		MATCH_BIT(sndbit, SND_MAX);
		MATCH_BIT(ffbit,  FF_MAX);
		MATCH_BIT(swbit,  SW_MAX);

		if (!handler->match || handler->match(handler, dev))             //不光是调用match函数,上面的匹配也很严格,厂商
			return id;                                               //版本一个也不能少
	}

	return NULL;
}

3.handler的注册

同样handler也要有注册的

int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int retval;

	retval = mutex_lock_interruptible(&input_mutex);
	if (retval)
		return retval;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5]) {
			retval = -EBUSY;
			goto out;
		}
		input_table[handler->minor >> 5] = handler;
	}

	list_add_tail(&handler->node, &input_handler_list);         //加入全局handler链表

	list_for_each_entry(dev, &input_dev_list, node)            //遍历devices链表,匹配
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

 out:
	mutex_unlock(&input_mutex);
	return retval;
}

三、input deivce实例

这个东东还是找个例子看起来比较方便,

翻一翻touchscreen下s3c2410_ts.c,

直接进入主题看probe

static int __devinit s3c2410ts_probe(struct platform_device *pdev)
{
	struct s3c2410_ts_mach_info *info;
	struct device *dev = &pdev->dev;
	struct input_dev *input_dev;
	struct resource *res;
	int ret = -EINVAL;

	/* Initialise input stuff */
	memset(&ts, 0, sizeof(struct s3c2410ts));

	ts.dev = dev;

	info = pdev->dev.platform_data;
	if (!info) {
		dev_err(dev, "no platform data, cannot attach\n");
		return -EINVAL;
	}

	dev_dbg(dev, "initialising touchscreen\n");

	ts.clock = clk_get(dev, "adc");                                //申请clock
	if (IS_ERR(ts.clock)) {
		dev_err(dev, "cannot get adc clock source\n");
		return -ENOENT;
	}

	clk_enable(ts.clock);                                         //使能clock
	dev_dbg(dev, "got and enabled clocks\n");

	ts.irq_tc = ret = platform_get_irq(pdev, 0);                 //中断号
	if (ret < 0) {
		dev_err(dev, "no resource for interrupt\n");
		goto err_clk;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);       //相关寄存器地址
	if (!res) {
		dev_err(dev, "no resource for registers\n");
		ret = -ENOENT;
		goto err_clk;
	}

	ts.io = ioremap(res->start, resource_size(res));           //进行映射
	if (ts.io == NULL) {
		dev_err(dev, "cannot map registers\n");
		ret = -ENOMEM;
		goto err_clk;
	}

	/* inititalise the gpio */
	if (info->cfg_gpio)
		info->cfg_gpio(to_platform_device(ts.dev));       //相关gpio配置

	ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,     //注意这里adc核心相关,select会启动timer上报adc事件
				     s3c24xx_ts_conversion, 1);
	if (IS_ERR(ts.client)) {
		dev_err(dev, "failed to register adc client\n");
		ret = PTR_ERR(ts.client);
		goto err_iomap;
	}

	/* Initialise registers */
	if ((info->delay & 0xffff) > 0)
		writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);

	writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);

	input_dev = input_allocate_device();                    //这里重点,分配了一个input device设备
	if (!input_dev) {
		dev_err(dev, "Unable to allocate the input device !!\n");
		ret = -ENOMEM;
		goto err_iomap;
	}

	ts.input = input_dev;
	ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
	input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
	input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);

	ts.input->name = "S3C24XX TouchScreen";
	ts.input->id.bustype = BUS_HOST;
	ts.input->id.vendor = 0xDEAD;
	ts.input->id.product = 0xBEEF;
	ts.input->id.version = 0x0102;

	ts.shift = info->oversampling_shift;
	ts.features = platform_get_device_id(pdev)->driver_data;

	ret = request_irq(ts.irq_tc, stylus_irq, 0,          //申请中断,input设备大多都是使用中断的,pen down up事件
			  "s3c2410_ts_pen", ts.input);
	if (ret) {
		dev_err(dev, "cannot get TC interrupt\n");
		goto err_inputdev;
	}

	dev_info(dev, "driver attached, registering input device\n");

	/* All went ok, so register to the input system */
	ret = input_register_device(ts.input);             //这里注册input device
	if (ret < 0) {
		dev_err(dev, "failed to register input device\n");
		ret = -EIO;
		goto err_tcirq;
	}

	return 0;

 err_tcirq:
	free_irq(ts.irq_tc, ts.input);
 err_inputdev:
	input_free_device(ts.input);
 err_iomap:
	iounmap(ts.io);
 err_clk:
	del_timer_sync(&touch_timer);
	clk_put(ts.clock);
	return ret;
}



看一下 input_allocate_device函数,

struct input_dev *input_allocate_device(void)
{
	struct input_dev *dev;

	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
	if (dev) {
		dev->dev.type = &input_dev_type;        //初始化成input类型
		dev->dev.class = &input_class; 
		device_initialize(&dev->dev);
		mutex_init(&dev->mutex);
		spin_lock_init(&dev->event_lock);
		INIT_LIST_HEAD(&dev->h_list);
		INIT_LIST_HEAD(&dev->node);

		__module_get(THIS_MODULE);
	}

	return dev;
}
irq用于判断pen down up,启动adc上报坐标事件.


未完待续....


Thanks







你可能感兴趣的:(linux,timer,struct,list,input,events)