当前位置: 代码迷 >> 综合 >> linux输入子系统——深入分析(一)
  详细解决方案

linux输入子系统——深入分析(一)

热度:72   发布时间:2023-12-22 05:10:26.0
在国嵌里我们已经讲解了关于input输入子系统的基础知识,在这里我们跟着韦东山老师在来深入理解关于input输入子系统相关的知识
在分析之前我们
1、 在input输入子系统体系结构里我们说过,其核心是input core,其实input core对应的具体文件就是input.c。那么我们就来分析一下吧:
/*file_operations的设置*/
static const struct file_operations input_fops =  {
.owner = THIS_MODULE,
.open = input_open_file,// 只有一个open函数,看来open函数肩负重任啊
};

static int __init input_init(void)
{
int err;

err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
return err;
}

err = input_proc_init();
if (err)
goto fail1;

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

return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
return err;
}
注释1:从上面我们可以看出,input.c里面完成的字符设备的注册,以及file_operations结构体的设置。看来input子系统设计的驱动程序也属于字符设备驱动程序,与一般的字符设备驱动程序的不同之处在于,input子系统字符设备的注册由内核自动完成,不需要程序员来完成,程序员要做的就是,将底层的硬件输入转化为统一事件形式,向输入核心层(input core)汇报。
2、我们看到在f ile_operations结构体里只有一个open函数,如果要想读设备或则写设备怎么办呢?这说明pen函数肩负着艰巨的使命,这诱使我们去了解这个小强
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;

lock_kernel();
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];// 根据打开的文件的次设备号,来得到一个input_handler结构体,关于这个结构体请见注释1
if (!handler || !(new_fops = fops_get(handler->fops)))// 通过handler得到新的 file_operations结构体
 {
err = -ENODEV;
goto out;
}

/*
 * That's _really_ odd. Usually NULL ->open means "nothing special",
 * not "no device". Oh, well...
 */
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;// 保存文件之前的f_op
file->f_op = new_fops;// 将新的f_op赋给当前文件的f_op

err = new_fops->open(inode, file);// 调用open函数,当应用程序读的时候,最终会调用这个函数

if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
注释1:struct input_handler {

void *private;

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

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

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

struct list_head h_list;
struct list_head node;
};
这个结构体可以通过   input_table来获得,那么   input_table是何许人也呢?查了一下发现 input_table在input_register_handler 里被构造了:
input_table[handler->minor >> 5] = handler;
input_register_handler函数由诸如下面一些文件调用:
keyboard.c、mousedev.c、edev.c 等等
我们进入 edev.c来看一看,发下了重要线索:
/*这里就是 file_operations结构体了 */
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};
.............................................................
.............................................................
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,// 用来表示是否支持该设备,这里表示可以支持任何设备
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
有点乱对吧!先来理一下:
在edev.c中构造了input_handler结构体(当然在这个结构体里包含了file_operation结构体),并向input.c注册这个结构体。在input.c的函数input_register_handler中根据该结构体构造了input_table:input_table[handler->minor >> 5] = handler,而在 input_open_file函数中,又通过input_table构造了一个input_handler结构体handler = input_table[iminor(inode) >> 5];这样看input_table起到一个中转作用。然后通过handler构造了新的file_operation结构体new_fops = fops_get(handler->fops。OK!
3、上面分析了input core的情况,下面我们从最底层的驱动来一步一步分析:
(1)在入口函数中注册input设备: input_register_device(button_dev);
(2)注册函数定义在input.c中:
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;

__set_bit(EV_SYN, dev->evbit);

/*
 * 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;

snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

error = device_add(&dev->dev);
if (error)
return error;

path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %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设备放入一个链表中

list_for_each_entry(handler, &input_handler_list, node) //对于一个input设备,使其与input_handler_list链表中的每一个handler都比较一下,在                                                                                                     //注册input_handler结构体时,会将该input_handler结构体放入链表中的,见注释
input_attach_handler(dev, handler);// 根据id.table比较来判断handler是否支持设备

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

return 0;
}
EXPORT_SYMBOL(input_register_device);
注释:注册input_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加入链表input_handler_list
list_for_each_entry(dev, &input_dev_list, node)// 对于每一个input设备进行比较
input_attach_handler(dev, handler);// 判断是否匹配

input_wakeup_procfs_readers();

out:
mutex_unlock(&input_mutex);
return retval;
}
综上:注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler, 根据input_handler的id_table判断这个input_handler能否支持这个input_dev, 如果能支持,则调用input_handler的connect函数建立"连接"
(3)那么这connect函数是怎么一回事儿呢? 让源码说话:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,  const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;

for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;

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

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);// 分配一个struct evdev结构体,然后设置
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);

snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;

evdev->handle.dev = input_get_device(dev);// input设备放进去
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;// handler处理函数放进去
evdev->handle.private = evdev;

strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);

error = input_register_handle(&evdev->handle);// 注册handle,我们可以走进去,见注释1
if (error)
goto err_free_evdev;

error = evdev_install_chrdev(evdev);
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;
}
注释1:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;// handler处理函数拿出来
struct input_dev *dev = handle->dev;// input设备拿出来
int error;

/*
 * We take dev->mutex here to prevent race with
 * input_release_device().
 */
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);// handle加入到dev->h_list链表中
mutex_unlock(&dev->mutex);
synchronize_rcu();

/*
 * 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.
 */
list_add_tail(&handle->h_node, &handler->h_list);// handle加入到handler->h_list链表中

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

return 0;
}

现在我们就清楚了,如果handler支持input设备,通过connect的连接,input_device可以根据其h_list来找到handle,然后根据handle找到相应的处理函数。反之,处理函数也一样。
我们可以整理出来框架:
evdev.c文件中:
input_register_handler(&evdev_handler); //evdev_handler里面定义了event、connect和fops等一些东东
          input_table[handler->minor >> 5] = handler; //将根据次设备号将handler放入数组中
          list_add_tail(&handler->node, &input_handler_list); //将handler加入链表input_handler_list
          list_for_each_entry(dev, &input_dev_list, node) //遍历input_handler_list中的所有项
                    input_attach_handler(dev, handler); //判断设备与处理函数是否匹配
                            id = input_match_device(handler->id_table, dev); //根据handler->id_table判断设备与处理函数是否匹配
                            if (!id)
                            return -ENODEV;
                            handler->connect(handler, dev, id); //如果匹配就调用handler->connect函数
                                      evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
                                      evdev->handle.dev = dev; //将dev放进去
                                      evdev->handle.handler = handler; //将handler放进去
                                      input_register_handle(&evdev->handle);
                                                struct input_handler *handler = handle->handler;
                                                list_add_tail(&handle->d_node, &handle->dev->h_list); //handle加入到dev->h_list链表中
                                                list_add_tail(&handle->h_node, &handler->h_list); //handle加入到handler->h_list链表中

input_register_device(input);
          list_add_tail(&dev->node, &input_dev_list); //将dev将加入input_dev_list列表中
          list_for_each_entry(handler, &input_handler_list, node) //遍历所有的handler判断是否支持设备
          input_attach_handler(dev, handler); //判断设备与处理函数是否匹配
           接下来的分析跟上面完全一致,我们不再分析
这样处理函数和设备就联系起来了。我们再来啰嗦一下:我们在注册设备的时候会有次设备号,而注册处理函数的时候又会根据这个次设备号来将处理函数放入数组中,然后在对设备进行操作的时候,根据次设备号来找到操作函数。这样我们就对应起来了。
                                                            
input.c文件中:
 register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
input_fops里面只有open函数,在open函数中会完成取出操作函数的工作。