字符设备驱动之输入子系统分析(二)

这篇具有很好参考价值的文章主要介绍了字符设备驱动之输入子系统分析(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者:Bright-Ho

联系方式:836665637@qq.com

input输入子系统框架分析(纯软件方面):

 文章来源地址https://www.toymoban.com/news/detail-550497.html

上一节,我们简单的描述的什么是输入子系统什么是字符设备以及其作用;重点是我们讲到分析输入子系统必须结合硬件设备来分析;那么这一节,我们主要讲解输入子系统的软件框架;接下来,我们就进入主题;

那么在进入主题之前,我们先来了解一个简单的字符设备驱动程序的框架;

1构造一个file_operations结构体;里面实现了read,write,open等函数,用于应用层调用;也就是给应用层提供接口;

2通过 register_chrdev()函数来注册驱动程序;所谓注册,就是以主设备号为下标,把file_operations结构体放入一个内核数组;

3)通过udev机制,自动创建设备节点,通过下面两个函数来实现;

class_create(THIS_MODULE,"firstdrv");先创建一个类firstdrv,会在sys/class下生成;

class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");在类下面创建一个xyz设备;

4)通过void first_drv_exit(void)函数,对所有资源进行卸载;

5)修饰驱动程序的入口函数;

  1. module_init(first_drv_init); /*加载驱动(insmod)的时候,会执行该宏*/

  2. module_exit(first_drv_exit); /*卸载驱动(rmmod)的时候,会执行该宏*/

注意,内核版本不一样, class_create() class_device_create()这两函数也不一样,但实际效果是一样的;

这上面5个步骤就是一个简单的字符设备框架,不涉及字符设备的高级技巧;也不对应任何具体设备;就单单是一个简单字符设备的框架;

上面这5部需要理解两个问题:

  1. 从应用层的角度来看,应用层调用read,write,open等函数,是如何调用到驱动程序里面的open,read,write等函数的?

  2. 就是所谓udev机制,需要知道怎么使用内核提供的函数来自动创建设备节点;

上面这种字符设备驱动框架有一个很大缺点它提供给应用程序的接口,也就是设备节点,只有自己清楚,或者只有自己公司的人知道它所提供的接口,别人不知到!所以它不是一个通用的设备驱动程序;这是引入udev机制的原因

接下来,我们就要真正的讲解输入子系统了;

我一直认为内核就是最好的老师,所以学习输入子系统,就得去分析内核源代码;我采用的内核版本为2.6.22.6的版本,该版本有点老,有些东西和新版本内核不一样,但是对于学习来说没什么太大影响;如果对新老版本都了解的话,那就更好了;那么,在学习输入子系统之前,需要牢记一个问题,加入输入子系统的字符设备驱动的步骤和上面没有加入输入子系统的字符设备框架的5个步骤有什么区别?这个问题,只有分析了内核自带的输入子系统源码,你才能知道其中的区别;弄清楚了这个问题之后,你应该清楚,哪些事情是由内核帮我们实现的,哪些事情是需要我们自己完成的;

input输入子系统的内核源码位于:linux-2.6.22.6/drivers/input下;

首先明确一点,输入子系统分为三层,核心层事件处理层设备硬件层

核心层(input.c):分析主要代码!!!

(1)入口函数input_init()

static int __init input_init(void)/*入口*/

{

int err;

 

err = class_register(&input_class); /*sys/class/input 创建设备类*/

err = input_proc_init();/* proc文件系统相关的初始化*/

err = register_chrdev(INPUT_MAJOR, "input", &input_fops); /*注册字符设备,majoy 13为索引,存放于把内核数组*/

...

}

input_fops结构体如下:

1279 static const struct file_operations input_fops = {

1280 .owner = THIS_MODULE,

1281 .open = input_open_file,

1282 };

<解析> 在入口函数中:

1. sys/class目录下,创建设备类,用于生成设备节点;

class_register(&input_class);

2. 注册字符设备驱动,以主设备号为下标,把input_fops 放入内核数组中,方便应用层通过主设备号索引;

register_chrdev(INPUT_MAJOR, "input", &input_fops);

注意:该函数执行后会在proc/devices里面生成设备信息;

问题我们知道应用程序调用open,read,write函数来操作设备,最终会调用到驱动程序里面所提供的open_drv,read_drv,write_drv函数,但是input_fops结构里面只提供了open函数,那么这是怎么回事呢?所以接下来,继续分析input_open_file函数;

2input_open_file函数分析:

static int input_open_file(struct inode *inode, struct file *file)

1245 {

1246 /*1input_table数组中以次设备号为下标,取出一项给handler*/

1247 struct input_handler *handler = input_table[iminor(inode) >> 5];

1248 const struct file_operations *old_fops, *new_fops = NULL;

1252 /*2handler里面的fops赋给new_fops*/

1253 if (!handler || !(new_fops = fops_get(handler->fops)))

1264 /*3new_fops赋给file_f_op,实际上也就是handler里面的fops*/

1265 old_fops = file->f_op;

1266 file->f_op = new_fops;

1267

1268 /*4调用new_fops里面的open函数*/

1269 err = new_fops->open(inode, file);

...

}

 

<解析>在分析input_open_file函数之前,有一个非常重要的结构体需要了解!!!

struct input_handler *handler

1065 struct input_handler {

1066

1067 void *private;

1069 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

1070 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

1071 void (*disconnect)(struct input_handle *handle);

1072 void (*start)(struct input_handle *handle);

1074 const struct file_operations *fops;

1075 int minor;

1076 const char *name;

1078 const struct input_device_id *id_table;

1079 const struct input_device_id *blacklist;

1081 struct list_head h_list;

1082 struct list_head node;

1083 };

input_handler结构体在核心层引用,那么谁来初始化该结构体呢?后面会分析到;

  1. input_table数组中,通过次设备号找到一项,赋给input_handler 指针;

    struct input_handler *handler = input_table[iminor(inode) >> 5];

  2. handler->fops赋给file_operations 指针;

    new_fops = fops_get(handler->fops)

  3. 调用new_fops里面的open函数

    new_fops->open(inode, file)

  4. 如果read的话,最终会调用到 file->f_op中的read函数;

实际上,最终调用的open函数,是input_handler里面的open函数;input_handler是从input_table数组中得到的;那么归根结底的问题就是input_table数组是由谁构造的

3input_table数组是由谁构造的?

搜索“input_table”字段,可知:

static struct input_handler *input_table[8];

它是一个静态结构体指针数组,说明只能在本文件使用;

最终在input_register_handler(struct input_handler *handler) 函数中找到该“input_table”数组,在这里被赋值的;

所以接下来得分析input_register_handler()函数:

源码如下:

1182 int input_register_handler(struct input_handler *handler)

1183 {

1184 struct input_dev *dev;

1185

1186 INIT_LIST_HEAD(&handler->h_list);

1187

1188 if (handler->fops != NULL) {

1189 if (input_table[handler->minor >> 5])

1190 return -EBUSY;

1191 /*1把传进来的handler保存在input_table数组中*/

1192 input_table[handler->minor >> 5] = handler;

1193 }

1194

1195 /*2handler放入handler链表*/

1196 list_add_tail(&handler->node, &input_handler_list);

1197 /*3把设备链表的每项,与handler比较看是否匹配*/

1198 list_for_each_entry(dev, &input_dev_list, node)

1199 input_attach_handler(dev, handler);

1200

1201 input_wakeup_procfs_readers();

1202 return 0;

1203 }

input_table[]数组是通过input_register_handler()函数的参数,来赋值的;也就是说谁调用该函数,就是谁传的这个input_handler结构体;

  1. input_register_handler()函数把这个结构体,存放于input_table数组;

  2. 并且把这个input_handler结构体放入handler链表

  3. 把设备链表的每项取出来,与handler比较看是否匹配;

    list_for_each_entry(dev, &input_dev_list, node)

    input_attach_handler(dev, handler);

    input_attach_handler()函数源码如下:

    static int input_attach_handler(struct inpuft_dev *dev, struct input_handler *handler)

    {

    const struct input_device_id *id;

    id = input_match_device(handler->id_table, dev);

    error = handler->connect(handler, dev, id);

    ...

    return error;

    }

    匹配设备和handlerid,匹配成功则调用handler->connect(handler, dev, id);

分析到这里,关键是看谁调用input_register_handler()函数;

 

遍历所有内核源代码:

input_register_handler()函数;evdev.c(事件设备)tsdev.c(触摸屏设备)keyborad.c(键盘设备),以及mousedev.c(鼠标设备)等;都调用了该注册函数;

实际上我们这里就进入了所谓的“事件处理层”了!!!

所以说目前对于事件处理层来说,核心层做了如下几件事情:

    1. 提供了用于事件处理层的注册接口,也就是input_register_handler()函数;

    2. 提供了 input_match_device()匹配函数,通过id号来匹配设备,一旦匹配成功,则调用input_handler->connect函数;

      注意:这个connect函数是由“事件处理层”实现的;

    3. 把事件处理层所提供的input_handler->fops通过register_chrdev()函数,放入内核数组,为应用层提供设备接口;

上面我们一直讲到input_handler这个结构体,它是在核心层里面声明,定义的,至于在哪里初始化的?就是“事件处理层”的事情了,“事件处理层”中会初始化这个结构体,并提交给核心层;

所以接下来,下一节我们具体的分析一个事件设备;

 

 

 

到了这里,关于字符设备驱动之输入子系统分析(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux Mii management/mdio子系统分析之二 mdio总线-设备-驱动模型分析

    (转载)原文链接:https://blog.csdn.net/u014044624/article/details/123303139       接着上篇文章继续分析mdio子系统,本章主要介绍mdio子系统的驱动模型,当然了介绍mdio子系统的驱动模型,就绕不开linux系统设备-总线-驱动模型,所有的总线类的驱动,基本上都可以理解为继承自linux系

    2024年01月17日
    浏览(35)
  • 【智能家居项目】裸机版本——网卡设备接入输入子系统 | 业务子系统 | 整体效果展示

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 网络子系统实现了,在我们整个项目框架中,网络子系统也输入子系统中输入设备的之一,所以现在要做的就是网络子系统接入到输入子系统中。 如上图所示,在输入子系统中增

    2024年02月08日
    浏览(35)
  • Linux驱动之input输入子系统

    目录 前言: 介绍: input_dev结构体: 输入子系统的使用流程: 实例测试 : 前言: 输入子系统用于实现Linux系统输入设备(鼠标 键盘 触摸屏  游戏杆)驱动的一种框架。Linux内核将其中的固定部分放入内核,驱动开发时只需要实现其中的不固定部分(主要还是和硬件相关的部分

    2024年02月15日
    浏览(29)
  • 迅为RK3568开发板驱动开发指南-输入子系统

    《iTOP-RK3568开发板驱动开发指南》 更新,本次更新内容对应的是驱动 (第十三篇 输入子系统) 视频,帮助用户快速入门,大大提升研发速度。 第13篇-输入子系统 目录 第1篇 驱动基础篇 第2篇 字符设备基础 第3篇 并发与竞争 第4篇 高级字符设备进阶 第5篇 中断 第6篇 平台总

    2024年03月26日
    浏览(35)
  • 操作系统实验·字符设备驱动程序

    编写一个简单的字符设备驱动程序,该字符设备并不驱动特定的硬件, 而是用内核空间模拟字符设备,要求该字符设备包括以下几个基本操作,打开、读、写和释放,并编写测试程序用于测试所编写的字符设备驱动程序。在此基础上,编写程序实现对该字符设备的同步操作。

    2024年02月10日
    浏览(45)
  • 驱动开发--字符驱动设备2

    字符设备驱动 1.定义 以字节流的形式进行访问,且只能顺序访问的设备,针对字符设备编写的驱动叫做字符设备驱动 2.字符设备框架 用户空间通过IO函数如open、read、write、close等函数接口,调用内核空间中的字符设备驱动函数中的用户自定义的open、read、write、close等函数,通

    2024年02月15日
    浏览(34)
  • 字符设备驱动实例(ADC驱动)

            ADC是将模拟信号转换为数字信号的转换器,在 Exynos4412 上有一个ADC,其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz,转换速率最高为1MSPS (4)具有四路模拟输入,同一时刻只有一路进行转换 (5) 转换完成后可以产生中断。

    2024年02月11日
    浏览(29)
  • 嵌入式Linux(8):字符设备驱动--注册字符类设备

    杂项设备 注册杂项设备: 注销杂项设备: 字符类设备 文件:include/linux/cdev.h 步骤流程: 定义一个cdev结构体。 使用cdev_init函数初始化cdev结构体成员变量。 参数: 第一个:要初始化的cdev结构体 第二个:文件操作集: cdev-ops = fops;//实际就是把文件操作集写ops 使用cdev_add函数

    2023年04月22日
    浏览(40)
  • Linux 驱动学习笔记 ——(1)字符设备驱动

    《【正点原子】I.MX6U嵌入式Linux驱动开发指南》学习笔记 字符设备是 Linux 驱动中最基本的一类设备驱动,字节设备就是按照字节流来读写的设备,常见的字符设备包括:LED、蜂鸣器、按键、I2C 以及 SPI 等。 Linux 中一切皆文件,字符设备驱动加载成功后会在 /dev 目录下生成相

    2024年02月08日
    浏览(42)
  • Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切,那痛苦就在人的心灵深处升起。——加缪 本章的目的是编写一个完整的字符设备驱动。我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备。字符驱动也比块驱动易于理解。本章的最终目的是编写一个模块化的字符驱动,但是我们不会在

    2024年02月08日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包