嵌入式软件开发之------浅析linux驱动模型(六)input框架

Linux代码版本:linux3.0
开发板环境: tiny4412
导读:input框架是是针对的输入设备的特点抽象出来的驱动模型,如常见的鼠标、键盘、陀螺仪、ADC和温度传感等等各种各样的输入设备,都有一个明显的特点,就是采集到数据向系统报告,过程有高度的相似性。所以就将相似的地方抽象出来,驱动开发就剩下和硬件相关的部分。如input设备,只需要完成input_dev的相关参数设置,然后调用 input_register_device 注册,有数据的时候调用
/* 报告指定 type、 code 的输入事件 */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
/* 报告键值 */
void input_report_key(struct input_dev *dev, unsigned int code, int value);
/* 报告相对坐标 */
void input_report_rel(struct input_dev *dev, unsigned int code, int value);
/* 报告绝对坐标 */
void input_report_abs(struct input_dev *dev, unsigned int code, int value);
/* 报告同步事件 */
void input_sync(struct input_dev *dev);
等接口报告事件即可,其它工作都有input框架完成,大大减少了驱动开发的任务。

device 都有 相应的 driver ,两者通过 bus 进行匹配,最终通过各种形式完成其 open、close、read和write等函数来操作devcie。
input_dev 也有对应的对应的 driver ,只不过称作 input_handler ,其通过 input_handle 关联起来,input_handler 要完成对该
input_dev 的 open、close、read和write等函数。

linux已经完成了几种设备 input_handler ,如 mousedev_handler 和 joydev_handler 。同时提供了适配所有 input_dev 的 evdev_handler,下面将会以 mma7660 和 evdev_handler 为例分析 input 模型。 


下面看 evdev_handler 注册过程,然后再 mma7660 对应的input_dev的注册过程。

一、注册 evdev_handler
先看下 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;

	struct list_head	h_list;
	struct list_head	node;
};

void *private;
封装的私有结构体
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
处理 input_event 上报的事件
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
对 input_event 事件进行过滤
bool (*match)(struct input_handler *handler, struct input_dev *dev);
在对 device id 和 handler 的 id_table 比较后做的最后 input_dev 和 input_handler 的匹配
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
用来将 input_dev 和 handler 关联起来,当 input_dev 和 handler 匹配上的时候调用此函数 关联
void (*disconnect)(struct input_handle *handle);
和 connect 函数功能相反,取消 input_dev 和 handler 的关联
void (*start)(struct input_handle *handle);
在 connect 启动指定 handle 的 handler 
const struct file_operations *fops;
设备文件的操作函数集,open、close、read 和 write 等
int minor;
次设备号,决定了了此 handler 在 input_table 中的位置
const char *name;
此 handler 的名字,用来显示在 /proc/bus/input/handlers 
const struct input_device_id *id_table;
兼容的设备列表
struct list_head    h_list;
将对应的 handle 挂接到此链表中
struct list_head    node;
用于插入到全局链表 input_handler_list 中,注册 input_dev 的时候会遍历 input_handler_list 匹配 handler 

再看 evdev_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,
};

再看 evdev_handler 支持的设备列表

static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

下面看 evdev_handler 的注册过程

static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
            {
            	struct input_dev *dev;
            	int retval;

            	retval = mutex_lock_interruptible(&input_mutex);
            	if (retval)
            		return retval;
                /*初始化 evdev_handler->h_list */
            	INIT_LIST_HEAD(&handler->h_list);
                /*定义的时候初始化 evdev_handler->fops = &evdev_fops */
            	if (handler->fops != NULL) {
                    /* 定义的时候初始化 evdev_handler->minor = 64 ,右移 5 位为 2 */
            		if (input_table[handler->minor >> 5]) {
            			retval = -EBUSY;
            			goto out;
            		}
                    /*赋值 input_table[2] = &evdev_handler*/
            		input_table[handler->minor >> 5] = handler;
            	}
                /*将 evdev_handler 插入 input_handler_list,是个全局变量,后面还会用到*/
            	list_add_tail(&handler->node, &input_handler_list);
                /*遍历 input_dev_list 然后通过 input_attach_handler 匹配,可以推测 注册 input_dev 的时候会插入
                input_dev_list ,并且遍历 input_handler_list 来匹配 handler */
            	list_for_each_entry(dev, &input_dev_list, node)
            	    /*绑定 dev 和 handler ,会调用到 evdev_handler-> connect, 注册 input_dev
                的时候再分析 input_attach_handler*/
            		input_attach_handler(dev, handler);

            	input_wakeup_procfs_readers();

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

二、注册 input_dev

下面看 input_dev 结构体

struct input_dev {
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_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)];
	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)];
	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);
	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;

	struct list_head	h_list;
	struct list_head	node;
};

const char *name;
该 input 设备的名字
const char *phys;
系统层次结构中的物理路径
const char *uniq;
设备的特殊标识码(如果有的话)
struct input_id id;
此 input 设备的标识,如 总线类型、供应商、产品和版本
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
设备的属性 bitmap
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
设备支持的 event 类型
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
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)];
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;

一次事件中上报的 event 数,用来计算缓存 event 的 buffer大小
unsigned int keycodemax;
keycode 表的大小
unsigned int keycodesize;
keycode 表中每个元素的大小
void *keycode;
scancodes 到 keycode 的对应表
int (*setkeycode)(struct input_dev *dev,const struct input_keymap_entry *ke,unsigned int *old_keycode);
设置当前 keymap 的方法
int (*getkeycode)(struct input_dev *dev,struct input_keymap_entry *ke);
获取当前 keymap 的方法
struct ff_device *ff;
用于压力反馈相关的设备
unsigned int repeat_key;
用于软件实现按键自动重复功能,保存最后一次按下的值
struct timer_list timer;
安检自动重复的定时器
int rep[REP_CNT];
按键自动重复的参数
struct input_mt_slot *mt;
指向多点触摸屏 slot 数组
int mtsize;
多点触摸屏使用的 slot 数
int slot;
多点触控屏 当前要发送的 slot 
int trkid;
用于多点触摸屏的 tracking ID 
struct input_absinfo *absinfo;
保存上报绝对值的相关信息,如 min、max和fuzz等
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
反应当前 按键 的状态
unsigned long led[BITS_TO_LONGS(LED_CNT)];
反应当前 led 的状态
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
反应当前声音的效果
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
反应当前的开关状态
int (*open)(struct input_dev *dev);
第一个使用者 open 设备时调用的打开 input_dev 的函数,此时 input_dev 要能够开始产生 event (通过轮询、中断等方式)
void (*close)(struct input_dev *dev);
最后一个使用者 close 设备时调用此函数
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
handler 向 input_dev 发送事件时的回调函数
struct input_handle __rcu *grab;

spinlock_t event_lock;
用于新产生的处理 event 的自旋锁
struct mutex mutex;
用于同步的信号量
unsigned int users;
打开此设备 的 handler 的数量,由 input_open_device() and input_close_device()确保只有第一个 handler 能打开
和 最后一个 handler 能关闭此 设备 
bool going_away;
标识 input_dev 在取消注册的过程中,会导致  input_open_device 返回  -ENODEV
bool sync;
当最后一次 EV_SYN 后没有新的 event 设置为 1
struct device dev;
封装的 device 结构体
struct list_head    h_list;
将对应的 handle 挂接到此链表中
struct list_head    node;
用于插入到全局链表 input_dev_list 中,注册 handler 的时候会遍历 input_dev_list 匹配 input_dev     

 再看一下 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;
};

下面接着 上一节 注册 i2c_mma7660_driver 最终调用到的 mma7660_probe 函数,将省略 一些不相关的代码

static int __devinit mma7660_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	struct input_dev *idev;
	int poll_interval = POLL_INTERVAL;
	int input_fuzz = INPUT_FUZZ;
	int input_flat = INPUT_FLAT;
	int ret;
    /*检查 adapter 支持的通信协议,计算结果肯定是支持 ,否则该驱动就是有问题的  */
	ret = i2c_check_functionality(adapter,I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA);   
            {
            	return (func & i2c_get_functionality(adap)) == func;
                                {
                                    /*在 s3c24xx_i2c_probe 中初始化的 i2c->adap.algo    = &s3c24xx_i2c_algorithm 
                                    s3c24xx_i2c_algorithm->functionality = s3c24xx_i2c_func */
                                	return adap->algo->functionality(adap);
                                            {
                                                
                                                return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
                                            }
                                            
                                }
            }
	if (!ret) {
		dev_err(&client->dev, "I2C check functionality failed\n");
		return -ENXIO;
	}
    /* 赋值plat_data = &mma7660_pdata,其中 
        mma7660_pdata = {
    	.irq			= IRQ_EINT(25),
    	.poll_interval	= 100,
    	.input_fuzz		= 4,
    	.input_flat		= 4,
    };*/
	plat_data = (struct mma7660_platform_data *)client->dev.platform_data;
	if (plat_data == NULL) {
		dev_err(&client->dev, "lack of platform data!\n");
		return -ENODEV;
	}

	/* Get parameters from platfrom data */
    /*赋值轮询周期 poll_interval = 100 */
	if (plat_data->poll_interval > 0)
		poll_interval = plat_data->poll_interval;
    /* 设置过滤噪声值 */
	if (plat_data->input_fuzz > 0)
		input_fuzz = plat_data->input_fuzz;
	if (plat_data->input_flat > 0)
		input_flat = plat_data->input_flat;
    /*mma7660硬件初始化,这里只看函数调用关系*/
	if (mma7660_initialize(client) < 0) {
        {
        	int val;

        	/* Using test mode to probe chip */
        	i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
            {
            	union i2c_smbus_data data;
            	data.byte = value;
            	return i2c_smbus_xfer(client->adapter, client->addr, client->flags,I2C_SMBUS_WRITE, command,I2C_SMBUS_BYTE_DATA, &data);
                        {
                        	unsigned long orig_jiffies;
                        	int try;
                        	s32 res;

                        	flags &= I2C_M_TEN | I2C_CLIENT_PEC;
                            /*s3c24xx_i2c_algorithm->smbus_xfer =NULL ,所以走 else 分支*/
                        	if (adapter->algo->smbus_xfer) {
                        		i2c_lock_adapter(adapter);

                        		/* Retry automatically on arbitration loss */
                        		orig_jiffies = jiffies;
                        		for (res = 0, try = 0; try <= adapter->retries; try++) {
                        			res = adapter->algo->smbus_xfer(adapter, addr, flags,
                        							read_write, command,
                        							protocol, data);
                        			if (res != -EAGAIN)
                        				break;
                        			if (time_after(jiffies,
                        				       orig_jiffies + adapter->timeout))
                        				break;
                        		}
                        		i2c_unlock_adapter(adapter);
                        	} else
                                /*模拟SMBUS协议 通过 i2c_transfer 调用到 s3c24xx_i2c_xfer*/
                        		res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
                        					      command, protocol, data);

                        	return res;
                        }
            }
        	......
        	mma7660_client = client;
        	return 0;
        }
        
		goto error_init_client;
	}
    /* 创建一些 属性文件 ,可通过 store和show函数看其功能,这里不再分析*/
	ret = sysfs_create_group(&client->dev.kobj, &mma7660_group);
	if (ret) {
		dev_err(&client->dev, "create sysfs group failed!\n");
		goto error_init_client;
	}

	/* register to hwmon device */
    /* haware monitorring框架,为一件监控,若温度电压等提供的驱动框架,这里也不分析,并不难*/
	hwmon_dev = hwmon_device_register(&client->dev);
	if (IS_ERR(hwmon_dev)) {
		dev_err(&client->dev, "hwmon register failed!\n");
		ret = PTR_ERR(hwmon_dev);
		goto error_rm_dev_file;
	}

	/* input poll device register */
    /*分配一个 input_polled_dev 结构体,也就是此驱动用的是 轮询模式,上面周期都设定为 100ms了
    input_polled_dev 也就是对input_dev的封装,以更好地适应轮询方式*/
	mma7660_idev = input_allocate_polled_device();
	if (!mma7660_idev) {
		dev_err(&client->dev, "alloc poll device failed!\n");
		ret = -ENOMEM;
		goto error_rm_hwmon_dev;
	}
    /*赋值轮询函数,调用到的时候再分析 mma7660_dev_poll */
	mma7660_idev->poll = mma7660_dev_poll;
    /*赋值 mma7660_idev->poll_interval = 100 */
	mma7660_idev->poll_interval = plat_data->poll_interval;
    /*要对 input_dev赋值了*/
	idev = mma7660_idev->input;
    /*mma7660_idev->input->name = "mma7660" */
	idev->name = MMA7660_NAME;
    /*设备的总线类型为 、制造商、产品型号和版本等信息 */
	idev->id.bustype = BUS_I2C;/
	idev->id.vendor = 0x12FA;
	idev->id.product = 0x7660;
	idev->id.version = 0x0100;
    /*mma7660_idev->input->dev.parent 指向了/sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004c/input */
	idev->dev.parent = &client->dev;
    /*设置支持的时间类型为 EV_ABS ,也就是绝对值类型*/
	set_bit(EV_ABS, idev->evbit);
    /*设置支持 X、Y和Z坐标*/
	set_bit(ABS_X, idev->absbit);
	set_bit(ABS_Y, idev->absbit);
	set_bit(ABS_Z, idev->absbit);
    /*设置 X 轴方向绝对值信息的相关参数*/
	input_set_abs_params(idev, ABS_X, -512, 512, input_fuzz, input_flat);
    {
    	struct input_absinfo *absinfo;

    	input_alloc_absinfo(dev);
    	if (!dev->absinfo)
    		return;
        
    	absinfo = &dev->absinfo[axis];
        /*设置 X 轴的最小值 为 -512*/
    	absinfo->minimum = min;
        /*设置X轴的最大值 为 512 */
    	absinfo->maximum = max;
        /*设置过滤噪声值*/
    	absinfo->fuzz = fuzz;
        /**/
    	absinfo->flat = flat;

    	dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
    }
	input_set_abs_params(idev, ABS_Y, -512, 512, input_fuzz, input_flat);
	input_set_abs_params(idev, ABS_Z, -512, 512, input_fuzz, input_flat);
    /*终于到了注册设备 mma7660_idev */
	ret = input_register_polled_device(mma7660_idev);
            {
            	struct input_dev *input = dev->input;
            	int error;
                /* mma7660_idev->input->dev->p->driver_data =  mma7660_idev
                mma7660_idev倒变成 input->dev 的私有类型 了*/
            	input_set_drvdata(input, dev);
                /*初始化延迟执行的任务,其实实现了周期性调用 input_polled_device_work的功能 */
            	INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
                                               {
                                                	struct input_polled_dev *dev =
                                                		container_of(work, struct input_polled_dev, work.work);
                                                    /*终于在创建的定时器里有到了 mma7660_idev->poll = mma7660_dev_poll 
                                                        按照 之前设定 100ms周期周期性的调用 */
                                                	dev->poll(dev);
                                                	input_polldev_queue_work(dev);
                /* mma7660_idev->poll_interval = 100ms*/                                } 
            	if (!dev->poll_interval)
            		dev->poll_interval = 500;
            	if (!dev->poll_interval_max)
            		dev->poll_interval_max = dev->poll_interval;
                /*赋值,调用到的时候再分析*/
            	input->open = input_open_polled_device;
            	input->close = input_close_polled_device;
                /*这里开始注册input_dev */
            	error = input_register_device(input);
                        {
                        	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. */
                            /*所有的 input 设备都要支持 EV_SYN 事件类型*/
                        	__set_bit(EV_SYN, dev->evbit);

                        	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
                            /* KEY_RESERVED 类型时间也不会被送到 用户空间*/
                        	__clear_bit(KEY_RESERVED, dev->keybit);

                        	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
                            /*清楚不支持 时间类型的相关  位*/
                        	input_cleanse_bitmasks(dev);
                            /* mma7660_idev->input.hint_events_per_packet 未被初始化,也就是为 0*/
                        	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;
                        	}
                            /*赋值获取和设置keymap的函数*/
                        	if (!dev->getkeycode)
                        		dev->getkeycode = input_default_getkeycode;

                        	if (!dev->setkeycode)
                        		dev->setkeycode = input_default_setkeycode;
                            /*设置 mma7660_idev->input->dev->name = "input"*/
                        	dev_set_name(&dev->dev, "input%ld",
                        		     (unsigned long) atomic_inc_return(&input_no) - 1);
                            /*又看到了mma7660_idev,将会在 /sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004c 创建input目录及在
                                     input目录下创建相关文件*/
                        	error = (&dev->dev);
                        	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;
                        	}
                            /*将 mma7660_idev->input 插入到 input_dev_list */
                        	list_add_tail(&dev->node, &input_dev_list);
                            /*遍历 input_handler_list 匹配 相应的 handler*/
                        	list_for_each_entry(handler, &input_handler_list, node)
                        		input_attach_handler(dev, handler);
                                
                            {
                                const struct input_device_id *id;
                                int error;
                                /* dev 和 handler 匹配*/
                                id = input_match_device(handler, dev);
                                    {
                                    	const struct input_device_id *id;
                                    	int i;
                                        /* evdev_handler->id_table =  evdev_ids 
                                            static const struct input_device_id evdev_ids[] = {
                                            	{ .driver_info = 1 },	//Matches all devices 
                                            	{ },			
                                            };

                                        id->flags = 0 , id->driver_info = 1*/
                                    	for (id = handler->id_table; id->flags || id->driver_info; id++) {
                                            /*按 bus 类型匹配*/
                                    		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
                                    			if (id->bustype != dev->id.bustype)
                                    				continue;
                                            /*按 vendor 类型匹配*/
                                    		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
                                    			if (id->vendor != dev->id.vendor)
                                    				continue;
                                            /*按 product 类型匹配*/
                                    		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
                                    			if (id->product != dev->id.product)
                                    				continue;
                                            /*按 version 类型匹配*/
                                    		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);
                                            /* evdev_handler->match 未赋值,也就是为 NULL */
                                    		if (!handler->match || handler->match(handler, dev))
                                    			return id;
                                    	}

                                    	return NULL;
                                    }
                                if (!id)
                                    return -ENODEV;
                                /* evdev_handler->connect = evdev_connect*/
                                error = handler->connect(handler, dev, id);
                                        {
                                        	struct evdev *evdev;
                                        	int minor;
                                        	int error;
                                            /*遍历查找未使用的 MINORS, 最大支持 32 个 evdev */
                                        	for (minor = 0; minor < EVDEV_MINORS; minor++)
                                        		if (!evdev_table[minor])
                                        			break;

                                        	if (minor == EVDEV_MINORS) {
                                        		pr_err("no more free evdev devices\n");
                                        		return -ENFILE;
                                        	}

                                        	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
                                        	if (!evdev)
                                        		return -ENOMEM;

                                        	INIT_LIST_HEAD(&evdev->client_list);
                                        	spin_lock_init(&evdev->client_lock);
                                        	mutex_init(&evdev->mutex);
                                        	init_waitqueue_head(&evdev->wait);
                                            /*设置 evdev 的名字,也就是   eventX ,X为子设备号*/
                                        	dev_set_name(&evdev->dev, "event%d", minor);
                                        	evdev->exist = true;
                                        	evdev->minor = minor;
                                            /*挂接在 evdev->handle 上*/
                                        	evdev->handle.dev = input_get_device(dev);
                                            /*evdev->handle.name = "   eventX"  X为子设备号 */
                                        	evdev->handle.name = dev_name(&evdev->dev);
                                            /*将 evdev_handler 挂接在 evdev->handle 上*/
                                        	evdev->handle.handler = handler;
                                        	evdev->handle.private = evdev;
                                            /*设置 evdev 设备号 */
                                        	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
                                            /*设置 evdev 设备类为       input_class */
                                        	evdev->dev.class = &input_class;
                                            /*将 evdev 的父目录 指向 /sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004c/input/input2 也就是会在
                                            input2 目录下创建 event 目录及相关文件 */
                                        	evdev->dev.parent = &dev->dev;
                                        	evdev->dev.release = evdev_free;
                                        	device_initialize(&evdev->dev);
                                            /*注册 handle ,上面不是将 dev 和 handler 关联起来了吗*/
                                        	error = input_register_handle(&evdev->handle);
                                                    {
                                                    	struct input_handler *handler = handle->handler;
                                                    	struct input_dev *dev = handle->dev;
                                                    	int error;

                                                    	/*
                                                    	 * We take dev->mutex here to prevent race with
                                                    	 * input_release_device().
                                                    	 */
                                                    	error = mutex_lock_interruptible(&dev->mutex);
                                                    	if (error)
                                                    		return error;

                                                    	/*
                                                    	 * Filters go to the head of the list, normal handlers
                                                    	 * to the tail.
                                                    	 */
                                                    	 /*将 evdev->handle 添加到  的 evdev 的 handle 链表中*/
                                                    	if (handler->filter)
                                                    		list_add_rcu(&handle->d_node, &dev->h_list);
                                                    	else
                                                    		list_add_tail_rcu(&handle->d_node, &dev->h_list);

                                                    	mutex_unlock(&dev->mutex);

                                                    	/*
                                                    	 * Since we are supposed to be called from ->connect()
                                                    	 * which is mutually exclusive with ->disconnect()
                                                    	 * we can't be racing with input_unregister_handle()
                                                    	 * and so separate lock is not needed here.
                                                    	 */
                                                    	 /*将  evdev->handle 添加到   evdev_handler 的 handle 链表中*/
                                                    	list_add_tail_rcu(&handle->h_node, &handler->h_list);

                                                    	if (handler->start)
                                                    		handler->start(handle);

                                                    	return 0;
                                                    }
                                        	if (error)
                                        		goto err_free_evdev;

                                        	error = evdev_install_chrdev(evdev);
                                                    {
                                                    	/*
                                                    	 * No need to do any locking here as calls to connect and
                                                    	 * disconnect are serialized by the input core
                                                    	 */
                                                    	evdev_table[evdev->minor] = evdev;
                                                    	return 0;
                                                    }
                                        	if (error)
                                        		goto err_unregister_handle;

                                        	error = device_add(&evdev->dev);
                                        	if (error)
                                        		goto err_cleanup_evdev;

                                        	return 0;

                                         err_cleanup_evdev:
                                        	evdev_cleanup(evdev);
                                         err_unregister_handle:
                                        	input_unregister_handle(&evdev->handle);
                                         err_free_evdev:
                                        	put_device(&evdev->dev);
                                        	return error;
                                        }
                                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;
                            }

                        	input_wakeup_procfs_readers();

                        	mutex_unlock(&input_mutex);

                        	return 0;
                        }
            	if (error)
            		return error;
                /*创建属性文件,不再分析*/
            	error = sysfs_create_group(&input->dev.kobj,
            				   &input_polldev_attribute_group);
            	if (error) {
            		input_unregister_device(input);
            		return error;
            	}

            	/*
            	 * Take extra reference to the underlying input device so
            	 * that it survives call to input_unregister_polled_device()
            	 * and is deleted only after input_free_polled_device()
            	 * has been invoked. This is needed to ease task of freeing
            	 * sparse keymaps.
            	 */
            	input_get_device(input);

            	return 0;
            }
	if (ret) {
		dev_err(&client->dev, "register poll device failed!\n");
		goto error_free_poll_dev;
	}

	/* register interrupt handle */
    /*注册中断处理函数,且设置为下降沿触发*/
	ret = request_irq(plat_data->irq, mma7660_interrupt,
			IRQF_TRIGGER_FALLING, MMA7660_NAME, idev);
	if (ret) {
		dev_err(&client->dev, "request irq (%d) failed %d\n", plat_data->irq, ret);
		goto error_rm_poll_dev;
	}

	dev_info(&client->dev, "MMA7660 device is probed successfully.\n");

#if	0
	set_mod(1);
#endif

	return 0;

error_rm_poll_dev:
	input_unregister_polled_device(mma7660_idev);
error_free_poll_dev:
	input_free_polled_device(mma7660_idev);
error_rm_hwmon_dev:
	hwmon_device_unregister(hwmon_dev);
error_rm_dev_file:
	sysfs_remove_group(&client->dev.kobj, &mma7660_group);
error_init_client:
	mma7660_client = NULL;

	return 0;
}

上面 的过程简单总结就是,初始化input_dev参数,然后在 input_handler_list 匹配到 evdev_handler ,然后创建 evdev 结构体,然后将 input_dev 和 evdev_handler 都挂接到 evdev->handle ,注册 evdev->handle 并在 /dev/ 目录下创建 eventX 设备文件,至此,用户空间可以对 eventx 进行 open 和 read 操作读到数据,最终调用到的 是 evdev_handler->fops 中的 open 和 read 函数。但是上面的代码还有一些不明确
1、就是周期性的 任务 是什么时候开始的,上面代码只初始化了定时器和周期性调用的函数 mma7660_dev_poll ,并不知道什么时候开始调用
2.如何上报数据的

先可以大胆猜测:
1. 当 open eventx的时候,周期性任务启动,并调用 mma7660_dev_poll
2. mma7660_dev_poll 中有接口将采集到的数据放到一个共享数据区,然后 read 的时候读取该数据区

下面先分析 evdev_open函数

static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev;
    /*声明了新的结构体类型 evdev_client 下面分析其用途*/
	struct evdev_client *client;
    /*由次设备号获取设备编号*/
	int i = iminor(inode) - EVDEV_MINOR_BASE;
    /*上报数据所需要的 bufsize*/
	unsigned int bufsize;
	int error;

	if (i >= EVDEV_MINORS)
		return -ENODEV;

	error = mutex_lock_interruptible(&evdev_table_mutex);
	if (error)
		return error;
    /*还记得前面将初始化完成的evdev 赋值个 evdev_table[evdev->minor] = evdev
    现在又取出来了*/
	evdev = evdev_table[i];
	if (evdev)
		get_device(&evdev->dev);
	mutex_unlock(&evdev_table_mutex);

	if (!evdev)
		return -ENODEV;
    /* 计算 该 input_dev所需要的 缓存 大小*/
	bufsize = evdev_compute_buffer_size(evdev->handle.dev);
                {
                	unsigned int n_events =
                		max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS,
                		    EVDEV_MIN_BUFFER_SIZE);

                	return roundup_pow_of_two(n_events);
                }

	client = kzalloc(sizeof(struct evdev_client) +
				bufsize * sizeof(struct input_event),
			 GFP_KERNEL);
	if (!client) {
		error = -ENOMEM;
		goto err_put_evdev;
	}
  
	client->bufsize = bufsize;
	spin_lock_init(&client->buffer_lock);
	snprintf(client->name, sizeof(client->name), "%s-%d",
			dev_name(&evdev->dev), task_tgid_vnr(current));
    /*赋值client->evdev = evdev ,从这也可以看出,后面要以  client为操作对象了*/
	client->evdev = evdev;
	evdev_attach_client(evdev, client);
    /*就是这里使能定时器任务的*/
	error = evdev_open_device(evdev);
            {
            	int retval;

            	retval = mutex_lock_interruptible(&evdev->mutex);
            	if (retval)
            		return retval;

            	if (!evdev->exist)
            		retval = -ENODEV;
            	else if (!evdev->open++) {
            		retval = input_open_device(&evdev->handle);
                            {
                            	struct input_dev *dev = handle->dev;
                            	int retval;

                            	retval = mutex_lock_interruptible(&dev->mutex);
                            	if (retval)
                            		return retval;

                            	if (dev->going_away) {
                            		retval = -ENODEV;
                            		goto out;
                            	}

                            	handle->open++;
                                /* dev->users 未初始化,也就是为 0 ,这个表达式是只允许第一此打开此设备 调用 input_open_polled_device */
                            	if (!dev->users++ && dev->open)
                                    /*前面初始化 input->open = input_open_polled_device ,此处调用到 input_open_polled_device */
                            		retval = dev->open(dev);
                                            {
                                            	struct input_polled_dev *dev = input_get_drvdata(input);

                                            	if (dev->open)
                                            		dev->open(dev);

                                            	/* Only start polling if polling is enabled */
                                                /* 启动轮询调用 input_open_polled_device */
                                            	if (dev->poll_interval > 0)
                                            		queue_delayed_work(system_freezable_wq, &dev->work, 0);

                                            	return 0;
                                            }

                            	if (retval) {
                            		dev->users--;
                            		if (!--handle->open) {
                            			/*
                            			 * Make sure we are not delivering any more events
                            			 * through this handle
                            			 */
                            			synchronize_rcu();
                            		}
                            	}

                             out:
                            	mutex_unlock(&dev->mutex);
                            	return retval;
                            }
            		if (retval)
            			evdev->open--;
            	}

            	mutex_unlock(&evdev->mutex);
            	return retval;
            }
	if (error)
		goto err_free_client;
    /*linux 常用写法 ,后面就可以通过 file->private_data 获取 client */
	file->private_data = client;
	nonseekable_open(inode, file);

	return 0;

 err_free_client:
	evdev_detach_client(evdev, client);
	kfree(client);
 err_put_evdev:
	put_device(&evdev->dev);
	return error;
}

下面再看周期性报点的函数 mma7660_dev_poll

static void mma7660_dev_poll(struct input_polled_dev *dev)
{
	mma7660_report_abs();
    {
    	int axis[3];
    	int i;

    	for (i = 0; i < 3; i++) {
            /*通过 i2c 读取数据,不再详细分析*/
    		mma7660_read_xyz(mma7660_client, i, &axis[i]);
    	}
        /*报告数据*/
    	input_report_abs(mma7660_idev->input, ABS_X, axis[0]);
        {
            /*封装的 input_event ,其它几种上报数据函数也是封装的 input_event 
         下面就详细分析*/
        	input_event(dev, EV_ABS, code, value);
            {
            	unsigned long flags;
                /*检查是否支持 EV_ABS ,还记得初始化的时候设置为EV_ABS*/
            	if (is_event_supported(type, dev->evbit, EV_MAX)) {

            		spin_lock_irqsave(&dev->event_lock, flags);
                    /*为熵池子增加样本,具体还没研究过*/
            		add_input_randomness(type, code, value);
                    
            		input_handle_event(dev, type, code, value);
                    {
                    	int disposition = INPUT_IGNORE_EVENT;

                    	switch (type) {

                    	......
                        /*其它类型的事件类似,只分析 EV_ABS 类型 */

                    	case EV_ABS:
                    		if (is_event_supported(code, dev->absbit, ABS_MAX))
                    			disposition = input_handle_abs_event(dev, code, &value);
                                                {
                                                	bool is_mt_event;
                                                	int *pold;
                                                    /*mma7660 不是触摸屏,code = ABS_X */
                                                	if (code == ABS_MT_SLOT) {
                                                		/*
                                                		 * "Stage" the event; we'll flush it later, when we
                                                		 * get actual touch data.
                                                		 */
                                                		if (*pval >= 0 && *pval < dev->mtsize)
                                                			dev->slot = *pval;
                                                		else
                                                			printk(KERN_DEBUG "[TSP] dev->slot[%d], pval[%d]\n",\
                                                				 dev->slot, *pval);


                                                		return INPUT_IGNORE_EVENT;
                                                	}
                                                    /* code = ABS_X = 0, 所以 is_mt_event = 0 */
                                                	is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST;
                                                    /*进此if分支*/
                                                	if (!is_mt_event) {
                                                		pold = &dev->absinfo[code].value;
                                                	} else if (dev->mt) {
                                                		struct input_mt_slot *mtslot = &dev->mt[dev->slot];
                                                		pold = &mtslot->abs[code - ABS_MT_FIRST];
                                                	} else {
                                                		/*
                                                		 * Bypass filtering for multi-touch events when
                                                		 * not employing slots.
                                                		 */
                                                		pold = NULL;
                                                	}
                                                    /* fuzz被初始化为 4 ,进行简单的滤波 */
                                                	if (pold) {
                                                		*pval = input_defuzz_abs_event(*pval, *pold,dev->absinfo[code].fuzz);
                                                                {
                                                                	if (fuzz) {
                                                                		if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
                                                                			return old_val;

                                                                		if (value > old_val - fuzz && value < old_val + fuzz)
                                                                			return (old_val * 3 + value) / 4;

                                                                		if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
                                                                			return (old_val + value) / 2;
                                                                	}

                                                                	return value;
                                                                }
                                                		if (*pold == *pval)
                                                			return INPUT_IGNORE_EVENT;

                                                		*pold = *pval;
                                                	}

                                                	/* Flush pending "slot" event */
                                                	if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
                                                		input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
                                                		input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
                                                	}

                                                	return INPUT_PASS_TO_HANDLERS;
                                                }

                    		break;

                    	......
                    	}
                        
                    	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
                    		dev->sync = false;

                    	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
                    		dev->event(dev, type, code, value);
                        
                        /* 上面的结果 disposition = INPUT_PASS_TO_HANDLERS */
                    	if (disposition & INPUT_PASS_TO_HANDLERS)
                    		input_pass_event(dev, type, code, value);
                            {
                            	struct input_handler *handler;
                            	struct input_handle *handle;

                            	rcu_read_lock();
                                /* handle = dev->grab = NULL*/
                            	handle = rcu_dereference(dev->grab);
                               
                            	if (handle)
                            		handle->handler->event(handle, type, code, value);
                            	else {
                            		bool filtered = false;

                            		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
                            			if (!handle->open)
                            				continue;
                                        
                            			handler = handle->handler;
                                        /* evdev_handler->filter = NULL */
                            			if (!handler->filter) {
                            				if (filtered)
                            					break;
                                             /* 定义时初始化 evdev_handler->event       = evdev_event*/
                            				handler->event(handle, type, code, value);
                                            {
                                            	struct evdev *evdev = handle->private;
                                            	struct evdev_client *client;
                                            	struct input_event event;
                                            	struct timespec ts;
                                                /*将上报的数值封装成 input_event 类型*/
                                            	ktime_get_ts(&ts);
                                            	event.time.tv_sec = ts.tv_sec;
                                            	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
                                            	event.type = type;
                                            	event.code = code;
                                            	event.value = value;

                                            	rcu_read_lock();
                                                /* client = evdev->grab = NULL */
                                            	client = rcu_dereference(evdev->grab);
                                            	if (client)
                                            		evdev_pass_event(client, &event);
                                            	else
                                            		list_for_each_entry_rcu(client, &evdev->client_list, node)
                                            			evdev_pass_event(client, &event);
                                                        {
                                                        	/* Interrupts are disabled, just acquire the lock. */
                                                        	spin_lock(&client->buffer_lock);
                                                            /*终于将 event 存入了 client->buffer ,read 函数肯定要从 client->buffer 读取值*/
                                                        	client->buffer[client->head++] = *event;
                                                            /*加入一个 event 自然 client->buffer 就少一个空间*/
                                                        	client->head &= client->bufsize - 1;
                                                            /* client->head == client->tail 说明 client->buffer 满了,开始drop*/
                                                        	if (unlikely(client->head == client->tail)) {
                                                        		/*
                                                        		 * This effectively "drops" all unconsumed events, leaving
                                                        		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
                                                        		 */
                                                        		client->tail = (client->head - 2) & (client->bufsize - 1);

                                                        		client->buffer[client->tail].time = event->time;
                                                        		client->buffer[client->tail].type = EV_SYN;
                                                        		client->buffer[client->tail].code = SYN_DROPPED;
                                                        		client->buffer[client->tail].value = 0;

                                                        		client->packet_head = client->tail;
                                                        		if (client->use_wake_lock)
                                                        			wake_unlock(&client->wake_lock);
                                                        	}

                                                        	if (event->type == EV_SYN && event->code == SYN_REPORT) {
                                                        		client->packet_head = client->head;
                                                        		if (client->use_wake_lock)
                                                        			wake_lock(&client->wake_lock);
                                                        		kill_fasync(&client->fasync, SIGIO, POLL_IN);
                                                        	}

                                                        	spin_unlock(&client->buffer_lock);
                                                        }

                                            	rcu_read_unlock();
                                                /* 唤醒等待队列上的任务,这里type和code都不满足 */
                                            	if (type == EV_SYN && code == SYN_REPORT)
                                            		wake_up_interruptible(&evdev->wait);
                                            }

                            			} 
                                        else if (handler->filter(handle, type, code, value))
                            				filtered = true;
                            		}
                            	}

                            	rcu_read_unlock();
                            }
                    }
            		spin_unlock_irqrestore(&dev->event_lock, flags);
            	}
            }
        }
    	input_report_abs(mma7660_idev->input, ABS_Y, axis[1]);
    	input_report_abs(mma7660_idev->input, ABS_Z, axis[2]);
        /* 唤醒等待队列上的任务 */
    	input_sync(mma7660_idev->input);
        {
        	input_event(dev, EV_SYN, SYN_REPORT, 0);
        }

    	//printk("3-Axis ... %3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
    }
}

简单说上面的过程就是,将要报的数据封装成 input_event 类型,然后存入 client->buffer ,然后通过 input_sync 唤醒等待队列上的任务。在此也可以大胆猜测,read 函数就是从 client->buffer 读取数据,如果读取不到则再等待队列休眠。
下面简单看下 evdev_read 函数

static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
    /* open 的时候将 client 赋值给了 file->private_data */
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	int retval = 0;

	if (count < input_event_size())
		return -EINVAL;
   /*以阻塞的方式打开,等待事件*/
	if (!(file->f_flags & O_NONBLOCK)) {
		retval = wait_event_interruptible(evdev->wait,
			 client->packet_head != client->tail || !evdev->exist);
		if (retval)
			return retval;
	}

	if (!evdev->exist)
		return -ENODEV;
    /*拷贝到用户空间*/
	while (retval + input_event_size() <= count &&
	       evdev_fetch_next_event(client, &event)) {

		if (input_event_to_user(buffer + retval, &event))
			return -EFAULT;

		retval += input_event_size();
	}

	if (retval == 0 && file->f_flags & O_NONBLOCK)
		retval = -EAGAIN;
	return retval;
}

三、杂谈

上面的代码还是比较简单,只是从 mma7660 里读值上报。上面的代码中也有一段专门处理点触摸屏的代码,多点触摸屏的报点要相对复杂一点,
不像 mma7660 有数据上报就行了,多点触摸屏需要能够区分一个触摸点,否则的话如何知道是一点手指的多个数据还是多个手指的数据?上面对
slot 的判断,就是处理多点触控的一种机制,上报触电数据前,先上报 slot 再报数据,下面是两种报多点触控的大致顺序:
A类型:
ABS_MT_POSITION_X x[0]
 
ABS_MT_POSITION_Y y[0]

SYN_MT_REPORT    //以此为标识作为一个触控点

ABS_MT_POSITION_X x[1]

ABS_MT_POSITION_Y y[1]

SYN_MT_REPORT  //以此为标识作为第二个触控点

SYN_REPORT    //最后以同步事件结束


B类型:

ABS_MT_SLOT 0
 
ABS_MT_TRACKING_ID 33  //根据 ID 判断属于哪条线

ABS_MT_POSITION_X x[0]

ABS_MT_POSITION_Y y[0]

ABS_MT_SLOT 1

ABS_MT_TRACKING_ID 34 //根据 ID 判断属于哪条线

ABS_MT_POSITION_X x[1]

ABS_MT_POSITION_Y y[1]

SYN_REPORT
 
关于 input 的 A 和 B 协议这里不详细分析。


在看linux 4.4 代码的时候,发现出现了新的目录 iio Industrial I/O subsystem ,看下面的目录可以看到 light 和 imu 等目录,很明显,对原来都用于 input 的一些传感器,提供了更加详细的分类,提供了新的 iio 框架,如果在linux 4.4里面,mma7660 套用 iio 架构更合适一些,这些都不再分析,万变不离其宗。

你可能感兴趣的:(嵌入式软件开发之------浅析linux驱动模型(六)input框架)