08-S3C2440驱动学习(二)嵌入式linux-input子系统分析与应用

发布者:文江桂青最新更新时间:2022-05-26 来源: eefocus关键字:S3C2440  子系统 手机看文章 扫描二维码
随时随地手机看文章

零、几个问题

(1)为什么有输入子系统:


针对输入设备的多样性和输入事件的多样性,内核通过输入子系统来实现输入设备的驱动、输入事件的提交及对输入事件的读取,并有统一的命名规则。


(2)分层分离结构


input子系统是对不同类型的输入设备进行统一处理的驱动程序。一个输入事件,如按键,按照:


驱动层-->系统核心层-->事件处理层-->用户空间层


的顺序达到用户空间并传递给应用程序的。


input子系统组成


驱动层

核心层

事件处理层

(3)几个重要结构体


input_dev:物理输入设备结构体,包含设备信息。存在input_dev_list链表中


input_handler:事件处理结构体,实现事件处理逻辑。存在input_handler_list链表中


input_handle:建立input_dev与input_handler之间关系。input_handle与input_dev、input_handler之间关系的链表。


内核中,有很多写好的框架、例子等,理解这些框架的流程,把其头文件包含进来。调用系统框架的函数,来实现驱动等。以前自己写,只能是自己的人用,现在用内核现成的写好的,来修改。


(4)input子系统我们需要做什么?


系统核心层input、事件处理层handler已经被系统做好了,内核启动的时候会加载相关驱动。我们需要做的是注册设备层的驱动,注册过程被核心层知道后,会产生设备dev和handler处理者之间的连接,我们要做的就是按照input子系统的框架,写出对应的设备驱动。


(5)架构图



Linux输入子系统(Input Subsystem):


   在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。下面用图形来描述一下这三者的关系吧!

一、input子系统分析

1、以前我们写驱动

register_chrdev

file_operations

module_init

module_exit

缺点:不能用在现成的应用程序,别人的应用程序不可以用。别人可能打开tty,scanf,如何适应这种情况?


2、使用现成的驱动:输入子系统

输入子系统框架,也有以上的几点,只不过是别人写好的,我们只需要修改。


框架分析如下:Input.c (driversinput)核心层。


(1)看驱动程序从入口函数开始看:subsys_initcall(input_init);--》

static int __init input_init(void)

{

int err;


。。。

err = class_register(&input_class);


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

。。。


}


#define INPUT_MAJOR13    可以看出输入子系统的主设备号都是13


(2)查看file_operations 结构体


static const struct file_operations input_fops = {

.owner = THIS_MODULE,

.open = input_open_file,

};


只有一个open函数, 这个open肯定做了某些特殊事情。

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


{


。。。


//从input_table中获得一个input_handler结构体。input_handler结构体里面存有file_operations结构体,后面会介绍。

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

 //定义两个file_operations指针变量


const struct file_operations *old_fops, *new_fops = NULL;    

 


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

 return -ENODEV;

 

 old_fops = file->f_op;

 file->f_op = new_fops;

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


。。。


}


这个特殊的open中,根据打开文件的次设备号得到一个input_handler,从input_handler中得到应用程序的file_operations,进行打开。


(3)input_table在哪呢?



在input_register_handler中构造了这个数组项。


int input_register_handler(struct input_handler *handler)

{。。

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

}

(4)input_register_handler被谁调用呢?内核中搜索



(一些通用的设备函数调用,系统写好的处理函数)input_register_handler被input的下层调用。

因此是被下一层调用。

(5)以Evdev.c (driversinput)为例



static int __init evdev_init(void)


{


return   input_register_handler(&evdev_handler);


}


调用input_register_handler向上核心层注册

(6)evdev_handler是一个input_handler结构体被存到input_table中。



static struct input_handler evdev_handler ={undefined


       .event=  evdev_event,


       .connect=     evdev_connect,


       .disconnect= evdev_disconnect,


       .fops=           &evdev_fops,


       .minor= EVDEV_MINOR_BASE,


       .name=         "evdev",


       .id_table=     evdev_ids,


};


 input_register_handler(&evdev_handler);


.fops =           &evdev_fops     evdev_fops里面有各种函数 如open


evdev_fops中

.minor = EVDEV_MINOR_BASE,=64


因此input_register_handler中input_table[handler->minor >> 5] = handler; =input_table[2]=handler



evdev 64右移5放到input_table的第二项input_table[2]


evdev_handler是一个input_handler结构体。


假设应用程序要读:

(7)handler 是纯软件,事件处理层(EventHandler)


 struct input_handler evdev_handler中有一个id_table,


.id_table =     evdev_ids,表示这个hander能支持哪些设备。


(8)还有一层叫设备驱动层。


看一下这个hander


       .id_table=     evdev_ids, 表示这个hander能支持哪些设备。


能支持会调用.connect =     evdev_connect,函数 ,建立一个连接.


(9)搜索下谁会调用 input_register_device,很多按键  鼠标  触摸屏等


注册输入设备:


看一下input_register_device做什么


       list_add_tail(&dev->node,&input_dev_list);


会把一个结构体放入链表input_dev_list中


list_for_each_entry(handler,&input_handler_list, node)


              input_attach_handler(dev,handler);


对链表里的每个input_handler(注册的时候也会放入链表),都调用input_attach_handler(dev, handler);来根据input_handler的id_table来判断能不能支持这个input_device输入设备。


(10)

注册input_register_handler的时候做什么?


放入数组


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


放入链表


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


对每个input_dev调用input_attach_handler,来根据input_handler的id_table来判断能不能支持这个输入设备。


       list_for_each_entry(dev,&input_dev_list, node)


              input_attach_handler(dev,handler);


 


很对称  无论先加载右边Hander还是 左边的设备,都可以调用input_attach_handler。



(11)来看一下input_attach_handler做什么?



根据handler的id_table和输入设备dev看看能不能匹配,可以的话调用handler 的connect函数


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


       if(!id)


              return-ENODEV;


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


       if(error && error != -ENODEV)


注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,


根据input_handler的id_table判断这个input_handler能否支持这个input_dev,


如果能支持,则调用input_handler的connect函数建立"连接",每个handler都有自己不同方式。


(12)怎么建立连接?

看一看evdev的connect函数


1. 分配一个input_handle结构体


evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);


error = input_register_handle(&evdev->handle);


2.


       input_handle.dev= input_dev;  // 指向左边的input_dev


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


3. 注册:


  input_handler->h_list = &input_handle;


  inpu_dev->h_list      =&input_handle;


 


evdev_connect


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


int input_register_handle(struct input_handle *handle)

{

struct input_handler *handler = handle->handler;



list_add_tail(&handle->d_node, &handle->dev->h_list);

list_add_tail(&handle->h_node, &handler->h_list);



if (handler->start)

handler->start(handle);



return 0;

}

(13)怎么读按键?

app: read


--------------------------


  .......新的结构体的read函数


            evdev_read


     // 无数据并且是非阻塞方式打开,则立刻返回


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


谁来唤醒?


evdev_event


       wake_up_interruptible(&evdev->wait);


evdev_event事件处理函数,唤醒应用程序。设备出发,hander处理。


 


evdev_event被谁调用?


猜:应该是硬件相关的代码,input_dev那层调用的


在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数


gpio_keys_isr


       //上报事件


       input_event(input,type, button->code, !!state);


       input_sync(input);


      


input_event(struct input_dev *dev, unsignedint type, unsigned int code, int value)


       structinput_handle *handle;


 


       list_for_each_entry(handle,&dev->h_list, d_node)


              if(handle->open)


                     handle->handler->event(handle,type, code, value);


二、按键input子系统代码实现

1.参考Gpio_keys.c (driversinputkeyboard)



(1)以前写驱动程序步骤:


应用程序:open、read、write。。。


驱动:drv_opne、drv_read、drv_write。。。


然后有一个结构体,涵盖了各个方法,注册、入口、出口


(2)input.c别人写的,也包括:


主设备号:13


file_opreations结构体:只有open函数(中转作用,找到某个hander 用里面的file_opreations)


注册函数:regist_chrdev(13,。。)


入口函数:


出口函数:

(3)怎么写符合输入子系统框架的驱动程序?


分配一个input_dev结构体

设置

注册

硬件相关的代码,比如在中断服务程序里上报事件

(4)参考Gpio_keys.c (driversinputkeyboard),拷贝里面的头文件

static int buttons_init(void)

static void buttons_exit(void)

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

(5)分配一个input_dev结构体


static struct input_dev *buttons_dev;

 /* 1. 分配一个input_dev结构体 */

buttons_dev = input_allocate_device();


(6)设置结构体的属性参数

/* 2. 设置 */

/* 2.1 能产生哪类事件 */

set_bit(EV_KEY, buttons_dev->evbit);

set_bit(EV_REP, buttons_dev->evbit);

/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */

set_bit(KEY_L, buttons_dev->keybit);

set_bit(KEY_S, buttons_dev->keybit);

set_bit(KEY_ENTER, buttons_dev->keybit);

set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);


struct  input_dev {

void *private;

const char *name;

const char *phys;

const char *uniq;

struct input_id id;

unsigned long evbit[NBITS(EV_MAX)];    //表示能产生哪类事件

设置可以产生:同步类、按键类事件、相对位移类、绝对位移事件(触摸屏)等事件

     unsigned long keybit[NBITS(KEY_MAX)];  //表示能产生哪些按键L、S 

     unsigned long relbit[NBITS(REL_MAX)];  // 表示能产生哪些相对位移事件,x,y,滚轮

     unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y

     unsigned long mscbit[NBITS(MSC_MAX)];

     unsigned long ledbit[NBITS(LED_MAX)];

     unsigned long sndbit[NBITS(SND_MAX)];

[1] [2]
关键字:S3C2440  子系统 引用地址:08-S3C2440驱动学习(二)嵌入式linux-input子系统分析与应用

上一篇:09-S3C2440驱动学习(三)嵌入式linux-platform平台总线驱动程序及分离分层构建驱动框架
下一篇:07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-查询+中断+引入poll机制的按键驱动程序

推荐阅读最新更新时间:2024-11-09 11:28

开发板S3C2440挂起NFS步骤
第一、安装、配置、启动FTP、SSH或NFS服务 参考韦东山的嵌入式linux应用开发完全手册 http://pan.baidu.com/s/1o79h3n0 第二、windows、linux以及开发板需要三者ping通 参考资料:http://pan.baidu.com/s/1o8Uw0vo 注:开发板上ping虚拟机是在根文件系统下,修改ip,掩码号可以在u-boot里修改set ipaddr 开发板ip号 set serverip 虚拟机linux ip号 。保存save 第三、要烧录一个具有一些特殊功能的u-boot 资源:http://pan.baidu.com/s/1hsFF8Cs 第四、虚拟机下,允许哪个目录被
[单片机]
s3c2440裸机-UART编程(一、UART硬件介绍及传输原理)
1.uart硬件介绍UART的全称是Universal Asynchronous Receiver and Transmitter(异步收发器)。 uart主要用于: 1.打印调试 2.数据传输 串口通过三根线即可,发送、接收、地线。 pc的TxD - arm的RxD (UART write) arm的TxD - pc的RxD (UART read) 2.uart的参数和格式 波特率:表示每秒传输多少bit,bits per second(bps).一般波特率都会有9600,19200,115200等选项。 格式: 起始位: 先发出一个逻辑”0”的信号,表示传输数据的开始。 数据位:可以是5~8位逻辑”0”或”1”。一般7
[单片机]
<font color='red'>s3c2440</font>裸机-UART编程(一、UART硬件介绍及传输原理)
s3c2440裸机-UART编程(二、UART编程实现)
UART编程 1.初始化 我们的2440支持3个UART串口,以uart0为例讲解。 那么我们需要实现以下这几个函数完成串口的最基本功能: (1)uart0_init()用于初始化串口 (2)putchar()用于发送一个字符 (3)getchar()用于接收一个字符 (4)puts()用于发送一串字符 1.uart0_init() 1.配置uart0引脚 (1)根据原理图GPH2,3用于TxD0, RxD0。 (2)查看dataset,配置GPH控制寄存器,让GPH2,3配成uart模式;为了将其保持为高电平,先设置其为上拉。 GPHCON &= ~((3 4) | (3 6)); GPHCON |= ((2 4) | (
[单片机]
<font color='red'>s3c2440</font>裸机-UART编程(二、UART编程实现)
u-boot-2011.06在基于s3c2440开发板的移植之NorFlash启动
在移植之前,我们还需要安装、配置eldk,用于编译u-boot。下面我们就先介绍一下eldk的安装与配置: 1.下载eldk 在 这里 选择任一版本的eldk并下载,我选择的是eldk4.2版本的arm-2008-11-24.iso文件。把该文件下载到/home/zhaocj/download/目录下。 2.在root权限下安装eldk 创建挂载点: mkdir /mnt/dvdrom 挂载光驱: mount –o loop /home/zhaocj/download/arm-2008-11-24.iso/mnt/dvdrom 安装eldk: cd /mnt/dvdrom ./install –d /opt/eldk4.2/
[单片机]
科学家实现在不破坏量子态的情况下测量机械量子系统的属性
在单个量子水平上控制机械运动的系统正在成为一个有前途的量子技术平台。新的实验工作现在确定了如何在不破坏量子态的情况下测量这种系统的量子特性--这是充分挖掘机械量子系统潜力的一个关键因素。当提到量子力学系统时,人们可能会想到单光子和隔离良好的离子和原子,或者电子在晶体中传播。在量子力学的背景下,更奇特的是真正的机械量子系统;也就是说,大质量物体的机械运动,如振动是量化的。 图为声学共振器的光学显微镜图像(两个较大的圆盘,其内部是压电换能器)和连接到超导量子轨道(白色结构)的天线 在一系列开创性的实验中,机械系统的基本量子力学特征已经被观察到,包括能量量化和纠缠。然而,为了将这些系统用于基础研究和技术应用,观察量子特性仅仅
[半导体设计/制造]
科学家实现在不破坏量子态的情况下测量机械量<font color='red'>子系统</font>的属性
s3c2440裸机-LCD编程(六、LCD上显示字符)
1.字库的移植 字符也是由点构成的,一个个点组成的点阵,其实本质上要显示文字就是把字库移植到对应的自己型号相匹配的board上,字库中的每一个字符都是一些点按照对应格式组合成的集合。 从linux内核源码中随便挑选一个字库文件,比如linux-4.18.16/lib/fonts这个目录下就有对应的很多字库文件。在这里我挑选font_8x16.c,如下图: 其中8x16表示每个字符所占的像素点的大小,表示每个字符占的大小为长*宽=8*16个像素点。 我们来看下一个字符'A'是如何显示的?从font_8x16.c我们找到字符'A'的数据,如下图: 那么我们如何让font_8x16.c这个字库
[单片机]
<font color='red'>s3c2440</font>裸机-LCD编程(六、LCD上显示字符)
Rambus推出PCIe 6.0接口子系统方案,以应对标准升级加速
2022年初,PCIe 6.0标准和规范正式提出,相较于2019年5月推出的PCIe 5.0标准,只过去了两年多时间。Rambus 战略营销副总裁Matt Jones表示,PCIe标准演进速度加快,主要是由于数据中心、云计算及高性能计算的迅速发展,数据密集型应用的要求需要更高的总线标准。 子系统方案加速开发进程 今年初,Rambus推出了针对PCIe 6.0的控制器IP,以助力PCIe 6.0技术的发展。今天,Rambus宣布推出PCIe 6.0的PHY,并可以与PCIe 6.0控制器结合,成为PCIe 6.0接口子系统。 Matt表示,Rambus这种以PHY和控制器全集成的方案,可进一步降低延迟,降低功耗,并且优
[嵌入式]
Rambus推出PCIe 6.0接口<font color='red'>子系统</font>方案,以应对标准升级加速
s3c2440——Nandflash
简介:   Nandflash与计算机的硬盘类似,用于保存系统运行所必须的操作系统、用户数据、运行过程等各类数据。并且在Nandflash中的数据在掉电后仍可永久保存。 一、目标芯片(K9F1208U0M)   1、此芯片为三星公司生产的容量为64MB。(下图为网上图片、非本人原创)。由下图可以看出,1page = 528byte(其中Data feild = 512bytes、spare = 16bytes)。 1 block = 32 pages = 32 * 528byte。其中可以存储数据的是 (32 * 512byte) = 16KB。然而此芯片有4096个block。所以此芯片的容量可以计算得出:(4096 * 3
[单片机]
<font color='red'>s3c2440</font>——Nandflash

推荐帖子

2022得捷电子创新设计大赛+开箱贴
今天刚收到得捷电子发过来的货,发个开箱贴,多图准备,顺便测试下STM32F7508-DK开发板的使用情况。发货单上详细写了元器件型号等相关信息。主角STM32F508-DK。购置了各种各样的传感器,后续做一款拓展板全部集成在一起。然后就是测试下板子自带例程这里是VNC例程,连接网线后即可测试。这个里面带了两个图形GUI方案。使用下来还是很流畅的。看下背面,所带接口满足我本次开发需求了。最后视
流行科技 DigiKey得捷技术专区
简易直流电子负载(C题)
设计和制作一台恒流(CC)工作模式的简易直流电子负载。其原理示意图如图1所示。图1直流电子负载原理示意图二、要求1.基本要求(1)恒流(CC)工作模式的电流设置范围为100mA~1000mA,设置分辨率为10mA,设置精度为±1%。还要求CC工作模式具有
田健平 模拟电子
求助,晶丰明源的BP85928D应用,看不太懂
这个固定5V输出,有输出引脚吗?是如何做到输出的?求助,晶丰明源的BP85928D应用,看不太懂【这个固定5V输出,有输出引脚吗?是如何做到输出的?】看电原理图,像是Buck-Boost电路。输出是Vout两端,上为负,下为正,恐怕不是【固定5V输出】。 上面又写着输出电压纹波小,但是看手册,就没有能输出电压的PIN脚把,我都看懵了。 【但是看手册,就没有能输出电压的PIN脚把,我都看懵了】确实没有输出电压的引脚。输出的正负两端都与芯片不直接联接。
tangbaoovo 电源技术
编译过程的警告
今天去编译Linux3.11,ARM1176,BCM2835.这个错误产生的原因大概有哪些?我用linux3.10.10的旧配置文件直接编译/编译过程的警告
cl17726 Linux与安卓
“全数控、积木式TI方案电源”分工,期待你加入!
“全数控、积木式TI方案电源”的项目,现在需要大量人员参与开发,对每一部分的电路拓扑、元器件选择及定型作一下实验,需要第一手的数据。希望大家多协助,踊跃参加。现把整个项目分为几个部分,由不同人员分别调试,最后将调试数据(包括中间数据和最终数据)汇总整理。其划分如下:一、PFC部分。指标:输入AC160V~230V,输出DC390V+10V-20V;功率250W;频率100KHz~150KHz电路拓扑:受CP
dontium DIY/开源硬件专区
【2024 DigiKey 创意大赛】”双光融合“智能热像仪 作品总结与提交
双光融合智能热像仪作者:JOEYCH 作品简介本项目为一款双光融合智能热像仪,双光融合指的是可见图像与热图像相融合。该设备硬件上基于高性能STM32H7微控制器,软件上基于使用micropython语言的OPENMV开源项目和TensorFlowLiteAI模型框架,实现了图像采集的高效率和手写数字分析的高精度。听取秦天大佬的建议,添加LCD触摸屏提供直观的用户交互体验,支持模式的快速切换和实
JOEYCH DigiKey得捷技术专区
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved