ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

驱动原理和逻辑说明

2021-02-25 21:02:27  阅读:178  来源: 互联网

标签:逻辑 r0 input 指令 事件 寄存器 驱动 原理 EventHub


成长是螺旋式的,涉及到:深度(高度)和广度。

(是否掌握某方面的知识,掌握或不掌握的程度;是否愿意,愿意或不愿意的程度;两者之间的关系;黑与白,有过度中间有赤橙黄绿青蓝紫;)

目录:

1.  makefile、Kconfig、.config的区别

2.  输入子系统(流程,TYPE-A/B协议)

3.  外设模块(升降马达,屏的知识, 自动化测试APK导入)

4.  系统启动流程

5.  性能分析(TP划线慢,)

6.  代码,函数解读(kthread_run()函数,TP代码)

7. 

8. 

 

 

-------------------------------------------------------------格式说明----------------------------------------------------------------

(1)大标题是.加两个空格;     (2)大标题可以直接搜索,内容若和大标题一致,要加空格分隔做为区分;

-------------------------------------------------------------旅行开始----------------------------------------------------------------

1. makefile、Kconfig、.config的区别

http://www.cnblogs.com/taomaomao/archive/2012/01/05/2312816.html

        Makefile:一个文本形式的文件,其中包含一些规则告诉make编译哪些文件以及怎样编译这些文件;

        Kconfig:一个文本形式的文件,其中主要作用是在内核配置时候,作为配置选项;

       .config:文件是在进行内核配置的时候,经过配置后生成的内核编译参考文件;

2. 输入子系统(流程,TYPE-A/B协议):

前言:安卓输入子系统流程:(里面有详细的分析)http://blog.csdn.net/wangkaiblog/article/details/12085183

            在Android系统中一说到重要的服务,基本都是要从systemserver进程开始说起,因为他是Android世界的开拓者,创建了Android世界所需要个基础。同样,Input系统也是从systemserver中开始说起,首先创建一个InputManagerService对象,为这个对象设置与WindowManagerService相关的回调函数,然后调用InputManagerService的start函数。

            在start函数中通过JNI调用,启动Native层的InputReaderThread,InputDispatcherThread线程,从而开始Input系统的运行。InputReaderThread主要是执行和InputReader相关的内容,主要是从EventHub中读取事件,预处理事件,然会是根据policy来处理此事件,最后发送一个消息到InputDispatcher中通知事件的产生。紧接着InputDispatcher会开始事件的分发,通过InputChannel把事件分发给WindowManager或者应用程序。所以,整个事件分发的大致流程是:

systemserver ---> InputManagerService ---> NativeInputManager  ---> Eventhub  ---> InputReader  ---> InputDispatcher  ---> InputPublisher  ---> InputChannel  ---> InputConsumer  --->  WindowManager or Application.

            由这个大致的流程开始,我们逐步来解析Android系统Input的内容。从Input的启动开始,也就是InputManagerService的创建和线程的启动开始。

            1).Input系统的启动

InputManagerService构成分析

https://blog.csdn.net/warticles/article/details/80975585

            InputManagerService的构造是很简单的,只是在最后通过JNI方法初始化了native层的Input系统,在native层初始化的时候,创建了一个名叫NativeInputManager的对象,这个对象是很重要的,因为它主要负责和系统的其他模块交互,而且InputReader和InputDispatcher都是只运行在Native层中,如果需要调用Java函数也是通过这个对象进行的,另外他实现了InputReaderPolicyInterface和InputDispatcherPolicyInterface,是一个重要的Policy。

            NativeInputManager在构造过程中,完成了InputManager在native基本运行组件的创建,比如创建了EventHub对象,它是事件的Android系统的起源地,所有的事件都是它从驱动中读取出来的;还创建了InputReaderThread线程用来执行InputReader的功能;InputDispatcherThread用来执行InputDispatcher的功能;同时也创建了InputManager来管理EventHub,InputReader,InputReaderThread,InputDispatcher,InputDispatcherThread这些Native运行的基本对象。

            不过要注意一点的是NativeInputManager是InputReaderPolicyInterface和InputDispatcherPolicyInterface的子类,因此在构造InputReader和InputDispatcher的时候要用到NativieInputManager对象。

            在对象构建完成后,开始执行start方法,让之前创建的这些对象运行起来。start方法也是比较简单的,就是通过JNI调用让native层的Input系统运行起来,然后在Java层把自己列入WatchDog的监视范围内。之后定义下自己需要接受的外部通知等。这个过程看代码的话,比较容易,不再列出。那么到这里位置,整个Input系统就运行起来了,至于其中具体的功能我们再逐步分析。这部分内容叙述完毕。

 

            2).InputReader的功能,以及执行的流程

            在InputManager的start方法被调用会,会执行两个线程,分别是InputReaderThread和InputDispatcherThread,虽然它们的启动在代码上有先后之分,但是在实际执行过程中是没有先后的,所以先从哪个线程开始解析Input系统不是很重要的。不过,我是按照从事件的产生到分发开始解析的,所以这里我是选择从InputReader开始。

InputReader是Android系统中重要的部分,根据Android文档中的描述,主要功能就是:

            (1) 从EventHub读取事件,这些事件是元事件,即没有经过加工或者仅仅是简单加工的处理的事件;

            (2)把这些事件加工处理,生成inputEvent事件,这样封装之后的事件,可以满足Android系统的一些需求;

            (3)把这些事件发送到事件监听器,即QueuedInputListener,这个监听器可以把事件传递给InputDispatcher。

下面我们就从线程开始执行的地方一步一步分析这些功能的实现。既然要看InputReader的功能,我就从InputReader的构造函数说起。

            在InputReader创建的时候,这里把InputDispatcher作为参数传递进来,然后以InputDispatcher作为参数构造出了QueuedInputListener对象。所以现在有这么一个关系:InputReader持有一个QueuedInputListener,而QueuedInputListener持有InputDispatcher对象。

            在这里补充一点内容: Android系统在Native层中实现了一个类似于Java中的线程对象,即C++中的Thread类。这个线程类有个特点就是,当线程开始执行后,会一直重复执行threadLoop方法,知道这个线程的强引用计数变为零为止。所以,这里的threadLoop函数会不停地执行下去,也即是mReader->loopOnce()会循环执行下去,每循环一次就能从EventHub中读取出若干事件。下面我们就以一次循环过程为例。

            1.1  从EventHub获取事件(上面第一个功能说明)

            EventHub对象,它是事件的Android系统的起源地,所有的事件都是它从驱动中读取出来的;从EventHub获得的事件有两种,一种是设备添加,移除类的;另一种是由输入设备产生的事件。

            eventhub源码分析:

             https://blog.csdn.net/warticles/article/details/80990809

            EventHub这个类的主要功能就是主动监视Input驱动的变化,一旦有事件产生,就从产生事件相应的驱动中读取出这个事件。实现这个监视驱动功能,是通过Linux提供的epoll机制来实现。epoll机制简单地说就是高效地I/O多路复用机制,使用epoll_wait来监听所需要的文件描述符的变化。EventHub的主要功能是通过epoll_wait来实现的,所以EventHub所在的线程应该会阻塞在epoll_wait方法中,一直等到epoll_wait设置的超时时间。

            现在我们开始看看EventHub的实现,在EventHub的构造函数中,建立了一个管道,并把这个管道的读端和写端的文件描述符添加到epoll的监视之下,以便于其他的线程或者进程能够使EventHub所在的线程从epoll_wait的阻塞中返回。

            EventHub在创建完成之后,第一个被调用的方法就是getEvents,而且这个方法也是EventHub的主要功能,对于这个方法需要仔细分析,我们把getEvents方法也分成了三个部分去解析,分别是:

            (1).  打开设备部分;

            (2).  事件等待部分;

            (3).  事件读取部分;

            这三个部分中,以事件的读取部分为重点。设备打开部分一般发生在Input系统建立的时候调用,所以在系 统 启 动完成,稳定之后,这部分内容应该不会再被执行的;而等待部分较为简单。

            打开设备部分:  EventHub是通过扫描/dev/input/目录下所有可用的设备,然后逐一打开这些设备,打开这些设备过程中,EventHub又做了一些Input系统必要的工作,比如构造Device对象,把这些设备加入到epoll的监视队列中等,时间戳的设定等。在构造Device对象的时候,是通过InputDeviceIdentifier来构造的,主要思路就是通过ioctl函数从内容中读取出一些必要的信息,然后把这些信息经过InputDeviceIdentifier存入Device中,然后再通过ioctl函数测试设备的属性,把这些属性信息也存入Device中。

            这部分代码,把InputDeviceIdentifier转化为了Device,因为Device能够存储更多的信息,是EventHub所需要的。在打开设备的时候对这些Device完成了初始化。然后就是把这些设备加入epoll的监视中,代码如下:

               1     epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)

如此之后,只要设备有输入事件的产生,通过epoll就能从阻塞中返回。

            之后就是设置设备的硬件时钟。在报告事件的时候,我们要使用的时钟是monotonic clock, 这时钟的特点就是在每次开机的时候初始化为0。事件发生时的时间戳在input系统中使用非常广泛,而且Input系统会假设事件的时间戳是monotonic的时间点。最后把这些设备添加到EventHub的一个Vector中,类似如下格式:

deviceId

Device*

1

Device*

2

Device*

...

...

            这个数组将会在EventHub中广泛地使用,经常使用的方式是通过deviceId获取Device设备。到这里,打开设备的工作已经完成,而且为EventHub的工作创建了一些有用的变量和数组等。EventHub中的第一个功能,打开设备已经完成。

            事件等待部分:    事件的等待部分很简单,主要的代码就一行,如下:

epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

            注意代码中的最后一个参数timeoutMillis,前面已经说到过,一般来说这个参数是-1,意味着线程会在这个地方阻塞,无限等待下去,直到有事件的发生,而在新的设备加入的时候,这个值为0,意味着可以立即返回。所以,在系 统 启 动完成后,如果没有事件发生的话,InputReaderThread线程会阻塞在这里,一直等待事件的发生。

          事件读取部分:      基本过程就是,监视到有事件的产生,把事件读取出来,不过这里读出的事件是input_event类型的,然后在逐个把input_event事件转化为InputReader需要的RawEvent类型的事件,放入InputReader提供给EventHub的数组中(通过getEvents参数传递进来的)。说起来很简单,其实也很简单。

            总结一下:  EventHub负责打开/dev/input/目录下的所有设备,然后为每一个设备创建一个Device,并把这个Device放入EventHub所定义的数组们Device中。之后,就是把这个设备纳入监视范围。然后就是开始等待事件的发生,一旦有事件发生,就从产生事件的设备中读取出这些设备,把这些事件转化为RawEvent类型放入InputReader提供的事件数组中,之后返回。到这里,从EventHub获取事件就结束了。

            1.2  InputReader对元事件的处理

由上节的内容,我们知道,从EventHub获得的事件有两种,一种是设备添加,移除类的;另一种是由输入设备产生的事件。InputReader在处理这两类事件稍微有点不一样。先看设备添加类型的事件,这些添加设备事件的处理,为InputReader的工作打下了基础,因为InputReader可以根据添加的设备定义一些数据结构,为以后处理由此设备产生的事件打下基础。

我们对于InputReader的功能的分析就完成了。总结一下,基本过程说就是:

            InputReader从EventHub中读取出来元事件,预处理加工这些元事件成为NotifyArgs,然后通过QueuedInputListener把他们通知给InputDispatcher。

            2). InputDispatcher的功能: 唯一功能分发事件,及执行流程

            把输入事件发送到他的目标中去。他的目标可能是应用程序,也可能是WindowManagerService。

            3). Input子系统中的通信方式是什么?

            4). 应用程序是如何接收到并处理事件的

举例:触摸事件工作原理

https://blog.csdn.net/warticles/article/details/81035943?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

      input事件处理:
https://blog.csdn.net/coldsnow33/article/details/12841077

input事件处理流程 input driver -> input core ->event handler -> userspace 给应用程序

事件的传递过程:首先在驱动层调用inport_report_abs,然后调用input core层的input_event,input_event调用了input_handle_event对事件进行分派,调用input_pass_event,在这里他会把事件传递给具体的handler层,然后在相应handler的event处理函数中,封装一个event,然后把它投入evdev的那个client_list上的client的事件buffer中,等待用户空间来读取。

代码追踪:

input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);

input_event(dev, EV_ABS, code, value);

input_handle_event(dev, type, code, value);

            (1)input_handle_abs_event(dev, code, &value);

input_is_mt_value(code)

            (2)input_pass_values(dev, dev->vals, dev->num_vals)

input_to_handler(handle, vals, count);

  handler->events(handle, vals, count);

.events           = evdev_events,

      evdev_pass_values(client, vals, count, ev_time);

            __pass_event

//发送信号SIGIO信号给fasync_struct 结构体所描述的PID,触发应用程序的SIGIO信号处理函数

             kill_fasync(&client->fasync, SIGIO, POLL_IN);

.fasync        = evdev_fasync,

.read         = evdev_read,

input_event_to_user(buffer + read, &event)

copy_to_user(buffer,&compat_event,sizeof(struct input_event_compat)

.read       = evdev_read,

cdev_init(&evdev->cdev, &evdev_fops); //最终在/dev/目录里面生成了字符设备

framework

eventhub.cpp

输入子系统框架总结:

            首先的话是我们核心层的,执行的时候会注册我们的设备号,然后在handler层注册input_handler,也就是evdev_handler会注册到核心层维护的链表中,这些都是内核帮你完成的,然后再device层我们需要做硬件初始化获取数据,而且需要将我们的设备注册到链表中,注册进来就就会遍历input_handler_list链表,找到对应的handler,匹配成功后会调用connect方法,connect就会帮我们分配出evdev,evdev就记录了input_handler和input_device之间的关系,

            还会帮我们创建设备节点,还会注册cdev从而可以让应用调用,然后当我们应用程序调用open,read等接口的时候就会调用input_handler层实现的xxx_open,那么这个open就会帮你分配好evdev_client,最终在input_dev层上报数据的时候会自动调用input_handler,这个里面就会调用events填充上报的数据到缓冲区client,此时如果没有唤醒队列的话应用read的时候会阻塞,而唤醒队列后最终使用copy_to_user来给应用数据。

            原文链接:https://blog.csdn.net/u010802169/article/details/80489602

            5).输入子系统A/B协议的数据格式

https://www.cnblogs.com/ljf181275034/articles/3343222.html

ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT

SYN_REPORT
简略为:

SYN_MT_REPORT
SYN_REPORT
 只有SYNC,没有其它任何信息,系统就会认为此次事件为UP。
       B协议使用了slot,还有一个新面孔TRACKING_ID.

ABS_MT_SLOT 0
ABS_MT_TRACKING_ID **
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID **
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT
             没有SYN_MT_REPORT,那么它用什么来跟踪当前点属于哪一条线呢,用的就是ABS_MT_TRACKING_ID,当前序列中某点的ID值,如果与前一次序列中某点的ID值相等,那么他们就属于同一条线。既然如此,那么android系统中还需要做排序等运算吗?当然不需要。那么手指全部抬起的时候序列又是怎样的呢?      

ABS_MT_SLOT 0
ABS_MT_TRACKING_ID -1
SYN_REPORT
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID -1  (是fffff)
SYN_REPORT
            6). 追代码记录,兼容二供TP,二供只能最多显示4点

看手机getevent -p

查看发现第二个手指的数据正常上报,但是,屏幕上没有显示,有可能是显示之前被释放了导致,所以就去排查触摸释放的,

input_mt_slot(ts->input_dev, i);

input_event(dev, EV_ABS, ABS_MT_SLOT, slot);

input_handle_event(dev, type, code, value);

input_get_disposition(dev, type, code, &value); 获得事件处理者身份

input_handle_abs_event(dev, code, &value);

            经过追踪发现,这个问题产生的原因是因为touch_num 一供和二供是不同的一共是5,二供是10,两个用的都是B协议,在追踪代码的时候发现一供在申请资源的时候没哟释放,这样就会产生二供也用一共申请的方式,当时for循环到5次之后就会释放第5个手指,这样就导致了二供只能显示4个手指问题。

            修改方法有两个:一个是修改touch_num都改为5,第二个是修改一共的代码, 添加遍历失败后释放申请的输入设备资源input_mt_destroy_slots(tpd->dev)。由于一共已经量产,所以我们最终选择了第一种方法,这样可以做到影响最小。

            7).

 

2.  外设模块(升降马达,屏的知识, 自动化测试APK导入)

     1).升降马达开发:

kernel-4.9/arch/arm64/boot/dts/mediatek/mt6765.dts   平台端

kernel-4.9/arch/arm64/boot/dts/mediatek/xx_hxx.dts 项目端

kernel-4.9/arch/arm64/configs/xxx_hxx_debug_defconfig

-----

     2). 自动化测试APK导入(涉及的文件)

projects对应的是下面的这个路 径下的device.mk

下面的这个是查看有没有copy动作的。 

如图所示,有cp动作。

总共有五个文件要修改:

新添加一个ini文件

和proc下的节点名字一致;所以要查看节点名字才可以设置。

自动化测试提供给应用的reallytek的路 径,把Conf_MultipleTest.ini文件放到  /device/reallytek/rlk6580_we_m/ 路 径下;这时会有脚本把ini文件拷到 /system/etc/ctp/ft5346/ 目录下;这样,在手机对应目录里就会看到ini文件。​

     3). 闪屏 http://blog.csdn.net/cailiwei712/article/details/8485513

调试屏的一些理论知识:http://blog.csdn.net/u012719256/article/details/54633365

实现屏的分辨力切换,通过创建节点,输入指令去控制。       

                    cd /sys/kernel/debug/

                   echo _efuse_test > mtkfb    _resolution_test

     4).

 

4.  系统启动流程

     1).系统启动流程,包括从preloader->lk,lk->kernel,有源码分析的,感兴趣可以了解下,有助于梳理思路

http://blog.csdn.net/forever_2015/article/details/53000643#comments

http://blog.csdn.net/forever_2015/article/details/53047993

     2).安卓bootloader的流程

如下网页分析的是基于MTK平台的bootloader启动流程,很棒的分析

https://blog.csdn.net/forever_2015/category_6498649.html

 

例如:2017.01的uboot分析:

上一节已经分析到了uboot的board_init_r函数,并且把两个参数传递给它

https://blog.csdn.net/qq_16777851/article/list/5

从第一课到第十课很详细

         

 

 https://blog.csdn.net/kai_zone/article/details/80443820

         其中的启动start.s文件解析为:https://www.cnblogs.com/shengruxiahua/p/4897527.html

         bootloader作用:系统上电后,需要一段程序来进行初始化:关闭看门狗,改变系统时钟,初始化存储控制器,将更多的代码复制到内存中等。

         CPU上电后,会从某个地址开始执行,比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则会从0x00000000开始,嵌入式开发板中,需要把存储器件的ROM或Flash等映射到这个地址,Bootloader就存放在这个地址的开始处,一上电就开始执行。(手机中的RAM和ROM分别对应电脑的内存和硬盘)

         (1)u-boot系统启动流程 大多数bootloader都分为stage1和stage2两部分,u-boot也不例外。

依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,

1.Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下

(1) 定义入口。: 该工作通过修改连接器脚本来完成。

(2)设置异常向量(Exception Vector)。 

(3)设置CPU的速度、时钟频率及终端控制寄存器。 

(4)初始化内存控制器。 

(5)将ROM中的程序复制到RAM中。 

(6) 关中断,关看门狗

(7)初始化堆栈,清bss段,为第二阶段准备。

(8)转到RAM中执行,该工作可使用指令ldr pc来完成。

2、Stage2

      C语言代码部分 lib_arm/board.c中的_start_armboot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是

整个u-boot(armboot)的主函数,该函数只要完成如下操作: 

(1)调用一系列的初始化函数。 

(2)初始化存储设备

(3)初始化简单硬件如串口,lcd等 

(4)初始化相关网络设备,填写IP、MAC地址等。 

(5)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

流程图如下:

                      

Bootloader是嵌入式系统在加电后执行的第一段代码,是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,
从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。
/*********************** 中断向量 ***********************/
.globl _start                         //u-boot启动入口
_start: b       reset               //复位向量并且跳转到reset
(pc寄存器 指向的是被取值的指令,而不是指向正在执行的指令,即取指令和更新pc值是同时进行的。
发生中断时是先将指令六(即下一条指令的地址)
放入中断模式下的r14(lr)寄存器。而pc被强制赋值为0x18,然后跳转到异常向量表中取出指令,一般异常
向量表中0x18位置处的代码为
ldr pc,_irq。_irq为中断处理函数的入口地址)。
        ldr     pc, _undefined_instruction       //未定义的指令异常
        ldr     pc, _software_interrupt        //软件中断异常
        ldr     pc, _prefetch_abort               //内存操作异常
        ldr     pc, _data_abort                   //数据异常
        ldr     pc, _not_used                     //未使用
        ldr     pc, _irq                          //慢速中断异常
        ldr     pc, _fiq                            //快速中断异常
b  sleep_setting                      //跳转到sleep_setting
(https://blog.csdn.net/IT_114/article/details/6260707?depth_1-
utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到相应的处理程序,然后再返回到主程序继续执行。)
/*系统上电或reset后,cpu的PC一般都指向0x0地址,在0x0地址上的指令是*/
reset:                                 //复位启动子程序
/******** 设置CPU为SVC32模式(超级保护模式)***********/
mrs r0,cpsr                    //将CPSR状态寄存器读取,保存到R0中
bic r0,r0,#0x1f              //r0寄存器最低5位清零
orr r0,r0,#0xd3            
msr cpsr,r0                    //将R0写入状态寄存器中
(MRS: 状态寄存器到通用寄存器的传送指令。
MSR: 通用寄存器到状态寄存器的传送指令。Msr  (cond)  (rd目标寄存器)
    BIC―――――位清除指令
指令格式:
BIC{cond}{S} Rd,Rn,operand2 
BIC指令将Rn 的值与操作数operand2 的反码按位逻辑”与”,结果存放到目的寄存器Rd 中。
指令示例:BIC R0,R0,#0x0F ;
将R0最低4位清零,其余位不变。
    ORR指令的格式为:
ORR{条件}{S}  目的寄存器,操作数1,操作数2
ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应该是一
个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置
操作数1的某些位。
指令示例:
ORR R0,R0,#3          ;  该指令设置R0的0、1位,其余位保持不变。)
/************** 关闭看门狗 ******************/
ldr      r0, =pWTCON
mov     r1, #0x0
str       r1, [r0]
(一般传送指令   MOV指令: 格式:
        MOV目的-->除CS、IP以外的寄存器或存储器源-->寄存器、存储器、立即数
STR{条件}  源寄存器,<存储器地址>
STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用。         
指令示例:
STR R0,[R1],#8             ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8
写入R1。)
/************** 关闭所有中断 *****************/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
ldr r2, =0x7ff
ldr r0, =INTSUBMSK
str r2, [r0]
/***************** 关键的初始化子程序 ************************/
/ * cpu初始化关键寄存器
* 设置重要寄存器
* 设置内存时钟* /
cpu_init_crit:
/** flush v4 I/D caches*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0              /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0              /* flush v4 TLB */
(MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该
操作,将产生未定义的指令异常中断。指令的语法格式:(CP15协处理器)
MCR{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{,<opcode_2>}
<Rd>作为元寄存器的ARM寄存器,其值被传送到协处理器寄存器中。
<CRn>作为目标寄存器的协处理器寄存器,其编号可能为C0,C1....C15。
 <CRm>附加的目标寄存器或者原操作数寄存器,用于区分同一个编号的不同物理寄存器。当指令中不需要
提供附加信息时,将C0指定为<CRm>,否则指令操作结果不可预知。  
<opcode_2>提供附加信息,用于区别同一个编号的不同物理寄存器。当指令中指定附加信息时,省略<opcode_2>或者将其指定为0,
否则指令操作结果不可预知。
MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,
将产生未定义的指令异常中断。)
/************* disable MMU stuff and caches ****************/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
(#表示立即数寻址。采用立即寻址方式的指令,在立即数前面加上立即寻址符“#”。
例如指令MOV A,#30H中30H就是立即数,指令功能为将30H赋给累加器A。
@表示寄存器的间接寻址。)
/******* 在重新定位前,我们要设置RAM的时间,因为内存时钟依赖开发板硬件的,你将会找到board
目录底下的memsetup.S。**************/
mov ip, lr
#ifndef CONFIG_S3C2440A_JTAG_BOOT
bl memsetup        //调用memsetup子程序(在board/smdk2442memsetup.S)
#endif
mov lr, ip
mov pc, lr                        //子程序返回
memsetup:   
(b用于不返回的跳转,比如跳到某个标号处,b  .  其中的‘.’代表当前地址,那么 b  .  就是死循环。
bl用于子程序跳转,要返回地址,返回地址存于LR中。当发生bl跳转前,会在寄存器 R14 (即LR)中保存
当前PC-4,即bl跳转指令的下一条指令的地址。所以在返回时只要 MOV pc,lr 。
lr就是连接寄存器(Link Register, LR),在ARM体系结构中LR的特殊用途有两种:
一.是用来保存子程序返回地址;
二.是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以
根据LR的值返回到异常发生前的相应位置继续执行。)
/**************** 初始化内存 **************/
    mov   r1,     #MEM_CTL_BASE       @ 存储控制器的13个寄存器的开始地址
    adrl    r2, mem_cfg_val         @ 这13个值的起始存储地址
    add    r3,     r1, #52             @ 13*4 = 54
1:  
    ldr r4,     [r2], #4            @ 读取设置值,并让r2加4
    str r4,     [r1], #4            @ 将此值写入寄存器,并让r1加4
    cmp r1,     r3                  @ 判断是否设置完所有13个寄存器
    bne 1b                   @ 若没有写成,继续bne 1b这条语句里的b是backward的意思,既然有          
                               backward就有forward,所有就有bne 1f语句
    mov pc,     lr       @ 返回
(加法指令 ADD(Addition) 格式: ADD A,B   //A=A+B;功能: 两数相加)。
/*********** 跳转到原来进来的下一个指令(start.S文件里) ***************/  
mov     pc, lr                 //子程序返回
/****************** 建立堆栈 *******************/
ldr r0, _armboot_end               //armboot_end重定位
add r0, r0, #CONFIG_STACKSIZE    //向下配置堆栈空间
sub sp, r0, #12                  //为abort-stack预留个3字即12
(汇编语言中SP寄存器是指的是堆栈指针寄存器,在堆栈操作中使用,PUSH和POP指令是从SP寄存器得到现行堆
栈段的段内偏移量,所以称SP寄存器为堆栈指针,SP始终指向栈顶。
Sub减法运算);
/**************** 跳转到C代码去 **************/
ldr pc, _start_armboot      //跳转到start_armboot函数入口,start_armboot字保存函数入口指针
_start_armboot: .word start_armboot    //start_armboot函数在lib_arm/board.c中实现从此进入第二阶段C语言代码部分
/**************** 异常处理程序 *******************/
.align  5                //ARM的.align 5就是2的5次方对齐,也就是32字节对齐, 它的含义就是使得下面的代码按一定规则对齐;
                         //如果不加这个,这些地址就按4个字节增加,因为一条ARM指令是4个字节。
undefined_instruction:               //未定义指令
get_bad_stack
bad_save_user_regs
bl  do_undefined_instruction
.align 5
software_interrupt:                   //软件中断
get_bad_stack
bad_save_user_regs
bl  do_software_interrupt
.align 5
prefetch_abort:                      //预取异常中止
get_bad_stack
bad_save_user_regs
bl  do_prefetch_abort
.align 5
data_abort:                          //数据异常中止
get_bad_stack
bad_save_user_regs
bl  do_data_abort
.align 5
not_used:                            //未利用
get_bad_stack
bad_save_user_regs
bl  do_not_used
.align 5
irq:                                   //中断请求
get_irq_stack
irq_save_user_regs
bl  do_irq
irq_restore_user_regs
.align 5
fiq:                                   //快速中断请求
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl  do_fiq
irq_restore_user_regs
sleep_setting:                           //休眠设置
@ prepare the SDRAM self-refresh mode
ldr r0, =0x48000024 @ REFRESH Register
ldr r1, [r0]
orr r1, r1,#(1bd = &bd_data;
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _armboot_end_data - _armboot_start;
/*** 调用执行init_sequence数组按顺序执行初始化 ***/
for (init_fnc_ptr = init_sequence;     *init_fnc_ptr;         ++init_fnc_ptr){
    if ((*init_fnc_ptr)() != 0) {
          hang ();
    }
}
#if 0
/**************** 配置可用的flash单元 *************/
size = flash_init ();             //初始化flash
display_flash_config (size);      //显示flash的大小
/******** _arm_boot在armboot.lds链接脚本中定义 ********/
#endif
#ifdef CONFIG_VFD
#  ifndef PAGE_SIZE
#  define PAGE_SIZE 4096
#  endif
/*********** 为VFD(VFD被用来配置网卡上的虚拟端口)显示预留内存(整个页面)  **********/
/******** armboot_real_end在board-specific链接脚本中定义********/
addr = (_armboot_real_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
/******* 进入下一个界面 ********/
addr += size;
addr = (addr + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
mem_malloc_init (addr);
#else
/********  armboot_real_end 在board-specific链接脚本中定义 *******/
mem_malloc_init (_armboot_real_end);
#endif    /* CONFIG_VFD */
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:");
nand_init();  /* NAND初始化 */
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/********* 初始化环境 **********/
env_relocate ();
/*********** 配置环境变量,重新定位 **********/
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif
/* 从环境中得到IP地址 */
bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr");
/*以太网接口MAC地址*/
{
int i;
ulong reg;
char *s, *e;
uchar tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg bd->bi_enetaddr);
#endif
#ifdef CONFIG_DRIVER_LAN91C96
if (getenv ("ethaddr")) {
       smc_set_mac_addr(gd->bd->bi_enetaddr);
}
/* eth_hw_init(); */
#endif          /* CONFIG_DRIVER_LAN91C96 */
/* 通过环境变量初始化*/
if ((s = getenv ("loadaddr")) != NULL) {
       load_addr = simple_strtoul (s, NULL, 16);

#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
       copy_filename (BootFile, s, sizeof (BootFile));
}
#endif           /* CFG_CMD_NET */
#ifdef BOARD_POST_INIT
board_post_init ();
#endif
/* main_loop() 总是试图自动启动,循环不断执行*/
for (;;) {         //死循环
        main_loop ();             /*主循环函数处理执行用户命令—common/main.c  */
}
/* NOTREACHED - no way out of command loop except booting */
}
 
        高通平台BootLoader的流程 

https://blog.csdn.net/makeyourprogress/article/details/73920431

 

5.  性能分析(TP划线慢,)

     1).这2篇文章写得不错 大家有空学习下  后续TP划线慢这种问题我们来主导分析,buffer是否满;

            http://gityuan.com/2016/12/31/input-ipc/

            http://gityuan.com/2017/01/01/input-anr/

     2).

 

6.   代码,函数解读 (kthread_run()函数,TP代码)

     1). kthread_run()函数详细说明

首先看看它的定义之处才发现它是一个宏函数,而不是一个真正意义上的函数。这个函数会创建一个名为namefmt的内核线程,这个线程刚创建时不会马上执行,要等到它将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。touch_event_handler这个函数就是创建的运行函数。​

            http://blog.chinaunix.net/uid-28776666-id-3797013.html

     2).  TP代码

     3).
————————————————
 

标签:逻辑,r0,input,指令,事件,寄存器,驱动,原理,EventHub
来源: https://blog.csdn.net/u010783226/article/details/114104961

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有