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  子系统 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic566471.html

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

推荐阅读

嵌入式linux开发 (十三) FLASH(3) s3c2440外扩NAND FLASH
- JZ2440 S3C2440 ARM920T -内置 Steppingstone (4K-Byte SRAM) 无rom -外扩 MX29LV160DBTI:2MB,并口 NOR FLASH K9F2G08U0C: 256MB,NAND FLASHsocThe S3C2440A is developed with ARM920T core, 0.13um CMOS standard cells and a memory complier.The ARM920T implements MMU, AMBA BUS, and Harvard cache architecture with separate 16KB instructi
发表于 2022-08-08
嵌入式linux开发 (二十三) 内存管理(5) s3c2440内存管理
之前想着s3c2440内存管理应该和imx6ull 是一样的,目前看起来是不一样JZ2440 S3C2440 ARM920T ARMv5t -内置 Steppingstone (4K-Byte SRAM) 无rom -外扩 EM63A165TS-6G:32MB/片,SDRAM,共两片 MX29LV160DBTI:2MB,并口 NOR FLASH K9F2G08U0C: 256MB,NAND FLASH硬件对内存的管理socSupports various types of ROM for booting (NOR/NAND Flash, EEPROM, and others)NAND Flash Boot Loader· Suppo
发表于 2022-08-08
<font color='red'>嵌入式</font>linux开发 (二十三) 内存管理(5) <font color='red'>s3c2440</font>内存管理
嵌入式linux开发 (九) RAM(3) s3c2440外扩sdram
- JZ2440 S3C2440 ARM920T -内置 Steppingstone (4K-Byte SRAM) 无rom -外扩 EM63A165TS-6G:32MB/片,SDRAM,共两片socThe S3C2440A is developed with ARM920T core, 0.13um CMOS standard cells and a memory complier.The ARM920T implements MMU, AMBA BUS, and Harvard cache architecture with separate 16KB instruction and 16KB data caches, each
发表于 2022-08-08
<font color='red'>嵌入式</font>linux开发 (九) RAM(3) <font color='red'>s3c2440</font>外扩sdram
S3C2440汇编点灯
如何点灯步骤如下1、看原理图确定控制LED的引脚2、看主芯片手册确定如何设置/控制引脚这里JZ2440的芯片,就是S3C24403、写程序主芯片引脚输出高电平或低电平就可以改变LED状态我们不关心GPIO引脚输出的逻辑电压,只关心高电平或低电平主芯片输出的两种情况第一种此时主芯片输出3.3V电压,点亮LED输出0V电压,熄灭LED第二种此时主芯片输出0V电压,点亮LED输出3.3V电压,熄灭LED引脚驱动能力不足的情况有可能有的引脚能够发出的最大电流不能够点亮LED这时候就需要我们来改进这个电路——使用三极管有可能芯片只能输出1.2V,但足以使得三极管导通那么电流从3.3V处往下流动,就能点亮LED另一种方法当芯片输出低电平/0V,
发表于 2022-08-08
<font color='red'>S3C2440</font>汇编点灯
反汇编解析S3C2440汇编点灯
代码首先回顾代码/**点亮LED:GPF4*/.text //表明它是代码段.global _start_start:/*配置GPF4为输出引脚*把0x100写到地址0x56000050上,熄灭led*/ ldr r1, =0x56000050 /*将这个地址存放到r1中*/ ldr r0, =0x100 /*或者使用 mov r0, #0x100 将0x100放入r0 */ str r0, [r1] /*将r0的值写入到r1的地址中*//*设置GPF4输出高电平*把0x00写到地址0x56000054上,点亮led*/ ldr r1, =0x56000054 /*将这个地址存放到r1中*/ ldr r0, =0 /*或者
发表于 2022-08-08
反汇编解析<font color='red'>S3C2440</font>汇编点灯
S3C2440使用bin文件机器码点灯
S3C2440芯片手册导读https://blog.csdn.net/qq_28258885/article/details/111461640这次我们先点亮另外一个LED灯,于是先阅读芯片手册这次配置LED2,那么我们将GPFCON的[11,10]设置成01,也就是0x400编程/**点亮LED:GPF4*/.text //表明它是代码段.global _start_start:/*配置GPF4为输出引脚*把0x100写到地址0x56000050上,熄灭led*/ ldr r1, =0x56000050 /*将这个地址存放到r1中*/ ldr r0, =0x400 /*或者使用 mov r0, #0x400 将0x400放入r
发表于 2022-08-05
<font color='red'>S3C2440</font>使用bin文件机器码点灯

推荐帖子

汇编与C相兼容编程求助
本帖最后由wensir于2016-5-3019:29编辑 #include<iom8v.h> unsignedcharbyte[200]={0};//初始化100个ram单元 voidTestASM(void); voidmain(void) { TestASM(); while(1) { if(byte[5]==100)//取第六个单元数据 { PORTB=~PORTB; } } } 以
wensir Microchip MCU
iTOP3399开发板Android应用开发环境搭建-安装AndroidStudio(一)
配套资料在网盘资料的“iTOP-3399开发资料汇总(不含光盘资料)\06_iTOP-3399开发板Androidstudio\安装Androidstudio” Androidstudio是一个Android集成开发工具,基于IntelliJIDEA,AndroidStudio提供了集成的Android开发工具用于开发和调试。本文档将带领大家安装AndroidStudio4.0.1版本。
遥寄山川 ARM技术
如何用tinymix调试WM8960 音频驱动?
RT,用过的来说一下,偶要多学习。 如何用tinymix调试WM8960音频驱动?
Wince.Android 嵌入式系统
485电路集(。sch+程序)
各种各样的485电路集485电路集(。sch+程序)
tonytong 单片机
简单的数值比较,谁能告诉我这是为什么
谁能告诉我这是为什么?明明“chipNumb”值是0x02,而且CHIP_NUMB是常量0x02,还返回FALSE? 1.JPG (6.25KB) 下载次数:3 2010-8-1915:32 2.JPG (39.49KB) 下载次数:2 2010-8-1915:32 简单的数值比较,谁能告诉我这是为什么
flamingo315 stm32/stm8
内核添加串口设备uart3-uart5失败
A8核心板,内核版本4.1.18,初始串口设备只有ttyO0-ttyO2三个,现在要使用ttyO5串口,在内核添加了uart3,uart4,uart5设备树修改如下:内核启动报错信息:.034815]omap_uart48022000.serial:nowakeirqforuart1 [1.040692]of_get_named_gpiod_flags:can'tparse'rts-gpio'propertyofnode
别叫你哥许鲜森 Linux与安卓
小广播
实战 培训 开发板 精华推荐

何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved