日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

驱动原理和逻辑说明

發布時間:2024/3/13 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 驱动原理和逻辑说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

(是否掌握某方面的知識,掌握或不掌握的程度;是否愿意,愿意或不愿意的程度;兩者之間的關系;黑與白,有過度中間有赤橙黃綠青藍紫;)

目錄:

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).
————————————————
?

總結

以上是生活随笔為你收集整理的驱动原理和逻辑说明的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。