首页 > 代码库 > S3C2440 输入子系统学习笔记 第一节

S3C2440 输入子系统学习笔记 第一节

  接触S3C2440已经有一段时间了,可是总是没有去坚持学习,刚毕业的我深受到自身技能的缺乏和工作中的压力,决定痛改前非,坚持每天下班都去学习,在这里我不敢说自己能把2440学完,因为技术永无止境。但是我相信我能一直坚持下去。我是一个热爱思考,并且将思考的东西通过各种方法实现,我要做我思想的造物主。博客从今天开始每隔一天将会有新的博客,前期会根据韦东山老师的视频教程目录来更新,如果有写得不好的地方请大家指点指点。

  好了废话不多说,进入正题。

  本博客的起点是韦东山老师的第2期的学习视频。  在第一期视频中,我们学到了简单的驱动结构,比如 LED驱动, 按键驱动等,但是这些驱动仅仅适合我们平时使用,没有办法让别人去调用,可如果要写通用的驱动程序的话,就要使用现成的驱动——input 输入子系统。

  首先回顾一下以前,按照韦老大的方式写一个驱动框架:

  1、确定主设备好

  2、构造一个file_operations结构体 结构体中包含有常用的接口, 如open、 write、 read 等等

  3、接下来时注册一个字符设备驱动程序(register_chrdev)

  4、入口函数、 出口函数、 加修饰。

  在输入子系统中大体也是这个框架,区别在于在输入子系统中这些框架是内核驱动开发这写好的。我们要做的就是把我们的代码融合到里边去。

  输入子系统的核心时input.c(目录: /linux/driver/input/input.c) 现在我们开始分析系统框架:

  输入子系统的核心是input.c 我们从入口函数着手 代码如下,我们看到入口函数中的 err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 这是注册一个名为input的设备,设备号为INPUT_MAJOR, input_fops是函数调用接口。

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;
}

  接下来我们来看input_fops提供了哪些调用接口呢?

static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,//这里边仅仅提供了一个open接口,这样的接口函数仅仅存在一个接口, 猜测input_open_file 中应该有隐含的接口
};

  再看看input_open_file函数。

static int input_open_file(struct inode *inode, struct file *file)
{
    struct input_handler *handler = input_table[iminor(inode) >> 5];  //根据打开文件的次设备号得到一个input_handler
    const struct file_operations *old_fops, *new_fops = NULL;
    int err;

    /* No load-on-demand here? */
    if (!handler || !(new_fops = fops_get(handler->fops)))   //新的fileoperlations 结构体 = handler->fops
        return -ENODEV;

    /*
     * That‘s _really_ odd. Usually NULL ->open means "nothing special",
     * not "no device". Oh, well...
     */
    if (!new_fops->open) {
        fops_put(new_fops);
        return -ENODEV;
    }
    old_fops = file->f_op;
    file->f_op = new_fops;  //将新文件的f_ops 赋给 文件的f_op  ?????  没理解。。。
    err = new_fops->open(inode, file);   //最后调用文件的open 函数; 最终应用程序read 函数调用的是 file->f_op->read函数.
    if (err) {
        fops_put(file->f_op);
        file->f_op = fops_get(old_fops);
    }
    fops_put(old_fops);
    return err;
}

  input_table是由input_handler 定义来的数组.

static struct input_handler *input_table[8]



注册input_handler 做的事情
int input_register_handler(struct input_handler *handler)
{
    struct input_dev *dev;

    INIT_LIST_HEAD(&handler->h_list);

    if (handler->fops != NULL) {
        if (input_table[handler->minor >> 5])
            return -EBUSY;

        input_table[handler->minor >> 5] = handler; //将handler 放入数组中
    }

    list_add_tail(&handler->node, &input_handler_list);  // 放入链表中

    list_for_each_entry(dev, &input_dev_list, node)   //对于没个device_input 放入连表中
        input_attach_handler(dev, handler);

    input_wakeup_procfs_readers();
    return 0;

  接下来看看注册input_register_devide做的事情

int input_register_device(struct input_dev *dev)
{
    static atomic_t input_no = ATOMIC_INIT(0);
        。。。。
        。。。。。
   ist_add_tail(&dev->node, &input_dev_list); // 放入链表中
    。。。。。。
   list_for_each_entry(handler, &input_handler_list, node)  //对handler链表里边的每一个项目都调用input_attach_handler
        input_attach_handler(dev, handler);            //根据input_handler的id_table判断是否支持input_dev

  从input_register_device 和 input_register_handler中可以看出,不论我们先注册device 还是handler 最后都会调用到input_attach_handler。 继续向下分析input_attach_handler.

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

    if (handler->blacklist && input_match_device(handler->blacklist, dev))
        return -ENODEV;

    id = input_match_device(handler->id_table, dev);   //根据handler->id_table 和输入设备比较, 看看handler是支持,是否匹配
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);     //如果匹配就调用handler中的connect函数
    if (error && error != -ENODEV)
        printk(KERN_ERR
            "input: failed to attach handler %s to device %s, "
            "error: %d\n",
            handler->name, kobject_name(&dev->cdev.kobj), error);

    return error;

  小结: 注册input_dev 或input_handler时,会两两比较左边的和右边的input_handler,根据input_handler的id_table 判断input_handler是否能支持input_drv, 如果支持,则调用input_handler 的connect函数进行链接。

 

 

 

 

 

 

 

 

 

   

  

  

S3C2440 输入子系统学习笔记 第一节