免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Linux kernel中斷子系統(tǒng)之(五):驅(qū)動(dòng)申請中斷API
{"root":{"nodeName":"document","nodeValue":null,"nodeType":"0","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t一、前言","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t本文主要的議題是作為一個(gè)普通的驅(qū)動(dòng)工程師,在撰寫自己負(fù)責(zé)的驅(qū)動(dòng)的時(shí)候,如何向Linux Kernel中的中斷子系統(tǒng)注冊中斷處理函數(shù)?為了理解注冊中斷的接口,必須了解一些中斷線程化(threaded interrupt handler)的基礎(chǔ)知識(shí),這些在第二章描述。第三章主要描述了驅(qū)動(dòng)申請 interrupt line接口API request_threaded_irq的規(guī)格。第四章是進(jìn)入request_threaded_irq的實(shí)現(xiàn)細(xì)節(jié),分析整個(gè)代碼的執(zhí)行過程。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t二、和中斷相關(guān)的linux實(shí)時(shí)性分析以及中斷線程化的背景介紹","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t1、非搶占式linux內(nèi)核的實(shí)時(shí)性","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t在遙遠(yuǎn)的過去,linux2.4之前的內(nèi)核是不支持搶占特性的,具體可以參考下圖:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"img","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[{"attrName":"style","attrValue":"display: inline;;"},{"attrName":"src","attrValue":"http://image84.360doc.com/DownloadImg/2015/04/0818/52154533_1.gif"}],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t事情的開始源自高優(yōu)先級任務(wù)(橘色block)由于要等待外部事件(例如網(wǎng)絡(luò)數(shù)據(jù))而進(jìn)入睡眠,調(diào)度器調(diào)度了某個(gè)低優(yōu)先級的任務(wù)(紫色block)執(zhí)行。該低優(yōu)先級任務(wù)歡暢的執(zhí)行,直到觸發(fā)了一次系統(tǒng)調(diào)用(例如通過read()文件接口讀取磁盤上的文件等)而進(jìn)入了內(nèi)核態(tài)。仍然是熟悉的配方,仍然是熟悉的味道,低優(yōu)先級任務(wù)正在執(zhí)行不會(huì)變化,只不過從user space切換到了kernel space。外部事件總是在你不想讓它來的時(shí)候到來,T0時(shí)刻,高優(yōu)先級任務(wù)等待的那個(gè)中斷事件發(fā)生了。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t中斷雖然發(fā)生了,但軟件不一定立刻響應(yīng),可能由于在內(nèi)核態(tài)執(zhí)行的某些操作不希望被外部事件打斷而主動(dòng)關(guān)閉了中斷(或是關(guān)閉了CPU的中斷,或者M(jìn)ASK了該IRQ number),這時(shí)候,中斷信號(hào)沒有立刻得到響應(yīng),軟件仍然在內(nèi)核態(tài)執(zhí)行低優(yōu)先級任務(wù)系統(tǒng)調(diào)用的代碼。在T1時(shí)刻,內(nèi)核態(tài)代碼由于退出臨界區(qū)而打開中斷(注意:上圖中的比例是不協(xié)調(diào)的,一般而言,linux kernel不會(huì)有那么長的關(guān)中斷時(shí)間,上面主要是為了表示清楚,同理,從中斷觸發(fā)到具體中斷服務(wù)程序的執(zhí)行也沒有那么長,都是為了表述清楚),中斷一旦打開,立刻跳轉(zhuǎn)到了異常向量地址,interrupt handler搶占了低優(yōu)先級任務(wù)的執(zhí)行,進(jìn)入中斷上下文(雖然這時(shí)候的current task是低優(yōu)先級任務(wù),但是中斷上下文和它沒有任何關(guān)系)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t從CPU開始處理中斷到具體中斷服務(wù)程序被執(zhí)行還需要一個(gè)分發(fā)的過程。這個(gè)期間系統(tǒng)要做的主要操作包括確定HW interrupt ID,確定IRQ Number,ack或者mask中斷,調(diào)用中斷服務(wù)程序等。T0到T2之間的delay被稱為中斷延遲(Interrupt Latency),主要包括兩部分,一部分是HW造成的delay(硬件的中斷系統(tǒng)識(shí)別外部的中斷事件并signal到CPU),另外一部分是軟件原因(內(nèi)核代碼中由于要保護(hù)臨界區(qū)而關(guān)閉中斷引起的)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t該中斷的服務(wù)程序執(zhí)行完畢(在其執(zhí)行過程中,T3時(shí)刻,會(huì)喚醒高優(yōu)先級任務(wù),讓它從sleep狀態(tài)進(jìn)入runable狀態(tài)),返回低優(yōu)先級任務(wù)的系統(tǒng)調(diào)用現(xiàn)場,這時(shí)候并不存在一個(gè)搶占點(diǎn),低優(yōu)先級任務(wù)要完成系統(tǒng)調(diào)用之后,在返回用戶空間的時(shí)候才出現(xiàn)搶占點(diǎn)。漫長的等待之后,T4時(shí)刻,調(diào)度器調(diào)度高優(yōu)先級任務(wù)執(zhí)行。有一個(gè)術(shù)語叫做任務(wù)響應(yīng)時(shí)間(Task Response Time)用來描述T3到T4之間的delay。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t2、搶占式linux內(nèi)核的實(shí)時(shí)性","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t2.6內(nèi)核和2.4內(nèi)核顯著的不同是提供了一個(gè)CONFIG_PREEMPT的選項(xiàng),打開該選項(xiàng)后,linux kernel就支持了內(nèi)核代碼的搶占(當(dāng)然不能在臨界區(qū)),其行為如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"img","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[{"attrName":"style","attrValue":"display: inline;;"},{"attrName":"src","attrValue":"http://image84.360doc.com/DownloadImg/2015/04/0818/52154533_2.gif"}],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tT0到T3的操作都是和上一節(jié)的描述一樣的,不同的地方是在T4。對于2.4內(nèi)核,只有返回用戶空間的時(shí)候才有搶占點(diǎn)出現(xiàn),但是對于搶占式內(nèi)核而言,即便是從中斷上下文返回內(nèi)核空間的進(jìn)程上下文,只要內(nèi)核代碼不在臨界區(qū)內(nèi),就可以發(fā)生調(diào)度,讓最高優(yōu)先級的任務(wù)調(diào)度執(zhí)行。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t在非搶占式linux內(nèi)核中,一個(gè)任務(wù)的內(nèi)核態(tài)是不可以被其他進(jìn)程搶占的。這里并不是說kernel space不可以被搶占,只是說進(jìn)程通過系統(tǒng)調(diào)用陷入到內(nèi)核的時(shí)候,不可以被其他的進(jìn)程搶占。實(shí)際上,中斷上下文當(dāng)然可以搶占進(jìn)程上下文(無論是內(nèi)核態(tài)還是用戶態(tài)),更進(jìn)一步,中斷上下文是擁有至高無上的權(quán)限,它甚至可以搶占其他的中斷上下文。引入搶占式內(nèi)核后,系統(tǒng)的平均任務(wù)響應(yīng)時(shí)間會(huì)縮短,但是,實(shí)時(shí)性更關(guān)注的是:無論在任何的負(fù)載情況下,任務(wù)響應(yīng)時(shí)間是確定的。因此,更需要關(guān)注的是worst-case的任務(wù)響應(yīng)時(shí)間。這里有兩個(gè)因素會(huì)影響worst case latency:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(1)為了同步,內(nèi)核中總有些代碼需要持有自旋鎖資源,或者顯式的調(diào)用preempt_disable來禁止搶占,這時(shí)候不允許搶占","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(2)中斷上下文(并非只是中斷handler,還包括softirq、timer、tasklet)總是可以搶占進(jìn)程上下文","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t因此,即便是打開了PREEMPT的選項(xiàng),實(shí)際上linux系統(tǒng)的任務(wù)響應(yīng)時(shí)間仍然是不確定的。一方面內(nèi)核代碼的臨界區(qū)非常多,我們需要找到,系統(tǒng)中持有鎖,或者禁止搶占的最大的時(shí)間片。另外一方面,在上圖的T4中,能順利的調(diào)度高優(yōu)先級任務(wù)并非易事,這時(shí)候可能有觸發(fā)的軟中斷,也可能有新來的中斷,也可能某些driver的tasklet要執(zhí)行,只有在沒有任何bottom half的任務(wù)要執(zhí)行的時(shí)候,調(diào)度器才會(huì)啟動(dòng)調(diào)度。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t3、進(jìn)一步提高linux內(nèi)核的實(shí)時(shí)性","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t通過上一個(gè)小節(jié)的描述,相信大家都確信中斷對linux 實(shí)時(shí)性的最大的敵人。那么怎么破?我曾經(jīng)接觸過一款RTOS,它的中斷handler非常簡單,就是發(fā)送一個(gè)inter-task message到該driver thread,對任何的一個(gè)驅(qū)動(dòng)都是如此處理。這樣,每個(gè)中斷上下文都變得非常簡短,而且每個(gè)中斷都是一致的。在這樣的設(shè)計(jì)中,外設(shè)中斷的處理線程化了,然后,系統(tǒng)設(shè)計(jì)師要仔細(xì)的為每個(gè)系統(tǒng)中的task分配優(yōu)先級,確保整個(gè)系統(tǒng)的實(shí)時(shí)性。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t在Linux kernel中,一個(gè)外設(shè)的中斷處理被分成top half和bottom half,top half進(jìn)行最關(guān)鍵,最基本的處理,而比較耗時(shí)的操作被放到bottom half(softirq、tasklet)中延遲執(zhí)行。雖然bottom half被延遲執(zhí)行,但始終都是先于進(jìn)程執(zhí)行的。為何不讓這些耗時(shí)的bottom half和普通進(jìn)程公平競爭呢?因此,linux kernel借鑒了RTOS的某些特性,對那些耗時(shí)的驅(qū)動(dòng)interrupt handler進(jìn)行線程化處理,在內(nèi)核的搶占點(diǎn)上,讓線程(無論是內(nèi)核線程還是用戶空間創(chuàng)建的線程,還是驅(qū)動(dòng)的interrupt thread)在一個(gè)舞臺(tái)上競爭CPU。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t三、request_threaded_irq的接口規(guī)格","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t1、輸入?yún)?shù)描述","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"table","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tbody","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t輸入?yún)?shù)\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t描述\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tirq\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t要注冊handler的那個(gè)IRQ number。這里要注冊的handler包括兩個(gè),一個(gè)是傳統(tǒng)意義的中斷handler,我們稱之primary handler,另外一個(gè)是threaded interrupt handler\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\thandler\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tprimary handler。需要注意的是primary handler和threaded interrupt handler不能同時(shí)為空,否則會(huì)出錯(cuò)\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tthread_fn\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tthreaded interrupt handler。如果該參數(shù)不是NULL,那么系統(tǒng)會(huì)創(chuàng)建一個(gè)kernel thread,調(diào)用的function就是thread_fn\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tirqflags\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t參見本章第三節(jié)\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tdevname\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t?\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tdev_id\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t參見第四章,第一節(jié)中的描述。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t2、輸出參數(shù)描述","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t0表示成功執(zhí)行,負(fù)數(shù)表示各種錯(cuò)誤原因。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t3、Interrupt type flags","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"table","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tbody","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tflag定義\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t描述\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_TRIGGER_XXX\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t描述該interrupt line觸發(fā)類型的flag\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_DISABLED\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t首先要說明的是這是一個(gè)廢棄的flag,在新的內(nèi)核中,該flag沒有任何的作用了。具體可以參考:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"Disabling IRQF_DISABLED","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"舊的內(nèi)核(2.6.35版本之前)認(rèn)為有兩種interrupt handler:slow handler和fast handle。在request irq的時(shí)候,對于fast handler,需要傳遞IRQF_DISABLED的參數(shù),確保其中斷處理過程中是關(guān)閉CPU的中斷,因?yàn)槭莊ast handler,執(zhí)行很快,即便是關(guān)閉CPU中斷不會(huì)影響系統(tǒng)的性能。但是,并不是每一種外設(shè)中斷的handler都是那么快(例如磁盤),因此就有 slow handler的概念,說明其在中斷處理過程中會(huì)耗時(shí)比較長。對于這種情況,在執(zhí)行interrupt handler的時(shí)候不能關(guān)閉CPU中斷,否則對系統(tǒng)的performance會(huì)有影響。 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_SHARED\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t\t這是flag用來描述一個(gè)interrupt line是否允許在多個(gè)設(shè)備中共享。如果中斷控制器可以支持足夠多的interrupt source,那么在兩個(gè)外設(shè)間共享一個(gè)interrupt request line是不推薦的,畢竟有一些額外的開銷(發(fā)生中斷的時(shí)候要逐個(gè)詢問是不是你的中斷,軟件上就是遍歷action list),因此外設(shè)的irq handler中最好是一開始就啟動(dòng)判斷,看看是否是自己的中斷,如果不是,返回IRQ_NONE,表示這個(gè)中斷不歸我管。 早期PC時(shí)代,使用8259中斷控制器,級聯(lián)的8259最多支持15個(gè)外部中斷,但是PC外設(shè)那么多,因此需要irq share?,F(xiàn)在,ARM平臺(tái)上的系統(tǒng)設(shè)計(jì)很少會(huì)采用外設(shè)共享IRQ方式,畢竟一般ARM SOC提供的有中斷功能的GPIO非常的多,足夠用的。 當(dāng)然,如果確實(shí)需要兩個(gè)外設(shè)共享IRQ,那也只能如此設(shè)計(jì)了。對于HW,中斷控制器的一個(gè)interrupt source的引腳要接到兩個(gè)外設(shè)的interrupt request line上,怎么接?直接連接可以嗎?當(dāng)然不行,對于低電平觸發(fā)的情況,我們可以考慮用與門連接中斷控制器和外設(shè)。\t\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_PROBE_SHARED\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_SHARED用來表示該interrupt action descriptor是允許和其他device共享一個(gè)interrupt line(IRQ number),但是實(shí)際上是否能夠share還是需要其他條件:例如觸發(fā)方式必須相同。有些驅(qū)動(dòng)程序可能有這樣的調(diào)用場景:我只是想scan一個(gè)irq table,看看哪一個(gè)是OK的,這時(shí)候,如果即便是不能和其他的驅(qū)動(dòng)程序share這個(gè)interrupt line,我也沒有關(guān)系,我就是想scan看看情況。這時(shí)候,caller其實(shí)可以預(yù)見sharing mismatche的發(fā)生,因此,不需要內(nèi)核打印“Flags mismatch irq……“這樣冗余的信息\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_PERCPU\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t在SMP的架構(gòu)下,中斷有兩種mode,一種中斷是在所有processor之間共享的,也就是global的,一旦中斷產(chǎn)生,interrupt controller可以把這個(gè)中斷送達(dá)多個(gè)處理器。當(dāng)然,在具體實(shí)現(xiàn)的時(shí)候不會(huì)同時(shí)將中斷送達(dá)多個(gè)CPU,一般是軟件和硬件協(xié)同處理,將中斷送達(dá)一個(gè)CPU處理。但是一段時(shí)間內(nèi)產(chǎn)生的中斷可以平均(或者按照既定的策略)分配到一組CPU上。這種interrupt mode下,interrupt controller針對該中斷的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一個(gè)CPU ack了該中斷,那么其他的CPU看到的該interupt source的狀態(tài)也是已經(jīng)ack的狀態(tài)。 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"和global對應(yīng)的就是per cpu interrupt了,對于這種interrupt,不是processor之間共享的,而是特定屬于一個(gè)CPU的。例如GIC中interrupt ID等于30的中斷就是per cpu的(這個(gè)中斷event被用于各個(gè)CPU的local timer),這個(gè)中斷號(hào)雖然只有一個(gè),但是,實(shí)際上控制該interrupt ID的寄存器有n組(如果系統(tǒng)中有n個(gè)processor),每個(gè)CPU看到的是不同的控制寄存器。在具體實(shí)現(xiàn)中,這些寄存器組有兩種形態(tài),一種是banked,所有CPU操作同樣的寄存器地址,硬件系統(tǒng)會(huì)根據(jù)訪問的cpu定向到不同的寄存器,另外一種是non banked,也就是說,對于該interrupt source,每個(gè)cpu都有自己獨(dú)特的訪問地址。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_NOBALANCING\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t這也是和multi-processor相關(guān)的一個(gè)flag。對于那些可以在多個(gè)CPU之間共享的中斷,具體送達(dá)哪一個(gè)processor是有策略的,我們可以在多個(gè)CPU之間進(jìn)行平衡。如果你不想讓你的中斷參與到irq balancing的過程中那么就設(shè)定這個(gè)flag\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_IRQPOLL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t?\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_ONESHOT\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tone shot本身的意思的只有一次的,結(jié)合到中斷這個(gè)場景,則表示中斷是一次性觸發(fā)的,不能嵌套。對于primary handler,當(dāng)然是不會(huì)嵌套,但是對于threaded interrupt handler,我們有兩種選擇,一種是mask該interrupt source,另外一種是unmask該interrupt source。一旦mask住該interrupt source,那么該interrupt source的中斷在整個(gè)threaded interrupt handler處理過程中都是不會(huì)再次觸發(fā)的,也就是one shot了。這種handler不需要考慮重入問題。 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"具體是否要設(shè)定one shot的flag是和硬件系統(tǒng)有關(guān)的,我們舉一個(gè)例子,比如電池驅(qū)動(dòng),電池里面有一個(gè)電量計(jì),是使用HDQ協(xié)議進(jìn)行通信的,電池驅(qū)動(dòng)會(huì)注冊一個(gè)threaded interrupt handler,在這個(gè)handler中,會(huì)通過HDQ協(xié)議和電量計(jì)進(jìn)行通信。對于這個(gè)handler,通過HDQ進(jìn)行通信是需要一個(gè)完整的HDQ交互過程,如果中間被打斷,整個(gè)通信過程會(huì)出問題,因此,這個(gè)handler就必須是one shot的。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_NO_SUSPEND\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t這個(gè)flag比較好理解,就是說在系統(tǒng)suspend的時(shí)候,不用disable這個(gè)中斷,如果disable,可能會(huì)導(dǎo)致系統(tǒng)不能正常的resume。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_FORCE_RESUME\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t在系統(tǒng)resume的過程中,強(qiáng)制必須進(jìn)行enable的動(dòng)作,即便是設(shè)定了IRQF_NO_SUSPEND這個(gè)flag。這是和特定的硬件行為相關(guān)的。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_NO_THREAD\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t有些low level的interrupt是不能線程化的(例如系統(tǒng)timer的中斷),這個(gè)flag就是起這個(gè)作用的。另外,有些級聯(lián)的interrupt controller對應(yīng)的IRQ也是不能線程化的(例如secondary GIC對應(yīng)的IRQ),它的線程化可能會(huì)影響一大批附屬于該interrupt controller的外設(shè)的中斷響應(yīng)延遲。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_EARLY_RESUME\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t?\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tIRQF_TIMER\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t?\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t四、request_threaded_irq代碼分析","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t1、request_threaded_irq主流程","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tint request_threaded_irq(unsigned int irq, irq_handler_t handler, ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"???????????? irq_handler_t thread_fn, unsigned long irqflags, ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"???????????? const char *devname, void *dev_id) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if ((irqflags & IRQF_SHARED) && !dev_id)---------(1) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? return -EINVAL;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? desc = irq_to_desc(irq); -----------------(2) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (!desc)???????? return -EINVAL;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? if (!irq_settings_can_request(desc) || ------------(3) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? WARN_ON(irq_settings_is_per_cpu_devid(desc))) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? return -EINVAL;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? if (!handler) { ----------------------(4) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? if (!thread_fn) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? return -EINVAL; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? handler = irq_default_primary_handler; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? }\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? action->handler = handler; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? action->thread_fn = thread_fn; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? action->flags = irqflags; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? action->name = devname; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? action->dev_id = dev_id;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? chip_bus_lock(desc); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? retval = __setup_irq(irq, desc, action); -----------(5) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? chip_bus_sync_unlock(desc); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(1)對于那些需要共享的中斷,在request irq的時(shí)候需要給出dev id,否則會(huì)出錯(cuò)退出。為何對于IRQF_SHARED的中斷必須要給出dev id呢?實(shí)際上,在共享的情況下,一個(gè)IRQ number對應(yīng)若干個(gè)irqaction,當(dāng)操作irqaction的時(shí)候,僅僅給出IRQ number就不是非常的足夠了,這時(shí)候,需要一個(gè)ID表示具體的irqaction,這里就是dev_id的作用了。我們舉一個(gè)例子:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tvoid free_irq(unsigned int irq, void *dev_id)\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t當(dāng)釋放一個(gè)IRQ資源的時(shí)候,不但要給出IRQ number,還要給出device ID。只有這樣,才能精準(zhǔn)的把要釋放的那個(gè)irqaction 從irq action list上移除。dev_id在中斷處理中有沒有作用呢?我們來看看source code:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tirqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? do { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? irqreturn_t res;? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? res = action->handler(irq, action->dev_id);\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? action = action->next; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? } while (action);\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tlinux interrupt framework雖然支持中斷共享,但是它并不會(huì)協(xié)助解決識(shí)別問題,它只會(huì)遍歷該IRQ number上注冊的irqaction的callback函數(shù),這樣,雖然只是一個(gè)外設(shè)產(chǎn)生的中斷,linux kernel還是把所有共享的那些中斷handler都逐個(gè)調(diào)用執(zhí)行。為了讓系統(tǒng)的performance不受影響,irqaction的callback函數(shù)必須在函數(shù)的最開始進(jìn)行判斷,是否是自己的硬件設(shè)備產(chǎn)生了中斷(讀取硬件的寄存器),如果不是,盡快的退出。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t需要注意的是,這里dev_id并不能在中斷觸發(fā)的時(shí)候用來標(biāo)識(shí)需要調(diào)用哪一個(gè)irqaction的callback函數(shù),通過上面的代碼也可以看出,dev_id有些類似一個(gè)參數(shù)傳遞的過程,可以把具體driver的一些硬件信息,組合成一個(gè)structure,在觸發(fā)中斷的時(shí)候可以把這個(gè)structure傳遞給中斷處理函數(shù)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(2)通過IRQ number獲取對應(yīng)的中斷描述符。在引入CONFIG_SPARSE_IRQ選項(xiàng)后,這個(gè)轉(zhuǎn)換變得不是那么簡單了。在過去,我們會(huì)以IRQ number為index,從irq_desc這個(gè)全局?jǐn)?shù)組中直接獲取中斷描述符。如果配置CONFIG_SPARSE_IRQ選項(xiàng),則需要從radix tree中搜索。CONFIG_SPARSE_IRQ選項(xiàng)的更詳細(xì)的介紹請參考","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"IRQ number和中斷描述符","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(3)并非系統(tǒng)中所有的IRQ number都可以request,有些中斷描述符被標(biāo)記為IRQ_NOREQUEST,標(biāo)識(shí)該IRQ number不能被其他的驅(qū)動(dòng)request。一般而言,這些IRQ number有特殊的作用,例如用于級聯(lián)的那個(gè)IRQ number是不能request。irq_settings_can_request函數(shù)就是判斷一個(gè)IRQ是否可以被request。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tirq_settings_is_per_cpu_devid函數(shù)用來判斷一個(gè)中斷描述符是否需要傳遞per cpu的device ID。per cpu的中斷上面已經(jīng)描述的很清楚了,這里不再細(xì)述。如果一個(gè)中斷描述符對應(yīng)的中斷 ID是per cpu的,那么在申請其handler的時(shí)候就有兩種情況,一種是傳遞統(tǒng)一的dev_id參數(shù)(傳入request_threaded_irq的最后一個(gè)參數(shù)),另外一種情況是針對每個(gè)CPU,傳遞不同的dev_id參數(shù)。在這種情況下,我們需要調(diào)用request_percpu_irq接口函數(shù)而不是request_threaded_irq。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(4)傳入request_threaded_irq的primary handler和threaded handler參數(shù)有下面四種組合:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"table","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tbody","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tprimary handler\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tthreaded handler\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t描述\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tNULL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tNULL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t函數(shù)出錯(cuò),返回-EINVAL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t設(shè)定\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t設(shè)定\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t正常流程。中斷處理被合理的分配到primary handler和threaded handler中。\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t設(shè)定\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tNULL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t中斷處理都是在primary handler中完成\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"tr","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\tNULL\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t設(shè)定\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"td","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t\t\t這種情況下,系統(tǒng)會(huì)幫忙設(shè)定一個(gè)default的primary handler:irq_default_primary_handler,協(xié)助喚醒threaded handler線程\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(5)這部分的代碼很簡單,分配struct irqaction,賦值,調(diào)用__setup_irq進(jìn)行實(shí)際的注冊過程。這里要羅嗦幾句的是鎖的操作,在內(nèi)核中,有很多函數(shù),有的是需要調(diào)用者自己加鎖保護(hù)的,有些是不需要加鎖保護(hù)的。對于這些場景,linux kernel采取了統(tǒng)一的策略:基本函數(shù)名字是一樣的,只不過需要調(diào)用者自己加鎖保護(hù)的那個(gè)函數(shù)需要增加__的前綴,例如內(nèi)核有有下面兩個(gè)函數(shù):setup_irq和__setup_irq。這里,我們在setup irq的時(shí)候已經(jīng)調(diào)用chip_bus_lock進(jìn)行保護(hù),因此使用lock free的版本__setup_irq。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tchip_bus_lock定義如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic inline void chip_bus_lock(struct irq_desc *desc) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (unlikely(desc->irq_data.chip->irq_bus_lock)) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? desc->irq_data.chip->irq_bus_lock(&desc->irq_data); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t大部分的interrupt controller并沒有定義irq_bus_lock這個(gè)callback函數(shù),因此chip_bus_lock這個(gè)函數(shù)對大多數(shù)的中斷控制器而言是沒有實(shí)際意義的。但是,有些interrupt controller是連接到慢速總線上的,例如一個(gè)i2c接口的IO expander芯片(這種芯片往往也提供若干有中斷功能的GPIO,因此也是一個(gè)interrupt controller),在訪問這種interrupt controller的時(shí)候需要lock住那個(gè)慢速bus(只能有一個(gè)client在使用I2C bus)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t2、注冊irqaction","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(1)nested IRQ的處理代碼","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t在看具體的代碼之前,我們首先要理解什么是nested IRQ。nested IRQ不是cascade IRQ,在","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"GIC代碼分析","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":"中我們有描述過cascade IRQ這個(gè)概念,主要用在interrupt controller級聯(lián)的情況下。為了方便大家理解,我還是給出一個(gè)具體的例子吧,具體的HW block請參考下圖:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"img","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[{"attrName":"style","attrValue":"display: inline;;"},{"attrName":"src","attrValue":"http://image84.360doc.com/DownloadImg/2015/04/0818/52154533_3.gif"}],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t上圖是一個(gè)兩個(gè)GIC級聯(lián)的例子,所有的HW block封裝在了一個(gè)SOC chip中。為了方便描述,我們先進(jìn)行編號(hào):Secondary GIC的IRQ number是A,外設(shè)1的IRQ number是B,外設(shè)2的IRQ number是C。對于上面的硬件,linux kernel處理如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)IRQ A的中斷描述符被設(shè)定為不能注冊irqaction(不能注冊specific interrupt handler,或者叫中斷服務(wù)程序)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)IRQ A的highlevel irq-events handler(處理interrupt flow control)負(fù)責(zé)進(jìn)行IRQ number的映射,在其irq domain上翻譯出具體外設(shè)的IRQ number,并重新定向到外設(shè)IRQ number對應(yīng)的highlevel irq-events handler。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(c)所有外設(shè)驅(qū)動(dòng)的中斷正常request irq,可以任意選擇線程化的handler,或者只注冊primary handler。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t需要注意的是,對root GIC和Secondary GIC寄存器的訪問非常快,因此ack、mask、EOI等操作也非??臁?,"nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t我們再看看另外一個(gè)interrupt controller級聯(lián)的情況:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"img","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[{"attrName":"style","attrValue":"display: inline;;"},{"attrName":"src","attrValue":"http://image84.360doc.com/DownloadImg/2015/04/0818/52154533_4.gif"}],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tIO expander HW block提供了有中斷功能的GPIO,因此它也是一個(gè)interrupt controller,有它自己的irq domain和irq chip。上圖中外設(shè)1和外設(shè)2使用了IO expander上有中斷功能的GPIO,它們有屬于自己的IRQ number以及中斷描述符。IO expander HW block的IRQ line連接到SOC內(nèi)部的interrupt controller上,因此,這也是一個(gè)interrupt controller級聯(lián)的情況,對于這種情況,我們是否可以采用和上面GIC級聯(lián)的處理方式呢?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t不行,對于GIC級聯(lián)的情況,如果secondary GIC上的外設(shè)1產(chǎn)生了中斷,整個(gè)關(guān)中斷的時(shí)間是IRQ A的中斷描述符的highlevel irq-events handler處理時(shí)間+I(xiàn)RQ B的的中斷描述符的highlevel irq-events handler處理時(shí)間+外設(shè)1的primary handler的處理時(shí)間。所幸對root GIC和Secondary GIC寄存器的訪問非常快,因此整個(gè)關(guān)中斷的時(shí)間也不是非常的長。但是如果是IO expander這個(gè)情況,如果采取和上面GIC級聯(lián)的處理方式一樣的話,關(guān)中斷的時(shí)間非常長。我們還是用外設(shè)1產(chǎn)生的中斷為例子好了。這時(shí)候,由于IRQ B的的中斷描述符的highlevel irq-events handler處理設(shè)計(jì)I2C的操作,因此時(shí)間非常的長,這時(shí)候,對于整個(gè)系統(tǒng)的實(shí)時(shí)性而言是致命的打擊。對這種硬件情況,linux kernel處理如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)IRQ A的中斷描述符的highlevel irq-events handler根據(jù)實(shí)際情況進(jìn)行設(shè)定,并且允許注冊irqaction。對于連接到IO expander上的外設(shè),它是沒有real time的要求的(否則也不會(huì)接到IO expander上),因此一般會(huì)進(jìn)行線程化處理。由于threaded handler中涉及I2C操作,因此要設(shè)定IRQF_ONESHOT的flag。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)在IRQ A的中斷描述符的threaded interrupt handler中進(jìn)行進(jìn)行IRQ number的映射,在IO expander irq domain上翻譯出具體外設(shè)的IRQ number,并直接調(diào)用handle_nested_irq函數(shù)處理該IRQ。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(c)外設(shè)對應(yīng)的中斷描述符設(shè)定IRQ_NESTED_THREAD的flag,表明這是一個(gè)nested IRQ。nested IRQ沒有highlevel irq-events handler,也沒有primary handler,它的threaded interrupt handler是附著在其parent IRQ的threaded handler上的。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t具體的nested IRQ的處理代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"nested = irq_settings_is_nested_thread(desc); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (nested) { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? if (!new->thread_fn) { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? ret = -EINVAL; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? goto out_mput; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? } ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? new->handler = irq_nested_primary_handler;","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? } else {? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? }\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t……\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t如果一個(gè)中斷描述符是nested thread type的,說明這個(gè)中斷描述符應(yīng)該設(shè)定threaded interrupt handler(當(dāng)然,內(nèi)核是不會(huì)單獨(dú)創(chuàng)建一個(gè)thread的,它是借著其parent IRQ的interrupt thread執(zhí)行),否則就會(huì)出錯(cuò)返回。對于primary handler,它應(yīng)該沒有機(jī)會(huì)被調(diào)用到,當(dāng)然為了調(diào)試,kernel將其設(shè)定為irq_nested_primary_handler,以便在調(diào)用的時(shí)候打印一些信息,讓工程師直到發(fā)生了什么狀況。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(2)forced irq threading處理","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t具體的forced irq threading的處理代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? nested = irq_settings_is_nested_thread(desc); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (nested) {? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"…… ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? } else { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"??????? if (irq_settings_can_thread(desc)) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? irq_setup_forced_threading(new);","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? }\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t……\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tforced irq threading其實(shí)就是將系統(tǒng)中所有可以被線程化的中斷handler全部線程化,即便你在request irq的時(shí)候,設(shè)定的是primary handler,而不是threaded handler。當(dāng)然那些不能被線程化的中斷(標(biāo)注了IRQF_NO_THREAD的中斷,例如系統(tǒng)timer)還是排除在外的。irq_settings_can_thread函數(shù)就是判斷一個(gè)中斷是否可以被線程化,如果可以的話,則調(diào)用irq_setup_forced_threading在set irq的時(shí)候強(qiáng)制進(jìn)行線程化。具體代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic void irq_setup_forced_threading(struct irqaction *new) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (!force_irqthreads)-------------------------------(a) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? return; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))-------(b) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? return;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? new->flags |= IRQF_ONESHOT; -------------------------(d)\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? if (!new->thread_fn) {-------------------------------(c) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? new->thread_fn = new->handler; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? new->handler = irq_default_primary_handler; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? } ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)系統(tǒng)中有一個(gè)強(qiáng)制線程化的選項(xiàng):CONFIG_IRQ_FORCED_THREADING,如果沒有打開該選項(xiàng),force_irqthreads總是0,因此irq_setup_forced_threading也就沒有什么作用,直接return了。如果打開了CONFIG_IRQ_FORCED_THREADING,說明系統(tǒng)支持強(qiáng)制線程化,但是具體是否對所有的中斷進(jìn)行強(qiáng)制線程化處理還是要看命令行參數(shù)threadirqs。如果kernel啟動(dòng)的時(shí)候沒有傳入該參數(shù),那么同樣的,irq_setup_forced_threading也就沒有什么作用,直接return了。只有bootloader向內(nèi)核傳入threadirqs這個(gè)命令行參數(shù),內(nèi)核才真正在啟動(dòng)過程中,進(jìn)行各個(gè)中斷的強(qiáng)制線程化的操作。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)看到IRQF_NO_THREAD選項(xiàng)你可能會(huì)奇怪,前面irq_settings_can_thread函數(shù)不是檢查過了嗎?為何還要重復(fù)檢查?其實(shí)一個(gè)中斷是否可以進(jìn)行線程化可以從兩個(gè)層面看:一個(gè)是從底層看,也就是從中斷描述符、從實(shí)際的中斷硬件拓?fù)涞确矫婵?。另外一個(gè)是從中斷子系統(tǒng)的用戶層面看,也就是各個(gè)外設(shè)在注冊自己的handler的時(shí)候是否想進(jìn)行線程化處理。所有的IRQF_XXX都是從用戶層面看的flag,因此如果用戶通過IRQF_NO_THREAD這個(gè)flag告知kernel,該interrupt不能被線程化,那么強(qiáng)制線程化的機(jī)制還是尊重用戶的選擇的。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\tPER CPU的中斷都是一些較為特殊的中斷,不是一般意義上的外設(shè)中斷,因此對PER CPU的中斷不強(qiáng)制進(jìn)行線程化。IRQF_ONESHOT選項(xiàng)說明該中斷已經(jīng)被線程化了(而且是特殊的one shot類型的),因此也是直接返回了。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(c)強(qiáng)制線程化只對那些沒有設(shè)定thread_fn的中斷進(jìn)行處理,這種中斷將全部的處理放在了primary interrupt handler中(當(dāng)然,如果中斷處理比較耗時(shí),那么也可能會(huì)采用bottom half的機(jī)制),由于primary interrupt handler是全程關(guān)閉CPU中斷的,因此可能對系統(tǒng)的實(shí)時(shí)性造成影響,因此考慮將其強(qiáng)制線程化。struct irqaction中的thread_flags是和線程相關(guān)的flag,我們給它打上IRQTF_FORCED_THREAD的標(biāo)簽,表明該threaded handler是被強(qiáng)制threaded的。new->thread_fn = new->handler這段代碼表示將原來primary handler中的內(nèi)容全部放到threaded handler中處理,新的primary handler被設(shè)定為default handler。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(d)強(qiáng)制線程化是一個(gè)和實(shí)時(shí)性相關(guān)的選項(xiàng),從我的角度來看是一個(gè)很不好的設(shè)計(jì)(個(gè)人觀點(diǎn)),各個(gè)驅(qū)動(dòng)工程師在撰寫自己的驅(qū)動(dòng)代碼的時(shí)候已經(jīng)安排好了自己的上下文環(huán)境。有的是進(jìn)程上下文,有的是中斷上下文,在各自的上下文環(huán)境中,驅(qū)動(dòng)工程師又選擇了適合的內(nèi)核同步機(jī)制。但是,強(qiáng)制線程化導(dǎo)致原來運(yùn)行在中斷上下文的primary handler現(xiàn)在運(yùn)行在進(jìn)程上下文,這有可能導(dǎo)致一些難以跟蹤和定位的bug。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t當(dāng)然,作為內(nèi)核的開發(fā)者,既然同意將強(qiáng)制線程化這個(gè)特性并入linux kernel,相信他們有他們自己的考慮。我猜測這是和一些舊的驅(qū)動(dòng)代碼維護(hù)相關(guān)的。linux kernel中的中斷子系統(tǒng)的API的修改會(huì)引起非常大的震動(dòng),因?yàn)閮?nèi)核中成千上萬的驅(qū)動(dòng)都是需要調(diào)用舊的接口來申請linux kernel中斷子系統(tǒng)的服務(wù),對每一個(gè)驅(qū)動(dòng)都進(jìn)行修改是一個(gè)非常耗時(shí)的工作,為了讓那些舊的驅(qū)動(dòng)代碼可以運(yùn)行在新的中斷子系統(tǒng)上,因此,在kernel中,實(shí)際上仍然提供了舊的request_irq接口函數(shù),如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic inline int __must_check ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? const char *name, void *dev) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? return request_threaded_irq(irq, handler, NULL, flags, name, dev); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t接口是OK了,但是,新的中斷子系統(tǒng)的思路是將中斷處理分成primary handler和threaded handler,而舊的驅(qū)動(dòng)代碼一般是將中斷處理分成top half和bottom half,如何將這部分的不同抹平?linux kernel是這樣處理的(這是我個(gè)人的理解,不保證是正確的):","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(d-1)內(nèi)核為那些被強(qiáng)制線程化的中斷handler設(shè)定了IRQF_ONESHOT的標(biāo)識(shí)。這是因?yàn)樵谂f的中斷處理機(jī)制中,top half是不可重入的,強(qiáng)制線程化之后,強(qiáng)制設(shè)定IRQF_ONESHOT可以保證threaded handler是不會(huì)重入的。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(d-2)在那些被強(qiáng)制線程化的中斷線程中,disable bottom half的處理。這是因?yàn)樵谂f的中斷處理機(jī)制中,botton half是不可能搶占top half的執(zhí)行,強(qiáng)制線程化之后,應(yīng)該保持這一點(diǎn)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(3)創(chuàng)建interrupt線程。代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tif (new->thread_fn && !nested) { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? struct task_struct *t; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? static const struct sched_param param = { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? .sched_priority = MAX_USER_RT_PRIO/2, ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? };\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? t = kthread_create(irq_thread, new, \"irq/%d-%s\", irq,------------------(a) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?????????????? new->name);\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? sched_setscheduler_nocheck(t, SCHED_FIFO, ?m);\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? get_task_struct(t);---------------------------(b) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? new->thread = t; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? set_bit(IRQTF_AFFINITY, &new->thread_flags);---------------(c) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tif (!alloc_cpumask_var(&mask, GFP_KERNEL)) {----------------(d) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? ret = -ENOMEM; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? goto out_thread; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"} ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)-----------(e) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? new->flags &= ~IRQF_ONESHOT;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)調(diào)用kthread_create來創(chuàng)建一個(gè)內(nèi)核線程,并調(diào)用sched_setscheduler_nocheck來設(shè)定這個(gè)中斷線程的調(diào)度策略和調(diào)度優(yōu)先級。這些是和進(jìn)程管理相關(guān)的內(nèi)容,我們留到下一個(gè)專題再詳細(xì)描述吧。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)調(diào)用get_task_struct可以為這個(gè)threaded handler的task struct增加一次reference count,這樣,即便是該thread異常退出也可以保證它的task struct不會(huì)被釋放掉。這可以保證中斷系統(tǒng)的代碼不會(huì)訪問到一些被釋放的內(nèi)存。irqaction的thread 成員被設(shè)定為剛剛創(chuàng)建的task,這樣,primary handler就知道喚醒哪一個(gè)中斷線程了。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(c)設(shè)定IRQTF_AFFINITY的標(biāo)志,在threaded handler中會(huì)檢查該標(biāo)志并進(jìn)行IRQ affinity的設(shè)定。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(d)分配一個(gè)cpu mask的變量的內(nèi)存,后面會(huì)使用到","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(e)驅(qū)動(dòng)工程師是撰寫具體外設(shè)驅(qū)動(dòng)的,他/她未必會(huì)了解到底層的一些具體的interrupt controller的信息。有些interrupt controller(例如MSI based interrupt)本質(zhì)上就是就是one shot的(通過IRQCHIP_ONESHOT_SAFE標(biāo)記),因此驅(qū)動(dòng)工程師設(shè)定的IRQF_ONESHOT其實(shí)是畫蛇添足,因此可以去掉。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(4)共享中斷的檢查。代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\told_ptr = &desc->action; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"old = *old_ptr;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tif (old) { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if (!((old->flags & new->flags) & IRQF_SHARED) ||-----------------(a) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? ((old->flags ^ new->flags) & IRQF_ONESHOT)) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? goto mismatch;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? /* All handlers must agree on per-cpuness */ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? if ((old->flags & IRQF_PERCPU) != (new->flags & IRQF_PERCPU)) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? goto mismatch;\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? /* add new interrupt at end of irq queue */ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? do {------------------------------------(b) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? thread_mask |= old->thread_mask; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? old_ptr = &old->next; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? old = *old_ptr; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? } while (old); ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? shared = 1; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)old指向注冊之前的action list,如果不是NULL,那么說明需要共享interrupt line。但是如果要共享,需要每一個(gè)irqaction都同意共享(IRQF_SHARED),每一個(gè)irqaction的觸發(fā)方式相同(都是level trigger或者都是edge trigger),相同的oneshot類型的中斷(都是one shot或者都不是),per cpu類型的相同中斷(都是per cpu的中斷或者都不是)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)將該irqaction掛入隊(duì)列的尾部。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(5)thread mask的設(shè)定。代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tif (new->flags & IRQF_ONESHOT) { ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? if (thread_mask == ~0UL) {------------------------(a) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? ret = -EBUSY; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????????? goto out_mask; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? } ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? new->thread_mask = 1 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t??? } else if (new->handler == irq_default_primary_handler && ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?????????? !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {--------(b) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? ret = -EINVAL; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??????? goto out_mask; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? }\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t對于one shot類型的中斷,我們還需要設(shè)定thread mask。如果一個(gè)one shot類型的中斷只有一個(gè)threaded handler(不支持共享),那么事情就很簡單(臨時(shí)變量thread_mask等于0),該irqaction的thread_mask成員總是使用第一個(gè)bit來標(biāo)識(shí)該irqaction。但是,如果支持共享的話,事情變得有點(diǎn)復(fù)雜。我們假設(shè)這個(gè)one shot類型的IRQ上有A,B和C三個(gè)irqaction,那么A,B和C三個(gè)irqaction的thread_mask成員會(huì)有不同的bit來標(biāo)識(shí)自己。例如A的thread_mask成員是0x01,B的是0x02,C的是0x04,如果有更多共享的irqaction(必須是oneshot類型),那么其thread_mask成員會(huì)依次設(shè)定為0x08,0x10……","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(a)在上面“共享中斷的檢查”這個(gè)section中,thread_mask變量保存了所有的屬于該interrupt line的thread_mask,這時(shí)候,如果thread_mask變量如果是全1,那么說明irqaction list上已經(jīng)有了太多的irq action(大于32或者64,和具體系統(tǒng)和編譯器相關(guān))。如果沒有滿,那么通過ffz函數(shù)找到第一個(gè)為0的bit作為該irq action的thread bit mask。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(b)irq_default_primary_handler的代碼如下:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"blockquote","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\tstatic irqreturn_t irq_default_primary_handler(int irq, void *dev_id) ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"{ ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??? return IRQ_WAKE_THREAD; ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"}\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t代碼非常的簡單,返回IRQ_WAKE_THREAD,讓kernel喚醒threaded handler就OK了。使用irq_default_primary_handler雖然簡單,但是有一個(gè)風(fēng)險(xiǎn):如果是電平觸發(fā)的中斷,我們需要操作外設(shè)的寄存器才可以讓那個(gè)asserted的電平信號(hào)消失,否則它會(huì)一直持續(xù)。一般,我們都是直接在primary中操作外設(shè)寄存器(slow bus類型的interrupt controller不行),盡早的clear interrupt,但是,對于irq_default_primary_handler,它僅僅是wakeup了threaded interrupt handler,并沒有clear interrupt,這樣,執(zhí)行完了primary handler,外設(shè)中斷仍然是asserted,一旦打開CPU中斷,立刻觸發(fā)下一次的中斷,然后不斷的循環(huán)。因此,如果注冊中斷的時(shí)候沒有指定primary interrupt handler,并且沒有設(shè)定IRQF_ONESHOT,那么系統(tǒng)是會(huì)報(bào)錯(cuò)的。當(dāng)然,有一種情況可以豁免,當(dāng)?shù)讓拥膇rq chip是one shot safe的(IRQCHIP_ONESHOT_SAFE)。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t(6)用戶IRQ flag和底層interrupt flag的同步(TODO)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"em","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"原創(chuàng)文章,轉(zhuǎn)發(fā)請注明出處。蝸窩科技。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"http://www.wowotech.net/linux_kenrel/request_threaded_irq.html","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"標(biāo)簽:\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"request_threaded_irq","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"\t\t? ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"Linux設(shè)備模型(9)_device resource management","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":"\t\t\t\t|\t\t\t\t ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"Linux電源管理(10)_autosleep","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":"?\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"p","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"評論:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"阿孟 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-25 16:42\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"Hi Linuxer,","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"請教一下,","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"“新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行?!?,"nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"如果有的中斷比較頻繁,會(huì)不會(huì)可能丟失,如果剛好有其他中斷已經(jīng)關(guān)閉了CPU中斷。 如果有這樣的情況有什么建議嗎?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"linuxer","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-26 09:55\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@阿孟:這是和系統(tǒng)(HW and SW)設(shè)計(jì)有關(guān)的,我們假設(shè)有硬件A和硬件B,在A的的handler中是關(guān)閉中斷的,因此,這時(shí)候B產(chǎn)生中斷只能體現(xiàn)在中斷控制器的中斷狀態(tài)寄存器上,如果該寄存器不能反應(yīng)多次中斷,那么在由于A handler而關(guān)閉CPU中斷的期間,多次B產(chǎn)生的中斷只能是合成一次。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"如何解決這個(gè)問題?我想可能有下面的方法:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"1、fast handler就是fast handler,不要做額外的事情,盡量快的處理,減少關(guān)閉中斷的時(shí)間","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2、硬件這么快的產(chǎn)生中斷是為何呢?是不是硬件的FIFO不夠大?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"阿孟 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-26 10:58\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@linuxer:是想使用mcu的一個(gè)timer中斷,定時(shí)處理一些事務(wù)。 我這個(gè)timer中斷是不是可能會(huì)導(dǎo)致其他中斷收不到?把timer中斷處理函數(shù)線程化會(huì)不會(huì)好些。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"中斷的原理這一塊不是很了解,我補(bǔ)補(bǔ)課。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"linuxer","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-26 12:48\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@阿孟:不能用softtime timer嗎?為何要用硬件timer的中斷來處理某些定時(shí)的事務(wù)?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"阿孟 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-26 13:29\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@linuxer:這個(gè)也是我們一個(gè)客戶搞的東西,具體為啥用個(gè)硬件timer還沒搞清楚。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"另外","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"還有點(diǎn)疑惑,現(xiàn)在版本linux都會(huì)禁止cpu中斷,那么之前版本linux如果不加IRQF_DISABLED這個(gè)flag是支持中斷嵌套嗎?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"linuxer","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-27 11:54\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@阿孟:是的,slow handler執(zhí)行時(shí)間太長了,必須開中斷,以便在執(zhí)行該handler的時(shí)候有機(jī)會(huì)讓其他類型的中斷handler搶占執(zhí)行。當(dāng)然,同一種類型的中斷不會(huì)嵌套","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"阿孟 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-27 15:17\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@linuxer:明白了,多謝linuxer的解答。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"RobinHsiang","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-10 21:10\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"Hi Linuxer,","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"請教一個(gè)問題,ARM外設(shè)使用GPIO中斷喚醒CPU時(shí),像GPIO配置成輸入,和讓該GPIO關(guān)聯(lián)系統(tǒng)中斷,以及讓GPIO作為中斷喚醒源等等動(dòng)作一定要在外設(shè)驅(qū)動(dòng)的初始化函數(shù)中完成嗎?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"可以在device tree中編寫,然后留一個(gè)類似的接口,讓驅(qū)動(dòng)只單純的調(diào)用一個(gè)函數(shù)可以嗎?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"我其實(shí)遇到的問題是,外設(shè)(中斷源)的驅(qū)動(dòng)是客戶寫的,但是這些系統(tǒng)相關(guān)的他們又不想管。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"linuxer","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-11 09:03\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@RobinHsiang:你問的問題涉及了三個(gè)模塊:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"1、GPIO子系統(tǒng)模塊","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2、電源管理子系統(tǒng)模塊","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"3、中斷子系統(tǒng)模塊","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"實(shí)際上,一個(gè)外設(shè)驅(qū)動(dòng)當(dāng)然需要調(diào)用內(nèi)核各個(gè)子系統(tǒng)的接口來完成自己的具體功能。因此:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"GPIO配置、申請中斷以及設(shè)置wakeup source本身都是驅(qū)動(dòng)功能的一部分。device tree的主要功能是讓系統(tǒng)知道硬件的拓?fù)浣Y(jié)構(gòu),不可能提供一個(gè)一統(tǒng)天下的接口來完成你說的那一系列功能。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"如果外設(shè)驅(qū)動(dòng)是客戶寫的,那么你是否在他撰寫該驅(qū)動(dòng)的時(shí)候仔細(xì)的定義了規(guī)格?我覺得電源管理也是規(guī)格之一的。不過我猜你們和客戶其實(shí)沒有那么正式的合同來約定該驅(qū)動(dòng)的開發(fā),因此,他們只想關(guān)注硬件功能的代碼而不想涉及其他,如果這樣的話,那么該驅(qū)動(dòng)的責(zé)任人應(yīng)該是你們,客戶僅僅是提供示例代碼而已。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"RobinHsiang","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2015-03-11 10:16\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"@linuxer:Thanks..!!","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"確實(shí)是你所說的,這幾個(gè)驅(qū)動(dòng)沒有約定這么詳細(xì),當(dāng)初只是定了大致如何實(shí)現(xiàn)功能。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"關(guān)鍵是這個(gè)客戶專門做這幾個(gè)模塊的研發(fā),而且可能牽涉到一些軍工和政府項(xiàng)目,不開源給我們。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"客戶的建議是系統(tǒng)平臺(tái)部分我們做,他們只open /dev/ttyhsl,至于電源和喚醒部分,我們也提供接口。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"最近也討論了一些方案,比如偵測UART的傳輸情況來關(guān)閉電源和clock啊,或者將系統(tǒng)平臺(tái)的情況寫一些接口出來供他們調(diào)用,但是都感覺怪怪的,也怕實(shí)現(xiàn)不好。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"最終看來,只能是我們將代碼寫好,讓他們做填空題,我們再測試了。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"回復(fù)","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]}]}]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"strong","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[{"nodeName":"a","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"linuxer","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"text","nodeValue":" ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2014-09-25 10:16\t\t\t","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"div","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"text","nodeValue":"你可以先用arm-linux-objdump的命令看看是否你編譯的異常向量表就是這樣的。","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"指令“EAFFFFFE”應(yīng)該是被翻譯成一個(gè)跳轉(zhuǎn)指令,跳轉(zhuǎn)范圍是24-bit的立即數(shù),對于“EAFFFFFE”,顯然還沒有填入這個(gè)立即數(shù),一般而言,當(dāng)你編譯出.o文件的時(shí)候,異常向量表的反匯編結(jié)果就是:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"__vectors_start():","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1134","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?? 0:?? eaffffff????????b?????? 4 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"__vectors_start+0x4","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1135","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?? 4:?? eafffffe????????b?????? 1a0 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_und","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1136","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?? 8:?? e59ffff0????????ldr???? pc, [pc, #4080] ; 1000 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":".vectors+0x1000","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1137","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"?? c:?? eafffffe????????b?????? 120 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_pabt","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1138","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??10:?? eafffffe????????b?????? a0 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_dabt","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1139","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??14:?? ea000086????????b?????? 220 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_swi","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1140","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??18:?? eafffffe????????b?????? 20 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_irq","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"arch/arm/kernel/entry-armv.S:1141","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"??1c:?? ea000087????????b?????? 224 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"vector_fiq_offset","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"這時(shí)候,還沒有l(wèi)ink,因此地址還是reallocated。當(dāng)連接完成,“EAFFFFFE”會(huì)被修正,其24-bit的立即數(shù)的位置會(huì)被填入真正跳轉(zhuǎn)的地址,例如:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"__vectors_start():","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4e4:????ef9f0000 ????svc????0x009f0000","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4e8:????ea0000dd ????b????c000e864 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"early_mem+0x8","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4ec:????e59ff410 ????ldr????pc, [pc, #1040]????; c000e904 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"early_initrd+0x38","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4f0:????ea0000bb ????b????c000e7e4 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"parse_tag_serialnr+0x8","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4f4:????ea00009a ????b????c000e764 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"parse_tag_ramdisk+0x20","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4f8:????ea0000fa ????b????c000e8e8 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"early_initrd+0x1c","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e4fc:????ea000078 ????b????c000e6e4 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"parse_tag_videotext+0x20","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"c000e500:????ea0000f7 ????b????c000e8e4 ","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"early_initrd+0x18","nodeValue":null,"nodeType":"1","display":"block","nodeAttrs":[],"childNodes":[{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"我建議你一步一步的檢查:","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"1、先檢查你編譯出來的kernel image","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"2、完成early_trap_init之后,檢查看看exception table的copy的對不對","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"br","nodeValue":null,"nodeType":"1","display":"inline","nodeAttrs":[],"childNodes":[]},{"nodeName":"text","nodeValue":"3、是否運(yùn)行時(shí),有些代碼誤操作了exception table?","nodeType":"3","display":"inline","nodeAttrs":[],"childNodes":[]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},"imageUrls":["https://image84.360doc.com/DownloadImg/2015/04/0818/52154533_1.gif","https://image84.360doc.com/DownloadImg/2015/04/0818/52154533_2.gif","https://image84.360doc.com/DownloadImg/2015/04/0818/52154533_3.gif","https://image84.360doc.com/DownloadImg/2015/04/0818/52154533_4.gif"],"text":"\n\t一、前言\n\t本文主要的議題是作為一個(gè)普通的驅(qū)動(dòng)工程師,在撰寫自己負(fù)責(zé)的驅(qū)動(dòng)的時(shí)候,如何向Linux Kernel中的中斷子系統(tǒng)注冊中斷處理函數(shù)?為了理解注冊中斷的接口,必須了解一些中斷線程化(threaded interrupt handler)的基礎(chǔ)知識(shí),這些在第二章描述。第三章主要描述了驅(qū)動(dòng)申請 interrupt line接口API request_threaded_irq的規(guī)格。第四章是進(jìn)入request_threaded_irq的實(shí)現(xiàn)細(xì)節(jié),分析整個(gè)代碼的執(zhí)行過程。\n\t二、和中斷相關(guān)的linux實(shí)時(shí)性分析以及中斷線程化的背景介紹\n\t1、非搶占式linux內(nèi)核的實(shí)時(shí)性\n\t在遙遠(yuǎn)的過去,linux2.4之前的內(nèi)核是不支持搶占特性的,具體可以參考下圖: \n\t事情的開始源自高優(yōu)先級任務(wù)(橘色block)由于要等待外部事件(例如網(wǎng)絡(luò)數(shù)據(jù))而進(jìn)入睡眠,調(diào)度器調(diào)度了某個(gè)低優(yōu)先級的任務(wù)(紫色block)執(zhí)行。該低優(yōu)先級任務(wù)歡暢的執(zhí)行,直到觸發(fā)了一次系統(tǒng)調(diào)用(例如通過read()文件接口讀取磁盤上的文件等)而進(jìn)入了內(nèi)核態(tài)。仍然是熟悉的配方,仍然是熟悉的味道,低優(yōu)先級任務(wù)正在執(zhí)行不會(huì)變化,只不過從user space切換到了kernel space。外部事件總是在你不想讓它來的時(shí)候到來,T0時(shí)刻,高優(yōu)先級任務(wù)等待的那個(gè)中斷事件發(fā)生了。\n\t中斷雖然發(fā)生了,但軟件不一定立刻響應(yīng),可能由于在內(nèi)核態(tài)執(zhí)行的某些操作不希望被外部事件打斷而主動(dòng)關(guān)閉了中斷(或是關(guān)閉了CPU的中斷,或者M(jìn)ASK了該IRQ number),這時(shí)候,中斷信號(hào)沒有立刻得到響應(yīng),軟件仍然在內(nèi)核態(tài)執(zhí)行低優(yōu)先級任務(wù)系統(tǒng)調(diào)用的代碼。在T1時(shí)刻,內(nèi)核態(tài)代碼由于退出臨界區(qū)而打開中斷(注意:上圖中的比例是不協(xié)調(diào)的,一般而言,linux kernel不會(huì)有那么長的關(guān)中斷時(shí)間,上面主要是為了表示清楚,同理,從中斷觸發(fā)到具體中斷服務(wù)程序的執(zhí)行也沒有那么長,都是為了表述清楚),中斷一旦打開,立刻跳轉(zhuǎn)到了異常向量地址,interrupt handler搶占了低優(yōu)先級任務(wù)的執(zhí)行,進(jìn)入中斷上下文(雖然這時(shí)候的current task是低優(yōu)先級任務(wù),但是中斷上下文和它沒有任何關(guān)系)。\n\t從CPU開始處理中斷到具體中斷服務(wù)程序被執(zhí)行還需要一個(gè)分發(fā)的過程。這個(gè)期間系統(tǒng)要做的主要操作包括確定HW interrupt ID,確定IRQ Number,ack或者mask中斷,調(diào)用中斷服務(wù)程序等。T0到T2之間的delay被稱為中斷延遲(Interrupt Latency),主要包括兩部分,一部分是HW造成的delay(硬件的中斷系統(tǒng)識(shí)別外部的中斷事件并signal到CPU),另外一部分是軟件原因(內(nèi)核代碼中由于要保護(hù)臨界區(qū)而關(guān)閉中斷引起的)。\n\t該中斷的服務(wù)程序執(zhí)行完畢(在其執(zhí)行過程中,T3時(shí)刻,會(huì)喚醒高優(yōu)先級任務(wù),讓它從sleep狀態(tài)進(jìn)入runable狀態(tài)),返回低優(yōu)先級任務(wù)的系統(tǒng)調(diào)用現(xiàn)場,這時(shí)候并不存在一個(gè)搶占點(diǎn),低優(yōu)先級任務(wù)要完成系統(tǒng)調(diào)用之后,在返回用戶空間的時(shí)候才出現(xiàn)搶占點(diǎn)。漫長的等待之后,T4時(shí)刻,調(diào)度器調(diào)度高優(yōu)先級任務(wù)執(zhí)行。有一個(gè)術(shù)語叫做任務(wù)響應(yīng)時(shí)間(Task Response Time)用來描述T3到T4之間的delay。\n\t2、搶占式linux內(nèi)核的實(shí)時(shí)性\n\t2.6內(nèi)核和2.4內(nèi)核顯著的不同是提供了一個(gè)CONFIG_PREEMPT的選項(xiàng),打開該選項(xiàng)后,linux kernel就支持了內(nèi)核代碼的搶占(當(dāng)然不能在臨界區(qū)),其行為如下: \n\tT0到T3的操作都是和上一節(jié)的描述一樣的,不同的地方是在T4。對于2.4內(nèi)核,只有返回用戶空間的時(shí)候才有搶占點(diǎn)出現(xiàn),但是對于搶占式內(nèi)核而言,即便是從中斷上下文返回內(nèi)核空間的進(jìn)程上下文,只要內(nèi)核代碼不在臨界區(qū)內(nèi),就可以發(fā)生調(diào)度,讓最高優(yōu)先級的任務(wù)調(diào)度執(zhí)行。\n\t在非搶占式linux內(nèi)核中,一個(gè)任務(wù)的內(nèi)核態(tài)是不可以被其他進(jìn)程搶占的。這里并不是說kernel space不可以被搶占,只是說進(jìn)程通過系統(tǒng)調(diào)用陷入到內(nèi)核的時(shí)候,不可以被其他的進(jìn)程搶占。實(shí)際上,中斷上下文當(dāng)然可以搶占進(jìn)程上下文(無論是內(nèi)核態(tài)還是用戶態(tài)),更進(jìn)一步,中斷上下文是擁有至高無上的權(quán)限,它甚至可以搶占其他的中斷上下文。引入搶占式內(nèi)核后,系統(tǒng)的平均任務(wù)響應(yīng)時(shí)間會(huì)縮短,但是,實(shí)時(shí)性更關(guān)注的是:無論在任何的負(fù)載情況下,任務(wù)響應(yīng)時(shí)間是確定的。因此,更需要關(guān)注的是worst-case的任務(wù)響應(yīng)時(shí)間。這里有兩個(gè)因素會(huì)影響worst case latency:\n\t(1)為了同步,內(nèi)核中總有些代碼需要持有自旋鎖資源,或者顯式的調(diào)用preempt_disable來禁止搶占,這時(shí)候不允許搶占\n\t(2)中斷上下文(并非只是中斷handler,還包括softirq、timer、tasklet)總是可以搶占進(jìn)程上下文\n\t因此,即便是打開了PREEMPT的選項(xiàng),實(shí)際上linux系統(tǒng)的任務(wù)響應(yīng)時(shí)間仍然是不確定的。一方面內(nèi)核代碼的臨界區(qū)非常多,我們需要找到,系統(tǒng)中持有鎖,或者禁止搶占的最大的時(shí)間片。另外一方面,在上圖的T4中,能順利的調(diào)度高優(yōu)先級任務(wù)并非易事,這時(shí)候可能有觸發(fā)的軟中斷,也可能有新來的中斷,也可能某些driver的tasklet要執(zhí)行,只有在沒有任何bottom half的任務(wù)要執(zhí)行的時(shí)候,調(diào)度器才會(huì)啟動(dòng)調(diào)度。\n\t3、進(jìn)一步提高linux內(nèi)核的實(shí)時(shí)性\n\t通過上一個(gè)小節(jié)的描述,相信大家都確信中斷對linux 實(shí)時(shí)性的最大的敵人。那么怎么破?我曾經(jīng)接觸過一款RTOS,它的中斷handler非常簡單,就是發(fā)送一個(gè)inter-task message到該driver thread,對任何的一個(gè)驅(qū)動(dòng)都是如此處理。這樣,每個(gè)中斷上下文都變得非常簡短,而且每個(gè)中斷都是一致的。在這樣的設(shè)計(jì)中,外設(shè)中斷的處理線程化了,然后,系統(tǒng)設(shè)計(jì)師要仔細(xì)的為每個(gè)系統(tǒng)中的task分配優(yōu)先級,確保整個(gè)系統(tǒng)的實(shí)時(shí)性。\n\t在Linux kernel中,一個(gè)外設(shè)的中斷處理被分成top half和bottom half,top half進(jìn)行最關(guān)鍵,最基本的處理,而比較耗時(shí)的操作被放到bottom half(softirq、tasklet)中延遲執(zhí)行。雖然bottom half被延遲執(zhí)行,但始終都是先于進(jìn)程執(zhí)行的。為何不讓這些耗時(shí)的bottom half和普通進(jìn)程公平競爭呢?因此,linux kernel借鑒了RTOS的某些特性,對那些耗時(shí)的驅(qū)動(dòng)interrupt handler進(jìn)行線程化處理,在內(nèi)核的搶占點(diǎn)上,讓線程(無論是內(nèi)核線程還是用戶空間創(chuàng)建的線程,還是驅(qū)動(dòng)的interrupt thread)在一個(gè)舞臺(tái)上競爭CPU。\n\t三、request_threaded_irq的接口規(guī)格\n\t1、輸入?yún)?shù)描述\n\t\t\t\t輸入?yún)?shù)\t\t\t\n\t\t\t\t描述\t\t\t\n\t\t\t\tirq\t\t\t\n\t\t\t\t要注冊handler的那個(gè)IRQ number。這里要注冊的handler包括兩個(gè),一個(gè)是傳統(tǒng)意義的中斷handler,我們稱之primary handler,另外一個(gè)是threaded interrupt handler\t\t\t\n\t\t\t\thandler\t\t\t\n\t\t\t\tprimary handler。需要注意的是primary handler和threaded interrupt handler不能同時(shí)為空,否則會(huì)出錯(cuò)\t\t\t\n\t\t\t\tthread_fn\t\t\t\n\t\t\t\tthreaded interrupt handler。如果該參數(shù)不是NULL,那么系統(tǒng)會(huì)創(chuàng)建一個(gè)kernel thread,調(diào)用的function就是thread_fn\t\t\t\n\t\t\t\tirqflags\t\t\t\n\t\t\t\t參見本章第三節(jié)\t\t\t\n\t\t\t\tdevname\t\t\t\n\t\t\t\t?\t\t\t\n\t\t\t\tdev_id\t\t\t\n\t\t\t\t參見第四章,第一節(jié)中的描述。\t\t\t\n\t2、輸出參數(shù)描述\n\t0表示成功執(zhí)行,負(fù)數(shù)表示各種錯(cuò)誤原因。\n\t3、Interrupt type flags\n\t\t\t\tflag定義\t\t\t\n\t\t\t\t描述\t\t\t\n\t\t\t\tIRQF_TRIGGER_XXX\t\t\t\n\t\t\t\t描述該interrupt line觸發(fā)類型的flag\t\t\t\n\t\t\t\tIRQF_DISABLED\t\t\t\n\t\t\t\t首先要說明的是這是一個(gè)廢棄的flag,在新的內(nèi)核中,該flag沒有任何的作用了。具體可以參考:Disabling IRQF_DISABLED 舊的內(nèi)核(2.6.35版本之前)認(rèn)為有兩種interrupt handler:slow handler和fast handle。在request irq的時(shí)候,對于fast handler,需要傳遞IRQF_DISABLED的參數(shù),確保其中斷處理過程中是關(guān)閉CPU的中斷,因?yàn)槭莊ast handler,執(zhí)行很快,即便是關(guān)閉CPU中斷不會(huì)影響系統(tǒng)的性能。但是,并不是每一種外設(shè)中斷的handler都是那么快(例如磁盤),因此就有 slow handler的概念,說明其在中斷處理過程中會(huì)耗時(shí)比較長。對于這種情況,在執(zhí)行interrupt handler的時(shí)候不能關(guān)閉CPU中斷,否則對系統(tǒng)的performance會(huì)有影響。 新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行。\t\t\t\n\t\t\t\tIRQF_SHARED\t\t\t\n\t\t\t\t\t這是flag用來描述一個(gè)interrupt line是否允許在多個(gè)設(shè)備中共享。如果中斷控制器可以支持足夠多的interrupt source,那么在兩個(gè)外設(shè)間共享一個(gè)interrupt request line是不推薦的,畢竟有一些額外的開銷(發(fā)生中斷的時(shí)候要逐個(gè)詢問是不是你的中斷,軟件上就是遍歷action list),因此外設(shè)的irq handler中最好是一開始就啟動(dòng)判斷,看看是否是自己的中斷,如果不是,返回IRQ_NONE,表示這個(gè)中斷不歸我管。 早期PC時(shí)代,使用8259中斷控制器,級聯(lián)的8259最多支持15個(gè)外部中斷,但是PC外設(shè)那么多,因此需要irq share。現(xiàn)在,ARM平臺(tái)上的系統(tǒng)設(shè)計(jì)很少會(huì)采用外設(shè)共享IRQ方式,畢竟一般ARM SOC提供的有中斷功能的GPIO非常的多,足夠用的。 當(dāng)然,如果確實(shí)需要兩個(gè)外設(shè)共享IRQ,那也只能如此設(shè)計(jì)了。對于HW,中斷控制器的一個(gè)interrupt source的引腳要接到兩個(gè)外設(shè)的interrupt request line上,怎么接?直接連接可以嗎?當(dāng)然不行,對于低電平觸發(fā)的情況,我們可以考慮用與門連接中斷控制器和外設(shè)。\t\t\t\t\n\t\t\t\tIRQF_PROBE_SHARED\t\t\t\n\t\t\t\tIRQF_SHARED用來表示該interrupt action descriptor是允許和其他device共享一個(gè)interrupt line(IRQ number),但是實(shí)際上是否能夠share還是需要其他條件:例如觸發(fā)方式必須相同。有些驅(qū)動(dòng)程序可能有這樣的調(diào)用場景:我只是想scan一個(gè)irq table,看看哪一個(gè)是OK的,這時(shí)候,如果即便是不能和其他的驅(qū)動(dòng)程序share這個(gè)interrupt line,我也沒有關(guān)系,我就是想scan看看情況。這時(shí)候,caller其實(shí)可以預(yù)見sharing mismatche的發(fā)生,因此,不需要內(nèi)核打印“Flags mismatch irq……“這樣冗余的信息\t\t\t\n\t\t\t\tIRQF_PERCPU\t\t\t\n\t\t\t\t在SMP的架構(gòu)下,中斷有兩種mode,一種中斷是在所有processor之間共享的,也就是global的,一旦中斷產(chǎn)生,interrupt controller可以把這個(gè)中斷送達(dá)多個(gè)處理器。當(dāng)然,在具體實(shí)現(xiàn)的時(shí)候不會(huì)同時(shí)將中斷送達(dá)多個(gè)CPU,一般是軟件和硬件協(xié)同處理,將中斷送達(dá)一個(gè)CPU處理。但是一段時(shí)間內(nèi)產(chǎn)生的中斷可以平均(或者按照既定的策略)分配到一組CPU上。這種interrupt mode下,interrupt controller針對該中斷的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一個(gè)CPU ack了該中斷,那么其他的CPU看到的該interupt source的狀態(tài)也是已經(jīng)ack的狀態(tài)。 和global對應(yīng)的就是per cpu interrupt了,對于這種interrupt,不是processor之間共享的,而是特定屬于一個(gè)CPU的。例如GIC中interrupt ID等于30的中斷就是per cpu的(這個(gè)中斷event被用于各個(gè)CPU的local timer),這個(gè)中斷號(hào)雖然只有一個(gè),但是,實(shí)際上控制該interrupt ID的寄存器有n組(如果系統(tǒng)中有n個(gè)processor),每個(gè)CPU看到的是不同的控制寄存器。在具體實(shí)現(xiàn)中,這些寄存器組有兩種形態(tài),一種是banked,所有CPU操作同樣的寄存器地址,硬件系統(tǒng)會(huì)根據(jù)訪問的cpu定向到不同的寄存器,另外一種是non banked,也就是說,對于該interrupt source,每個(gè)cpu都有自己獨(dú)特的訪問地址。\t\t\t\n\t\t\t\tIRQF_NOBALANCING\t\t\t\n\t\t\t\t這也是和multi-processor相關(guān)的一個(gè)flag。對于那些可以在多個(gè)CPU之間共享的中斷,具體送達(dá)哪一個(gè)processor是有策略的,我們可以在多個(gè)CPU之間進(jìn)行平衡。如果你不想讓你的中斷參與到irq balancing的過程中那么就設(shè)定這個(gè)flag\t\t\t\n\t\t\t\tIRQF_IRQPOLL\t\t\t\n\t\t\t\t?\t\t\t\n\t\t\t\tIRQF_ONESHOT\t\t\t\n\t\t\t\tone shot本身的意思的只有一次的,結(jié)合到中斷這個(gè)場景,則表示中斷是一次性觸發(fā)的,不能嵌套。對于primary handler,當(dāng)然是不會(huì)嵌套,但是對于threaded interrupt handler,我們有兩種選擇,一種是mask該interrupt source,另外一種是unmask該interrupt source。一旦mask住該interrupt source,那么該interrupt source的中斷在整個(gè)threaded interrupt handler處理過程中都是不會(huì)再次觸發(fā)的,也就是one shot了。這種handler不需要考慮重入問題。 具體是否要設(shè)定one shot的flag是和硬件系統(tǒng)有關(guān)的,我們舉一個(gè)例子,比如電池驅(qū)動(dòng),電池里面有一個(gè)電量計(jì),是使用HDQ協(xié)議進(jìn)行通信的,電池驅(qū)動(dòng)會(huì)注冊一個(gè)threaded interrupt handler,在這個(gè)handler中,會(huì)通過HDQ協(xié)議和電量計(jì)進(jìn)行通信。對于這個(gè)handler,通過HDQ進(jìn)行通信是需要一個(gè)完整的HDQ交互過程,如果中間被打斷,整個(gè)通信過程會(huì)出問題,因此,這個(gè)handler就必須是one shot的。\t\t\t\n\t\t\t\tIRQF_NO_SUSPEND\t\t\t\n\t\t\t\t這個(gè)flag比較好理解,就是說在系統(tǒng)suspend的時(shí)候,不用disable這個(gè)中斷,如果disable,可能會(huì)導(dǎo)致系統(tǒng)不能正常的resume。\t\t\t\n\t\t\t\tIRQF_FORCE_RESUME\t\t\t\n\t\t\t\t在系統(tǒng)resume的過程中,強(qiáng)制必須進(jìn)行enable的動(dòng)作,即便是設(shè)定了IRQF_NO_SUSPEND這個(gè)flag。這是和特定的硬件行為相關(guān)的。\t\t\t\n\t\t\t\tIRQF_NO_THREAD\t\t\t\n\t\t\t\t有些low level的interrupt是不能線程化的(例如系統(tǒng)timer的中斷),這個(gè)flag就是起這個(gè)作用的。另外,有些級聯(lián)的interrupt controller對應(yīng)的IRQ也是不能線程化的(例如secondary GIC對應(yīng)的IRQ),它的線程化可能會(huì)影響一大批附屬于該interrupt controller的外設(shè)的中斷響應(yīng)延遲。\t\t\t\n\t\t\t\tIRQF_EARLY_RESUME\t\t\t\n\t\t\t\t?\t\t\t\n\t\t\t\tIRQF_TIMER\t\t\t\n\t\t\t\t?\t\t\t\n\t四、request_threaded_irq代碼分析\n\t1、request_threaded_irq主流程\n\t\tint request_threaded_irq(unsigned int irq, irq_handler_t handler, ???????????? irq_handler_t thread_fn, unsigned long irqflags, ???????????? const char *devname, void *dev_id) {? ??? if ((irqflags & IRQF_SHARED) && !dev_id)---------(1) ??????? return -EINVAL;\t\n\t\t??? desc = irq_to_desc(irq); -----------------(2) ??? if (!desc)???????? return -EINVAL;\t\n\t\t??? if (!irq_settings_can_request(desc) || ------------(3) ??????? WARN_ON(irq_settings_is_per_cpu_devid(desc))) ??????? return -EINVAL;\t\n\t\t??? if (!handler) { ----------------------(4) ??????? if (!thread_fn) ??????????? return -EINVAL; ??????? handler = irq_default_primary_handler; ??? }\t\n\t\t??? action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);\t\n\t\t??? action->handler = handler; ??? action->thread_fn = thread_fn; ??? action->flags = irqflags; ??? action->name = devname; ??? action->dev_id = dev_id;\t\n\t\t??? chip_bus_lock(desc); ??? retval = __setup_irq(irq, desc, action); -----------(5) ??? chip_bus_sync_unlock(desc); }\t\n\t(1)對于那些需要共享的中斷,在request irq的時(shí)候需要給出dev id,否則會(huì)出錯(cuò)退出。為何對于IRQF_SHARED的中斷必須要給出dev id呢?實(shí)際上,在共享的情況下,一個(gè)IRQ number對應(yīng)若干個(gè)irqaction,當(dāng)操作irqaction的時(shí)候,僅僅給出IRQ number就不是非常的足夠了,這時(shí)候,需要一個(gè)ID表示具體的irqaction,這里就是dev_id的作用了。我們舉一個(gè)例子:\n\t\tvoid free_irq(unsigned int irq, void *dev_id)\t\n\t當(dāng)釋放一個(gè)IRQ資源的時(shí)候,不但要給出IRQ number,還要給出device ID。只有這樣,才能精準(zhǔn)的把要釋放的那個(gè)irqaction 從irq action list上移除。dev_id在中斷處理中有沒有作用呢?我們來看看source code:\n\t\tirqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) {\t\n\t\t??? do { ??????? irqreturn_t res;? ??????? res = action->handler(irq, action->dev_id);\t\n\t\t…… ??????? action = action->next; ??? } while (action);\t\n\t\t…… }\t\n\tlinux interrupt framework雖然支持中斷共享,但是它并不會(huì)協(xié)助解決識(shí)別問題,它只會(huì)遍歷該IRQ number上注冊的irqaction的callback函數(shù),這樣,雖然只是一個(gè)外設(shè)產(chǎn)生的中斷,linux kernel還是把所有共享的那些中斷handler都逐個(gè)調(diào)用執(zhí)行。為了讓系統(tǒng)的performance不受影響,irqaction的callback函數(shù)必須在函數(shù)的最開始進(jìn)行判斷,是否是自己的硬件設(shè)備產(chǎn)生了中斷(讀取硬件的寄存器),如果不是,盡快的退出。\n\t需要注意的是,這里dev_id并不能在中斷觸發(fā)的時(shí)候用來標(biāo)識(shí)需要調(diào)用哪一個(gè)irqaction的callback函數(shù),通過上面的代碼也可以看出,dev_id有些類似一個(gè)參數(shù)傳遞的過程,可以把具體driver的一些硬件信息,組合成一個(gè)structure,在觸發(fā)中斷的時(shí)候可以把這個(gè)structure傳遞給中斷處理函數(shù)。\n\t(2)通過IRQ number獲取對應(yīng)的中斷描述符。在引入CONFIG_SPARSE_IRQ選項(xiàng)后,這個(gè)轉(zhuǎn)換變得不是那么簡單了。在過去,我們會(huì)以IRQ number為index,從irq_desc這個(gè)全局?jǐn)?shù)組中直接獲取中斷描述符。如果配置CONFIG_SPARSE_IRQ選項(xiàng),則需要從radix tree中搜索。CONFIG_SPARSE_IRQ選項(xiàng)的更詳細(xì)的介紹請參考IRQ number和中斷描述符 \n\t(3)并非系統(tǒng)中所有的IRQ number都可以request,有些中斷描述符被標(biāo)記為IRQ_NOREQUEST,標(biāo)識(shí)該IRQ number不能被其他的驅(qū)動(dòng)request。一般而言,這些IRQ number有特殊的作用,例如用于級聯(lián)的那個(gè)IRQ number是不能request。irq_settings_can_request函數(shù)就是判斷一個(gè)IRQ是否可以被request。\n\tirq_settings_is_per_cpu_devid函數(shù)用來判斷一個(gè)中斷描述符是否需要傳遞per cpu的device ID。per cpu的中斷上面已經(jīng)描述的很清楚了,這里不再細(xì)述。如果一個(gè)中斷描述符對應(yīng)的中斷 ID是per cpu的,那么在申請其handler的時(shí)候就有兩種情況,一種是傳遞統(tǒng)一的dev_id參數(shù)(傳入request_threaded_irq的最后一個(gè)參數(shù)),另外一種情況是針對每個(gè)CPU,傳遞不同的dev_id參數(shù)。在這種情況下,我們需要調(diào)用request_percpu_irq接口函數(shù)而不是request_threaded_irq。\n\t(4)傳入request_threaded_irq的primary handler和threaded handler參數(shù)有下面四種組合:\n\t\t\t\tprimary handler\t\t\t\n\t\t\t\tthreaded handler\t\t\t\n\t\t\t\t描述\t\t\t\n\t\t\t\tNULL\t\t\t\n\t\t\t\tNULL\t\t\t\n\t\t\t\t函數(shù)出錯(cuò),返回-EINVAL\t\t\t\n\t\t\t\t設(shè)定\t\t\t\n\t\t\t\t設(shè)定\t\t\t\n\t\t\t\t正常流程。中斷處理被合理的分配到primary handler和threaded handler中。\t\t\t\n\t\t\t\t設(shè)定\t\t\t\n\t\t\t\tNULL\t\t\t\n\t\t\t\t中斷處理都是在primary handler中完成\t\t\t\n\t\t\t\tNULL\t\t\t\n\t\t\t\t設(shè)定\t\t\t\n\t\t\t\t這種情況下,系統(tǒng)會(huì)幫忙設(shè)定一個(gè)default的primary handler:irq_default_primary_handler,協(xié)助喚醒threaded handler線程\t\t\t\n\t(5)這部分的代碼很簡單,分配struct irqaction,賦值,調(diào)用__setup_irq進(jìn)行實(shí)際的注冊過程。這里要羅嗦幾句的是鎖的操作,在內(nèi)核中,有很多函數(shù),有的是需要調(diào)用者自己加鎖保護(hù)的,有些是不需要加鎖保護(hù)的。對于這些場景,linux kernel采取了統(tǒng)一的策略:基本函數(shù)名字是一樣的,只不過需要調(diào)用者自己加鎖保護(hù)的那個(gè)函數(shù)需要增加__的前綴,例如內(nèi)核有有下面兩個(gè)函數(shù):setup_irq和__setup_irq。這里,我們在setup irq的時(shí)候已經(jīng)調(diào)用chip_bus_lock進(jìn)行保護(hù),因此使用lock free的版本__setup_irq。\n\tchip_bus_lock定義如下:\n\t\tstatic inline void chip_bus_lock(struct irq_desc *desc) { ??? if (unlikely(desc->irq_data.chip->irq_bus_lock)) ??????? desc->irq_data.chip->irq_bus_lock(&desc->irq_data); }\t\n\t大部分的interrupt controller并沒有定義irq_bus_lock這個(gè)callback函數(shù),因此chip_bus_lock這個(gè)函數(shù)對大多數(shù)的中斷控制器而言是沒有實(shí)際意義的。但是,有些interrupt controller是連接到慢速總線上的,例如一個(gè)i2c接口的IO expander芯片(這種芯片往往也提供若干有中斷功能的GPIO,因此也是一個(gè)interrupt controller),在訪問這種interrupt controller的時(shí)候需要lock住那個(gè)慢速bus(只能有一個(gè)client在使用I2C bus)。\n\t2、注冊irqaction\n\t(1)nested IRQ的處理代碼\n\t在看具體的代碼之前,我們首先要理解什么是nested IRQ。nested IRQ不是cascade IRQ,在GIC代碼分析中我們有描述過cascade IRQ這個(gè)概念,主要用在interrupt controller級聯(lián)的情況下。為了方便大家理解,我還是給出一個(gè)具體的例子吧,具體的HW block請參考下圖: \n\t上圖是一個(gè)兩個(gè)GIC級聯(lián)的例子,所有的HW block封裝在了一個(gè)SOC chip中。為了方便描述,我們先進(jìn)行編號(hào):Secondary GIC的IRQ number是A,外設(shè)1的IRQ number是B,外設(shè)2的IRQ number是C。對于上面的硬件,linux kernel處理如下:\n\t(a)IRQ A的中斷描述符被設(shè)定為不能注冊irqaction(不能注冊specific interrupt handler,或者叫中斷服務(wù)程序)\n\t(b)IRQ A的highlevel irq-events handler(處理interrupt flow control)負(fù)責(zé)進(jìn)行IRQ number的映射,在其irq domain上翻譯出具體外設(shè)的IRQ number,并重新定向到外設(shè)IRQ number對應(yīng)的highlevel irq-events handler。\n\t(c)所有外設(shè)驅(qū)動(dòng)的中斷正常request irq,可以任意選擇線程化的handler,或者只注冊primary handler。\n\t需要注意的是,對root GIC和Secondary GIC寄存器的訪問非常快,因此ack、mask、EOI等操作也非??臁n\t我們再看看另外一個(gè)interrupt controller級聯(lián)的情況: \n\tIO expander HW block提供了有中斷功能的GPIO,因此它也是一個(gè)interrupt controller,有它自己的irq domain和irq chip。上圖中外設(shè)1和外設(shè)2使用了IO expander上有中斷功能的GPIO,它們有屬于自己的IRQ number以及中斷描述符。IO expander HW block的IRQ line連接到SOC內(nèi)部的interrupt controller上,因此,這也是一個(gè)interrupt controller級聯(lián)的情況,對于這種情況,我們是否可以采用和上面GIC級聯(lián)的處理方式呢?\n\t不行,對于GIC級聯(lián)的情況,如果secondary GIC上的外設(shè)1產(chǎn)生了中斷,整個(gè)關(guān)中斷的時(shí)間是IRQ A的中斷描述符的highlevel irq-events handler處理時(shí)間+I(xiàn)RQ B的的中斷描述符的highlevel irq-events handler處理時(shí)間+外設(shè)1的primary handler的處理時(shí)間。所幸對root GIC和Secondary GIC寄存器的訪問非??欤虼苏麄€(gè)關(guān)中斷的時(shí)間也不是非常的長。但是如果是IO expander這個(gè)情況,如果采取和上面GIC級聯(lián)的處理方式一樣的話,關(guān)中斷的時(shí)間非常長。我們還是用外設(shè)1產(chǎn)生的中斷為例子好了。這時(shí)候,由于IRQ B的的中斷描述符的highlevel irq-events handler處理設(shè)計(jì)I2C的操作,因此時(shí)間非常的長,這時(shí)候,對于整個(gè)系統(tǒng)的實(shí)時(shí)性而言是致命的打擊。對這種硬件情況,linux kernel處理如下:\n\t(a)IRQ A的中斷描述符的highlevel irq-events handler根據(jù)實(shí)際情況進(jìn)行設(shè)定,并且允許注冊irqaction。對于連接到IO expander上的外設(shè),它是沒有real time的要求的(否則也不會(huì)接到IO expander上),因此一般會(huì)進(jìn)行線程化處理。由于threaded handler中涉及I2C操作,因此要設(shè)定IRQF_ONESHOT的flag。\n\t(b)在IRQ A的中斷描述符的threaded interrupt handler中進(jìn)行進(jìn)行IRQ number的映射,在IO expander irq domain上翻譯出具體外設(shè)的IRQ number,并直接調(diào)用handle_nested_irq函數(shù)處理該IRQ。\n\t(c)外設(shè)對應(yīng)的中斷描述符設(shè)定IRQ_NESTED_THREAD的flag,表明這是一個(gè)nested IRQ。nested IRQ沒有highlevel irq-events handler,也沒有primary handler,它的threaded interrupt handler是附著在其parent IRQ的threaded handler上的。\n\t具體的nested IRQ的處理代碼如下:\n\t\tstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) {\t\n\t\t…… ??? nested = irq_settings_is_nested_thread(desc); ??? if (nested) { ??????? if (!new->thread_fn) { ??????????? ret = -EINVAL; ??????????? goto out_mput; ??????? } ??????? new->handler = irq_nested_primary_handler; ??? } else {? …… ??? }\t\n\t\t……\t\n\t\t}\t\n\t如果一個(gè)中斷描述符是nested thread type的,說明這個(gè)中斷描述符應(yīng)該設(shè)定threaded interrupt handler(當(dāng)然,內(nèi)核是不會(huì)單獨(dú)創(chuàng)建一個(gè)thread的,它是借著其parent IRQ的interrupt thread執(zhí)行),否則就會(huì)出錯(cuò)返回。對于primary handler,它應(yīng)該沒有機(jī)會(huì)被調(diào)用到,當(dāng)然為了調(diào)試,kernel將其設(shè)定為irq_nested_primary_handler,以便在調(diào)用的時(shí)候打印一些信息,讓工程師直到發(fā)生了什么狀況。\n\t(2)forced irq threading處理\n\t具體的forced irq threading的處理代碼如下:\n\t\tstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) {\t\n\t\t…… ??? nested = irq_settings_is_nested_thread(desc); ??? if (nested) {? …… ??? } else { ??????? if (irq_settings_can_thread(desc)) ??????????? irq_setup_forced_threading(new); ??? }\t\n\t\t……\t\n\t\t}\t\n\tforced irq threading其實(shí)就是將系統(tǒng)中所有可以被線程化的中斷handler全部線程化,即便你在request irq的時(shí)候,設(shè)定的是primary handler,而不是threaded handler。當(dāng)然那些不能被線程化的中斷(標(biāo)注了IRQF_NO_THREAD的中斷,例如系統(tǒng)timer)還是排除在外的。irq_settings_can_thread函數(shù)就是判斷一個(gè)中斷是否可以被線程化,如果可以的話,則調(diào)用irq_setup_forced_threading在set irq的時(shí)候強(qiáng)制進(jìn)行線程化。具體代碼如下:\n\t\tstatic void irq_setup_forced_threading(struct irqaction *new) { ??? if (!force_irqthreads)-------------------------------(a) ??????? return; ??? if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))-------(b) ??????? return;\t\n\t\t??? new->flags |= IRQF_ONESHOT; -------------------------(d)\t\n\t\t??? if (!new->thread_fn) {-------------------------------(c) ??????? set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); ??????? new->thread_fn = new->handler; ??????? new->handler = irq_default_primary_handler; ??? } }\t\n\t(a)系統(tǒng)中有一個(gè)強(qiáng)制線程化的選項(xiàng):CONFIG_IRQ_FORCED_THREADING,如果沒有打開該選項(xiàng),force_irqthreads總是0,因此irq_setup_forced_threading也就沒有什么作用,直接return了。如果打開了CONFIG_IRQ_FORCED_THREADING,說明系統(tǒng)支持強(qiáng)制線程化,但是具體是否對所有的中斷進(jìn)行強(qiáng)制線程化處理還是要看命令行參數(shù)threadirqs。如果kernel啟動(dòng)的時(shí)候沒有傳入該參數(shù),那么同樣的,irq_setup_forced_threading也就沒有什么作用,直接return了。只有bootloader向內(nèi)核傳入threadirqs這個(gè)命令行參數(shù),內(nèi)核才真正在啟動(dòng)過程中,進(jìn)行各個(gè)中斷的強(qiáng)制線程化的操作。\n\t(b)看到IRQF_NO_THREAD選項(xiàng)你可能會(huì)奇怪,前面irq_settings_can_thread函數(shù)不是檢查過了嗎?為何還要重復(fù)檢查?其實(shí)一個(gè)中斷是否可以進(jìn)行線程化可以從兩個(gè)層面看:一個(gè)是從底層看,也就是從中斷描述符、從實(shí)際的中斷硬件拓?fù)涞确矫婵?。另外一個(gè)是從中斷子系統(tǒng)的用戶層面看,也就是各個(gè)外設(shè)在注冊自己的handler的時(shí)候是否想進(jìn)行線程化處理。所有的IRQF_XXX都是從用戶層面看的flag,因此如果用戶通過IRQF_NO_THREAD這個(gè)flag告知kernel,該interrupt不能被線程化,那么強(qiáng)制線程化的機(jī)制還是尊重用戶的選擇的。\n\tPER CPU的中斷都是一些較為特殊的中斷,不是一般意義上的外設(shè)中斷,因此對PER CPU的中斷不強(qiáng)制進(jìn)行線程化。IRQF_ONESHOT選項(xiàng)說明該中斷已經(jīng)被線程化了(而且是特殊的one shot類型的),因此也是直接返回了。\n\t(c)強(qiáng)制線程化只對那些沒有設(shè)定thread_fn的中斷進(jìn)行處理,這種中斷將全部的處理放在了primary interrupt handler中(當(dāng)然,如果中斷處理比較耗時(shí),那么也可能會(huì)采用bottom half的機(jī)制),由于primary interrupt handler是全程關(guān)閉CPU中斷的,因此可能對系統(tǒng)的實(shí)時(shí)性造成影響,因此考慮將其強(qiáng)制線程化。struct irqaction中的thread_flags是和線程相關(guān)的flag,我們給它打上IRQTF_FORCED_THREAD的標(biāo)簽,表明該threaded handler是被強(qiáng)制threaded的。new->thread_fn = new->handler這段代碼表示將原來primary handler中的內(nèi)容全部放到threaded handler中處理,新的primary handler被設(shè)定為default handler。\n\t(d)強(qiáng)制線程化是一個(gè)和實(shí)時(shí)性相關(guān)的選項(xiàng),從我的角度來看是一個(gè)很不好的設(shè)計(jì)(個(gè)人觀點(diǎn)),各個(gè)驅(qū)動(dòng)工程師在撰寫自己的驅(qū)動(dòng)代碼的時(shí)候已經(jīng)安排好了自己的上下文環(huán)境。有的是進(jìn)程上下文,有的是中斷上下文,在各自的上下文環(huán)境中,驅(qū)動(dòng)工程師又選擇了適合的內(nèi)核同步機(jī)制。但是,強(qiáng)制線程化導(dǎo)致原來運(yùn)行在中斷上下文的primary handler現(xiàn)在運(yùn)行在進(jìn)程上下文,這有可能導(dǎo)致一些難以跟蹤和定位的bug。\n\t當(dāng)然,作為內(nèi)核的開發(fā)者,既然同意將強(qiáng)制線程化這個(gè)特性并入linux kernel,相信他們有他們自己的考慮。我猜測這是和一些舊的驅(qū)動(dòng)代碼維護(hù)相關(guān)的。linux kernel中的中斷子系統(tǒng)的API的修改會(huì)引起非常大的震動(dòng),因?yàn)閮?nèi)核中成千上萬的驅(qū)動(dòng)都是需要調(diào)用舊的接口來申請linux kernel中斷子系統(tǒng)的服務(wù),對每一個(gè)驅(qū)動(dòng)都進(jìn)行修改是一個(gè)非常耗時(shí)的工作,為了讓那些舊的驅(qū)動(dòng)代碼可以運(yùn)行在新的中斷子系統(tǒng)上,因此,在kernel中,實(shí)際上仍然提供了舊的request_irq接口函數(shù),如下:\n\t\tstatic inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, ??????? const char *name, void *dev) { ??? return request_threaded_irq(irq, handler, NULL, flags, name, dev); }\t\n\t接口是OK了,但是,新的中斷子系統(tǒng)的思路是將中斷處理分成primary handler和threaded handler,而舊的驅(qū)動(dòng)代碼一般是將中斷處理分成top half和bottom half,如何將這部分的不同抹平?linux kernel是這樣處理的(這是我個(gè)人的理解,不保證是正確的):\n\t(d-1)內(nèi)核為那些被強(qiáng)制線程化的中斷handler設(shè)定了IRQF_ONESHOT的標(biāo)識(shí)。這是因?yàn)樵谂f的中斷處理機(jī)制中,top half是不可重入的,強(qiáng)制線程化之后,強(qiáng)制設(shè)定IRQF_ONESHOT可以保證threaded handler是不會(huì)重入的。\n\t(d-2)在那些被強(qiáng)制線程化的中斷線程中,disable bottom half的處理。這是因?yàn)樵谂f的中斷處理機(jī)制中,botton half是不可能搶占top half的執(zhí)行,強(qiáng)制線程化之后,應(yīng)該保持這一點(diǎn)。\n\t(3)創(chuàng)建interrupt線程。代碼如下:\n\t\tif (new->thread_fn && !nested) { ??? struct task_struct *t; ??? static const struct sched_param param = { ??????? .sched_priority = MAX_USER_RT_PRIO/2, ??? };\t\n\t\t??? t = kthread_create(irq_thread, new, \"irq/%d-%s\", irq,------------------(a) ?????????????? new->name);\t\n\t\t??? sched_setscheduler_nocheck(t, SCHED_FIFO, ?m);\t??? get_task_struct(t);---------------------------(b) ??? new->thread = t; ??? set_bit(IRQTF_AFFINITY, &new->thread_flags);---------------(c) }\t\n\t\tif (!alloc_cpumask_var(&mask, GFP_KERNEL)) {----------------(d) ??? ret = -ENOMEM; ??? goto out_thread; } if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)-----------(e) ??? new->flags &= ~IRQF_ONESHOT;\t\n\t(a)調(diào)用kthread_create來創(chuàng)建一個(gè)內(nèi)核線程,并調(diào)用sched_setscheduler_nocheck來設(shè)定這個(gè)中斷線程的調(diào)度策略和調(diào)度優(yōu)先級。這些是和進(jìn)程管理相關(guān)的內(nèi)容,我們留到下一個(gè)專題再詳細(xì)描述吧。\n\t(b)調(diào)用get_task_struct可以為這個(gè)threaded handler的task struct增加一次reference count,這樣,即便是該thread異常退出也可以保證它的task struct不會(huì)被釋放掉。這可以保證中斷系統(tǒng)的代碼不會(huì)訪問到一些被釋放的內(nèi)存。irqaction的thread 成員被設(shè)定為剛剛創(chuàng)建的task,這樣,primary handler就知道喚醒哪一個(gè)中斷線程了。\n\t(c)設(shè)定IRQTF_AFFINITY的標(biāo)志,在threaded handler中會(huì)檢查該標(biāo)志并進(jìn)行IRQ affinity的設(shè)定。\n\t(d)分配一個(gè)cpu mask的變量的內(nèi)存,后面會(huì)使用到\n\t(e)驅(qū)動(dòng)工程師是撰寫具體外設(shè)驅(qū)動(dòng)的,他/她未必會(huì)了解到底層的一些具體的interrupt controller的信息。有些interrupt controller(例如MSI based interrupt)本質(zhì)上就是就是one shot的(通過IRQCHIP_ONESHOT_SAFE標(biāo)記),因此驅(qū)動(dòng)工程師設(shè)定的IRQF_ONESHOT其實(shí)是畫蛇添足,因此可以去掉。\n\t(4)共享中斷的檢查。代碼如下:\n\t\told_ptr = &desc->action; old = *old_ptr;\t\n\t\tif (old) { ??? if (!((old->flags & new->flags) & IRQF_SHARED) ||-----------------(a) ??????? ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || ??????? ((old->flags ^ new->flags) & IRQF_ONESHOT)) ??????? goto mismatch;\t\n\t\t??? /* All handlers must agree on per-cpuness */ ??? if ((old->flags & IRQF_PERCPU) != (new->flags & IRQF_PERCPU)) ??????? goto mismatch;\t\n\t\t??? /* add new interrupt at end of irq queue */ ??? do {------------------------------------(b) ??????? thread_mask |= old->thread_mask; ??????? old_ptr = &old->next; ??????? old = *old_ptr; ??? } while (old); ??? shared = 1; }\t\n\t(a)old指向注冊之前的action list,如果不是NULL,那么說明需要共享interrupt line。但是如果要共享,需要每一個(gè)irqaction都同意共享(IRQF_SHARED),每一個(gè)irqaction的觸發(fā)方式相同(都是level trigger或者都是edge trigger),相同的oneshot類型的中斷(都是one shot或者都不是),per cpu類型的相同中斷(都是per cpu的中斷或者都不是)。\n\t(b)將該irqaction掛入隊(duì)列的尾部。\n\t(5)thread mask的設(shè)定。代碼如下:\n\t\tif (new->flags & IRQF_ONESHOT) { ??????? if (thread_mask == ~0UL) {------------------------(a) ??????????? ret = -EBUSY; ??????????? goto out_mask; ??????? } ??????? new->thread_mask = 1 \n\t\t??? } else if (new->handler == irq_default_primary_handler && ?????????? !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {--------(b) ??????? ret = -EINVAL; ??????? goto out_mask; ??? }\t\n\t對于one shot類型的中斷,我們還需要設(shè)定thread mask。如果一個(gè)one shot類型的中斷只有一個(gè)threaded handler(不支持共享),那么事情就很簡單(臨時(shí)變量thread_mask等于0),該irqaction的thread_mask成員總是使用第一個(gè)bit來標(biāo)識(shí)該irqaction。但是,如果支持共享的話,事情變得有點(diǎn)復(fù)雜。我們假設(shè)這個(gè)one shot類型的IRQ上有A,B和C三個(gè)irqaction,那么A,B和C三個(gè)irqaction的thread_mask成員會(huì)有不同的bit來標(biāo)識(shí)自己。例如A的thread_mask成員是0x01,B的是0x02,C的是0x04,如果有更多共享的irqaction(必須是oneshot類型),那么其thread_mask成員會(huì)依次設(shè)定為0x08,0x10……\n\t(a)在上面“共享中斷的檢查”這個(gè)section中,thread_mask變量保存了所有的屬于該interrupt line的thread_mask,這時(shí)候,如果thread_mask變量如果是全1,那么說明irqaction list上已經(jīng)有了太多的irq action(大于32或者64,和具體系統(tǒng)和編譯器相關(guān))。如果沒有滿,那么通過ffz函數(shù)找到第一個(gè)為0的bit作為該irq action的thread bit mask。\n\t(b)irq_default_primary_handler的代碼如下:\n\t\tstatic irqreturn_t irq_default_primary_handler(int irq, void *dev_id) { ??? return IRQ_WAKE_THREAD; }\t\n\t代碼非常的簡單,返回IRQ_WAKE_THREAD,讓kernel喚醒threaded handler就OK了。使用irq_default_primary_handler雖然簡單,但是有一個(gè)風(fēng)險(xiǎn):如果是電平觸發(fā)的中斷,我們需要操作外設(shè)的寄存器才可以讓那個(gè)asserted的電平信號(hào)消失,否則它會(huì)一直持續(xù)。一般,我們都是直接在primary中操作外設(shè)寄存器(slow bus類型的interrupt controller不行),盡早的clear interrupt,但是,對于irq_default_primary_handler,它僅僅是wakeup了threaded interrupt handler,并沒有clear interrupt,這樣,執(zhí)行完了primary handler,外設(shè)中斷仍然是asserted,一旦打開CPU中斷,立刻觸發(fā)下一次的中斷,然后不斷的循環(huán)。因此,如果注冊中斷的時(shí)候沒有指定primary interrupt handler,并且沒有設(shè)定IRQF_ONESHOT,那么系統(tǒng)是會(huì)報(bào)錯(cuò)的。當(dāng)然,有一種情況可以豁免,當(dāng)?shù)讓拥膇rq chip是one shot safe的(IRQCHIP_ONESHOT_SAFE)。\n\t(6)用戶IRQ flag和底層interrupt flag的同步(TODO)原創(chuàng)文章,轉(zhuǎn)發(fā)請注明出處。蝸窩科技。http://www.wowotech.net/linux_kenrel/request_threaded_irq.html 標(biāo)簽:\trequest_threaded_irq\t\t? Linux設(shè)備模型(9)_device resource management\n\t\t\t\t|\t\t\t\t Linux電源管理(10)_autosleep?\t評論:阿孟 2015-03-25 16:42\t\t\tHi Linuxer,請教一下,“新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行?!比绻械闹袛啾容^頻繁,會(huì)不會(huì)可能丟失,如果剛好有其他中斷已經(jīng)關(guān)閉了CPU中斷。 如果有這樣的情況有什么建議嗎?回復(fù)linuxer 2015-03-26 09:55\t\t\t@阿孟:這是和系統(tǒng)(HW and SW)設(shè)計(jì)有關(guān)的,我們假設(shè)有硬件A和硬件B,在A的的handler中是關(guān)閉中斷的,因此,這時(shí)候B產(chǎn)生中斷只能體現(xiàn)在中斷控制器的中斷狀態(tài)寄存器上,如果該寄存器不能反應(yīng)多次中斷,那么在由于A handler而關(guān)閉CPU中斷的期間,多次B產(chǎn)生的中斷只能是合成一次。如何解決這個(gè)問題?我想可能有下面的方法:1、fast handler就是fast handler,不要做額外的事情,盡量快的處理,減少關(guān)閉中斷的時(shí)間2、硬件這么快的產(chǎn)生中斷是為何呢?是不是硬件的FIFO不夠大?回復(fù)阿孟 2015-03-26 10:58\t\t\t@linuxer:是想使用mcu的一個(gè)timer中斷,定時(shí)處理一些事務(wù)。 我這個(gè)timer中斷是不是可能會(huì)導(dǎo)致其他中斷收不到?把timer中斷處理函數(shù)線程化會(huì)不會(huì)好些。中斷的原理這一塊不是很了解,我補(bǔ)補(bǔ)課?;貜?fù)linuxer 2015-03-26 12:48\t\t\t@阿孟:不能用softtime timer嗎?為何要用硬件timer的中斷來處理某些定時(shí)的事務(wù)?回復(fù)阿孟 2015-03-26 13:29\t\t\t@linuxer:這個(gè)也是我們一個(gè)客戶搞的東西,具體為啥用個(gè)硬件timer還沒搞清楚。另外還有點(diǎn)疑惑,現(xiàn)在版本linux都會(huì)禁止cpu中斷,那么之前版本linux如果不加IRQF_DISABLED這個(gè)flag是支持中斷嵌套嗎?回復(fù)linuxer 2015-03-27 11:54\t\t\t@阿孟:是的,slow handler執(zhí)行時(shí)間太長了,必須開中斷,以便在執(zhí)行該handler的時(shí)候有機(jī)會(huì)讓其他類型的中斷handler搶占執(zhí)行。當(dāng)然,同一種類型的中斷不會(huì)嵌套回復(fù)阿孟 2015-03-27 15:17\t\t\t@linuxer:明白了,多謝linuxer的解答。RobinHsiang 2015-03-10 21:10\t\t\tHi Linuxer,請教一個(gè)問題,ARM外設(shè)使用GPIO中斷喚醒CPU時(shí),像GPIO配置成輸入,和讓該GPIO關(guān)聯(lián)系統(tǒng)中斷,以及讓GPIO作為中斷喚醒源等等動(dòng)作一定要在外設(shè)驅(qū)動(dòng)的初始化函數(shù)中完成嗎?可以在device tree中編寫,然后留一個(gè)類似的接口,讓驅(qū)動(dòng)只單純的調(diào)用一個(gè)函數(shù)可以嗎?我其實(shí)遇到的問題是,外設(shè)(中斷源)的驅(qū)動(dòng)是客戶寫的,但是這些系統(tǒng)相關(guān)的他們又不想管?;貜?fù)linuxer 2015-03-11 09:03\t\t\t@RobinHsiang:你問的問題涉及了三個(gè)模塊:1、GPIO子系統(tǒng)模塊2、電源管理子系統(tǒng)模塊3、中斷子系統(tǒng)模塊實(shí)際上,一個(gè)外設(shè)驅(qū)動(dòng)當(dāng)然需要調(diào)用內(nèi)核各個(gè)子系統(tǒng)的接口來完成自己的具體功能。因此:GPIO配置、申請中斷以及設(shè)置wakeup source本身都是驅(qū)動(dòng)功能的一部分。device tree的主要功能是讓系統(tǒng)知道硬件的拓?fù)浣Y(jié)構(gòu),不可能提供一個(gè)一統(tǒng)天下的接口來完成你說的那一系列功能。如果外設(shè)驅(qū)動(dòng)是客戶寫的,那么你是否在他撰寫該驅(qū)動(dòng)的時(shí)候仔細(xì)的定義了規(guī)格?我覺得電源管理也是規(guī)格之一的。不過我猜你們和客戶其實(shí)沒有那么正式的合同來約定該驅(qū)動(dòng)的開發(fā),因此,他們只想關(guān)注硬件功能的代碼而不想涉及其他,如果這樣的話,那么該驅(qū)動(dòng)的責(zé)任人應(yīng)該是你們,客戶僅僅是提供示例代碼而已?;貜?fù)RobinHsiang 2015-03-11 10:16\t\t\t@linuxer:Thanks..!!確實(shí)是你所說的,這幾個(gè)驅(qū)動(dòng)沒有約定這么詳細(xì),當(dāng)初只是定了大致如何實(shí)現(xiàn)功能。關(guān)鍵是這個(gè)客戶專門做這幾個(gè)模塊的研發(fā),而且可能牽涉到一些軍工和政府項(xiàng)目,不開源給我們??蛻舻慕ㄗh是系統(tǒng)平臺(tái)部分我們做,他們只open /dev/ttyhsl,至于電源和喚醒部分,我們也提供接口。最近也討論了一些方案,比如偵測UART的傳輸情況來關(guān)閉電源和clock啊,或者將系統(tǒng)平臺(tái)的情況寫一些接口出來供他們調(diào)用,但是都感覺怪怪的,也怕實(shí)現(xiàn)不好。最終看來,只能是我們將代碼寫好,讓他們做填空題,我們再測試了?;貜?fù)linuxer 2014-09-25 10:16\t\t\t你可以先用arm-linux-objdump的命令看看是否你編譯的異常向量表就是這樣的。指令“EAFFFFFE”應(yīng)該是被翻譯成一個(gè)跳轉(zhuǎn)指令,跳轉(zhuǎn)范圍是24-bit的立即數(shù),對于“EAFFFFFE”,顯然還沒有填入這個(gè)立即數(shù),一般而言,當(dāng)你編譯出.o文件的時(shí)候,異常向量表的反匯編結(jié)果就是:__vectors_start():arch/arm/kernel/entry-armv.S:1134?? 0:?? eaffffff????????b?????? 4 arch/arm/kernel/entry-armv.S:1135?? 4:?? eafffffe????????b?????? 1a0 arch/arm/kernel/entry-armv.S:1136?? 8:?? e59ffff0????????ldr???? pc, [pc, #4080] ; 1000 arch/arm/kernel/entry-armv.S:1137?? c:?? eafffffe????????b?????? 120 arch/arm/kernel/entry-armv.S:1138??10:?? eafffffe????????b?????? a0 arch/arm/kernel/entry-armv.S:1139??14:?? ea000086????????b?????? 220 arch/arm/kernel/entry-armv.S:1140??18:?? eafffffe????????b?????? 20 arch/arm/kernel/entry-armv.S:1141??1c:?? ea000087????????b?????? 224 這時(shí)候,還沒有l(wèi)ink,因此地址還是reallocated。當(dāng)連接完成,“EAFFFFFE”會(huì)被修正,其24-bit的立即數(shù)的位置會(huì)被填入真正跳轉(zhuǎn)的地址,例如:__vectors_start():c000e4e4:????ef9f0000 ????svc????0x009f0000c000e4e8:????ea0000dd ????b????c000e864 c000e4ec:????e59ff410 ????ldr????pc, [pc, #1040]????; c000e904 c000e4f0:????ea0000bb ????b????c000e7e4 c000e4f4:????ea00009a ????b????c000e764 c000e4f8:????ea0000fa ????b????c000e8e8 c000e4fc:????ea000078 ????b????c000e6e4 c000e500:????ea0000f7 ????b????c000e8e4 我建議你一步一步的檢查:1、先檢查你編譯出來的kernel image2、完成early_trap_init之后,檢查看看exception table的copy的對不對3、是否運(yùn)行時(shí),有些代碼誤操作了exception table?"}
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
linux中斷之中斷注冊
中斷二 C實(shí)現(xiàn)
Linux中斷基礎(chǔ)構(gòu)架
Linux中斷(interrupt)子系統(tǒng)之一:中斷系統(tǒng)基本原理
linux中斷處理之IRQ中斷
Linux 內(nèi)核中斷內(nèi)幕
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服