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

发布时间:2023-08-10  

  接触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 classn");

        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: %dn",

            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函数进行链接。


下面了解一下时怎么建立链接的。


以evdev_connect为例 先进入evdev.c 找到evdev_handler结构体,代码如下:


static struct input_handler evdev_handler = {

    .event =    evdev_event,

    .connect =    evdev_connect,   //这是evdev的链接函数,进入evdev_connect函数static int evdev_connect

    .disconnect =    evdev_disconnect,

    .fops =        &evdev_fops,

    .minor =    EVDEV_MINOR_BASE,

    .name =        "evdev",

    .id_table =    evdev_ids,

};


static int evdev_connect(struct input_handler *handler, struct input_dev *dev,

             const struct input_device_id *id)

{

    struct evdev *evdev;

    struct class_device *cdev;

  .....

  .....

  evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);  //分配一个input_handle

  //设置

  evdev->handle.dev = dev;    //指向左边的input_dev

    evdev->handle.name = evdev->name; 

    evdev->handle.handler = handler;  //指向右边的input_handler

    evdev->handle.private = evdev;

  //注册

  error = input_register_handle(&evdev->handle);


  // 跟进input_register_handle


  int input_register_handle(struct input_handle *handle)

{

    struct input_handler *handler = handle->handler;


    list_add_tail(&handle->d_node, &handle->dev->h_list);  //将设备和input_handle->dev建立关系

    list_add_tail(&handle->h_node, &handler->h_list);    //将系统的handler_list和input_handle结构体挂钩,

                                   //这样就可以通过input_handle结构体将设备和系统联系到一起


    if (handler->start)

        handler->start(handle);


    return 0;

}

 小结: 1、分配一个inout_hanfle结构体

    2、 input_handle.dev = input_dev;    //指向右边的input_dev

      input_handle.handler = input_handler;   //指向右边的input_handler

    3、注册

      input_handler->h_list = &input_handle;

      input_dev->h_list     = &input)handle;


数据的读取:


应用程序读取程序通过read函数读取数据的时候,会调用到evdev_handler(仅仅时以evdev为例)


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 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,



static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

{

    struct evdev_client *client = file->private_data;

    struct evdev *evdev = client->evdev;

    int retval;


    if (count < evdev_event_size())

        return -EINVAL;

  //如果没有数据并且是非阻塞的方式打开,则立刻返回

    if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) 

        return -EAGAIN;

  //休眠

    retval = wait_event_interruptible(evdev->wait,

        client->head != client->tail || !evdev->exist);





static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)

{

    struct evdev *evdev = handle->private;

    struct evdev_client *client;

.....

.....

  wake_up_interruptible(&evdev->wait); //唤醒操作

}


那么evdev_event 被谁调用呢? 猜测时硬件相关的代码,input_dev调用的, 在设备的中断服务程序中,确定是什么事件,然后再调用相应的input_handler 的event处理函数


以 gpio_key_isr为例


static irqreturn_t gpio_keys_isr(int irq, void *dev_id)

{

    int i;

    struct platform_device *pdev = dev_id;

    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>