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 架构更合适一些,这些都不再分析,万变不离其宗。