Linux設備驅動開發基礎---新驅動程序添加到內核方法(2)

一 . 概述:

在 linux 內核中增加程序需要完成以下三項工作:

1. 將編寫的源代碼複製到 Linux 內核源代碼的相應目錄

2. 在目錄的 Kconfig 文件中增加新源代碼對應項目的編譯配置選項

3. 在目錄的 Makefile 文件中增加對新源代碼的編譯條目

二 . 以mini2440中ADC驅動的添加爲例來進行說明

ADC設備在Linux中可以看做是簡單的字符設備,也可以當做是一混雜設備(misc設備),這裏我們就看做是misc設備來實現ADC的驅動。

1. 先把驅動代碼 s3c24xx-adc.h和mini2440_adc.c源代碼複製linux-2.6.32.15\drivers\misc目錄下。 

首先你要清楚你的模塊應在內核源代碼樹中處於何處。

1> 設備驅動程序存放在內核源碼樹根目錄 drivers/ 的子目錄下,在其內部,設備驅動文件進一步按照類別,類型等有序地組織起來。

a. 字符設備存在於 drivers/char/ 目錄下

b. 塊設備存放在 drivers/block/ 目錄下

c.USB 設備則存放在 drivers/usb/ 目錄下。

注意:

(1) 此處的文件組織規則並非絕對不變,例如: USB 設備也屬於字符設備,也可以存放在 drivers/usb/ 目錄下。

(2) 例如我們把驅動程序存放在 drivers/misc/ 目錄下,那麼你要注意,在該目錄下同時會存在大量的 C 源代碼文件和許多其他目錄。所有對於僅僅只有一兩個源文件的設備驅動程序,可以直接存放在該目錄下,但如果驅動程序包含許多源文件和其他輔助文件,那麼可以創建一個新子目錄。

2. 修改 misc 目錄下的 Kconfig 和 Makefile

(1) 在目錄的Kconfig文件中增加ADC的編譯配置選項,如下所示:

sudo gedit Kconfig

添加下面一句後

configMINI2440_ADC
bool "ADC driver for FriendlyARM Mini2440 development boards"
depends on MACH_MINI2440
default y if MACH_MINI2440
help
       this is ADC driver for FriendlyARMMini2440 development boards
       Notes: the touch-screen-driver requiredthis option

上述 Kconfig 文件的這段腳本意味着只有在MACH_MINI2440項目被配置的情況下,纔會出現MINI2440_ADC配置項目,這個配置項目爲布爾型(要麼編譯入內核,要麼不編譯,選項爲“Y”或“N” ) ,菜單上顯示的字符串爲“ADC driver for FriendlyARMMini2440 development boards” ,“help”後面的內容爲幫助信息。

除了布爾型的配置項目外,還存在一種三態型(tristate)配置選項,它意味着要麼編譯入內核,要麼編譯爲內核模塊,要麼不編譯,選項爲“Y”、 “M”或“N” 。 

1> 對驅動程序而言, Kconfig 通常和源代碼處於同一目錄。

2> 如果你建立了一個新字目錄,而且也希望 Kconfig 文件存在於該目錄中的話,那麼就必須在一個已存在的 Kconfig 文件中將它引入,需要將其掛接在 drivers/misc 目錄中的 Kconfig 中。

(2) 在目錄的Makefile文件中增加對mini2440_adc.c源代碼的編譯,如下所示:

添加一句話:

obj-$(CONFIG_MINI2440_ADC)+= mini2440_adc.o 

上述腳本意味着如果MINI2440_ADC配置選項被選擇爲“Y”或“M” ,即obj-$(CONFIG_MINI2440_ADC)等同於obj-y或obj-m時,則編譯mini2440_adc.c,選“Y”的情況直接會將生成的目標代碼直接連接到內核,爲“M”的情況則會生成模塊mini2440_adc.ko(由於MINI2440_ADC爲布爾型,實際不會爲“M”) ;如果 MINI2440_ADC 配置選項被選擇爲“N”,即obj-$(CONFIG_MINI2440_ADC)等同於obj-n時,則不編譯mini2440_adc.c。 

3、正確配置好後,我們在源碼下執行makemenuconfig 後,在出現的 LinuxKernel Configuration 圖形界面中選擇DeviceDrivers --->[*] Misc devices --->  ,將會看到新加的 ADCdriver for FriendlyARM Mini2440 development boards 選項

4、刪除:

刪除也很簡單,首先在 drivers/char 目錄下刪掉自己的驅動文件夾。其次再刪除 Makefile 和 Kconfig 之前添加的東西,就搞定了

三 . 詳解:

Makefile , Kconfig 和配置工具組成了 Linux2.6 內核的配置系統。 
其中 Makefile 定義了 Linux 內核的編譯規則,它是大型項目開發的產物。 Linux 環境下的大型項目開發中,系統被分爲很多模塊,而這些模塊一般會經歷幾次修改,而在修改後的編譯過程中,由於某些文件中存在依賴關係,人工編譯效率低 ( 有些文件不需要重新編譯 ) 且易出錯, Makefile 文件便應運而生。 Makefile 文件定義了模塊間的依賴關係,指定文件的編譯順序,以及編譯所使用的命令。它和 make 命令使得項目的源程序文件可以自動編譯,提高了軟件開發效率。到此,再談一下 make ,它是用來維護程序模塊關係和生成可執行程序的工具,它可以根據程序模塊的修改情況重新編譯鏈接生成中間代碼或最終的可執行程序,省去那些重複的不必要的編譯工作,提高編譯效率。 
Kconfig 給用戶提供配置選擇的功能。通常配置內核會有四種方法, makeconfig (字符界面配置), makemenuconfig (菜單界面配置), makexconfig (依賴 QT ), makegconfig (依賴 GTK+ )。 makeconfig 比較適合專業人員,像初學者比較適合 makemenuconfig ,讓我們重點關注一下它。當我們運行 makemenuconfig 時,配置工具會首先分析與體系結構相對應的 /arch/xxx/Kconfig 文件( xxx 爲傳入的 arch 參數),它裏面包含了除一些與體系結構相關的配置項和配置菜單外,還通過 source 語句引入了一系列 Kconfig ,配置工具依據這些 Kconfig 包含的菜單和項目就可以描繪出一個分層結構。 
例如當我們運行 makezImagine 、 makebzImagine 等生成映像的命令時,會先檢索頂層的 Makefie (在 arch/xxx/ 目錄下的 Makefile 爲頂層Makefile 補充體系結構相關的信息),頂層 Makefile 的兩個主要任務是:產生內核映像文件和內核模塊。接着頂層 Makefile 會去遞歸地進入內核的各個子目錄,然後分別調用子目錄中的 Makefile (這些 Makefile 記錄編譯目標),而進入哪些子目錄取決於內核的配置。 
當使用 makemenuconfig , makeconfig 命令時,生成的 .config 在源碼目錄下記錄哪些部分被編譯入內核,哪些部分被編譯爲內核模塊。簡而言之,它是保存內核配置結果的文件。當我們裝上 Linux 系統時,第一次查看源碼下的所有文件,會發現沒有 .config 文件,那是因爲從來沒配置過內核。當你運行 makemenuconfig 保存並退出時,再次查看就有這個文件了。 
配置工具,包括配置命令解釋器(對配置腳本中使用的命令進行解釋)和配置用戶界面(提供字符界面和圖形界面),配置工具都是用腳本語言編寫的。

1. 在進入 menuconfig 配置界面時,會發現每個配置項目爲布爾型(要麼編譯入內核,要麼不編譯,選項爲“ Y” 或“ N” ),菜單上爲配置選項的名字例如:“ XXX Driver”,help 後面的內容爲幫助信息。

1> 除了布爾型的配置項目外,還存在一種三態型 (tristate) 配置選項,它意味着要麼編譯入內核,要麼編譯爲內核模塊,要麼不編譯,選項爲“ Y”“ M” “ N” 

eg:obj-$(CONFIG_FILENAME) +=filename.o

上面的腳本含義是:如果 FILENAME 選項被選擇爲“ Y” 或“ M” ,即 obj-$(CONFIG_FILENAME) 就等同於 obj-y  obj-m 時,則編譯 filename.c ,選 的情況直接會將生成的目標代碼直接連接到內核,爲“ M” 的情況則會生成模塊 filename.ko ,如果 FILENAME 配置選項被選擇爲“ N” ,即 obj-$(CONFIG_FILENAME) 等同於 obj-n 時,則不編譯 filename.c

2.Makefile

對內核源代碼各級子目錄中的 kbuildMakefile 進行介紹,

(1) 目標定義

目標定義用來定義哪些內容要作爲模塊編譯,哪些要編譯並連接進內核

(a)obj-y:=foo.o

表示要由 foo.c 或者 foo.s 文件編譯得到 foo.o 並連接進內核,而 obj-m 則表示該文件要作爲模塊編譯。處了 y,m 以外的 obj-x 形式的目標都不會被編譯。

( b) 我們最常用的的做法是根據 .config 文件的 CONFIG_ 變量來決定文件的編譯方式:

eg:

obj-$(CONFIG_ISDN)+= isdn.o

obj-$(CONFIG_ISDN_PPP_BSDCOMP)+= isdn_bsdcomp.o

除了obj-形式的目標以外,還有lib-ylibrary庫、hostprogs-y主機程序等目標,但是基本都應用在特定的目錄和場合下。

lib-y用來定義那些文件被編譯成庫文件,lib-y中定義的.o文件有當前目錄下的.c或.S文件編譯生成,他們被打包成當前目錄下的一個庫文件:lib.a。同事出現在obj-y、lib-y中的.o文件,不會被包含進lib.a中。要把這個lib.a編譯進內核,需要在頂層Makefile中libs-y變量中列出當前目錄。

(c) 多個文件模塊的定義

如果一個模塊由多個文件組成,這時候應採用模塊名加 -objs 後綴或者 -y 後綴的形式來定義模塊的組成文件。

eg:

obj-$(CONFIG_EXT2_FS)+= ext2.o

ext2-y :=balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR)+= xattr.o

模塊的名字爲ext2,由balloc.o和bitmap.o兩個目標文件最終連接生成 ext2.o 直至 ext2.ko 文件,是否包括 xattr.o 取決於內核配置文件的配置情況。

或者寫成如-objs的形式:

obj-$(CONFIG_ISDN)+= isdn.o

isdn-objs:= isdn_net_lib.o isdn_v110.o isdn_common.o

(d)目錄層次的迭代。

eg:

obj-$(CONFIG_EXT2_FS)+= ext2/

當CONFIG_EXT2_FS的值爲y或m時, kbuild將會把ext2目錄列入向下迭代的目標中,具體 ext2 目錄下的文件是要作爲模塊編譯還是鏈入內核由ext2目錄下的Makefile文件的內容決定。

3.Kconfig

內核配置腳本文件的語法主要包括如下幾個方面。

(1)菜單入口。

大多數的內核配置選項都對應Kconfig中的一個菜單入口,如下所示:

configMODVERSIONS

     bool "Set version information on allmodule symbols"

     depends on MODULES

     help

        Usually, modules have to be recompiledwhenever you switch to anew

        kernel. ...

“config”關鍵字定義新的配置選項,之後的幾行定義了該配置選項的屬性。配置選項的屬性包括類型、數據範圍、輸入提示、依賴關係(及反向依賴關係) 、幫助信息和默認值等。 每個配置選項都必須指定類型,類型包括bool、tristate、string、hex 和 int,其中tristate 和string是兩種基本的類型,其他類型都基於這兩種基本類型。bool變量取值有兩種:y和n;tristate取值有3種:y、n和m;string變量取值爲字符串;hex變量取值爲16進制的數據;int變量取值爲10進制的數據。

類型定義後可以緊跟輸入提示,下面的兩段腳本是等價的。

腳本1:

bool"Networking support"

腳本2:

bool

prompt"Networking support"

輸入提示信息的完整格式如下所示:

prompt<prompt> [if <expr>]

如果使用“if<expr>”,則當expre爲真時,才顯示提示信息。在實際使用中“prompt”關鍵字可以省略。其中可選的if用來表示該提示的依賴關係。

依賴關係的格式如下所示:

depends on(或者requires) <expr>

如果定義了多個依賴關係,它們之間用“&&”間隔。依賴關係也可以應用到該菜單中所有的其他選項中(這些選項同樣可接受 if 表達式) ,下面的兩段腳本是等價的。

腳本1:

bool "foo" if BAR

default y if BAR

腳本2:

depends on BAR

bool "foo"

default y

默認值的格式如下所示:

default <expr> [if<expr>]

反向依賴關係的格式如下所示:

select <symbol> [if<expr>]

depends能限定一個symbol的上限,即如果A依賴於 B,則在 B被配置爲“Y”的情況下,A可以爲“Y”、“M”和“N”;在B被配置爲“M”的情況下,A可以被配置爲“M”或“N” ;B在被配置爲“N”的情況下,A只能爲“N” 。

select 能限定一個 symbol 的下限,若 A 反向依賴於 B ,則 A 的配置值會高於或等於 B (正好與 depends 相反)。如果 symbol 反向依賴於多個對象,則它的下限是這些對象的對大值。

幫助信息的格式如下:

help(或---help---)

  開始

  …

  結束

幫助信息完全靠文本縮進識別結束。 “---help---”和 “help”在作用上沒有區別,設計“---help---”的初衷在於將文件中的配置邏輯與給開發人員的提示分開。

(2)菜單結構。

菜單入口在菜單樹結構中的位置可由兩種方法決定。第一種方式如下所示:

menu "Network devicesupport"

     depends on NET

config NETDEVICES

     …

endmenu

“menu”和 “endmenu”之間的有很多config條目。菜單入口都會成爲“Network device support”的子菜單。 而且, 所有子菜單選項都會繼承父菜單的依賴關係,比如, “Network device support”對“NET”的依賴被加到了配置選項NETDEVICES的依賴列表中。

另一種方式是通過分析依賴關係生成菜單結構。如果菜單選項在一定程度上依賴於前面的選項,它就能成爲該選項的子菜單。如果父選項爲“ N”,則子選項不可見;如果父選項爲“ Y” “ M” ,則子選項可見。

Eg:

configMODULES

bool“Enable loadable module support”

configMODVERSIONS

bool“Set version information on all module symbole”

dependson MODULES

comment“module support disabled”

dependson !MODULES

MODVERSIONS 直接依賴 MODULES ,如果 MODULES 不爲“ N” ,該選項纔可見。

除此之外, Kconfig中還可能使用 “choices … endchoice” 、 “comment” 、 “if…endif”

這樣的語法結構。其中“choices  … endchoice”的結構如下所示:

choice

<choice options>

<choice block>

endchoice

chioce條目將多個類似的配置選項組合在一起,供用戶單選或多選。

Comment條目用於定義一些幫助信息,它在配置過程中出現在界面的第一行;並且這些幫助信息出現在幫助文件中(作爲註釋)。

source條目用於讀入另一個kconfig文件。例如上級Kconfig文件要讀入net/Kconfig文件:

source  “net/Kconfig”

四、實例:在內核中新增驅動代碼目錄和子目錄

下面講解一個綜合實例,假設我們要在內核源代碼drivers目錄下爲 ARM體系結

構新增如下用於test driver的樹型目錄:

|--test

    |-- cpu

        | -- cpu.c

    |-- test.c

    |-- test_client.c

    |-- test_ioctl.c

    |-- test_proc.c

    |-- test_queue.c

在內核中增加目錄和子目錄,我們需爲相應的新增目錄創建 Kconfig 和 Makefile

文件,而新增目錄的父目錄中的 Kconfig 和 Makefile 文件也需要修改,以便新增的

Kconfig和Makefile文件能被引用。

在新增的test目錄下,應該包含如下Kconfig文件:

#

# TEST driver configuration

#

menu "TEST Driver "

comment " TEST Driver"

 

config CONFIG_TEST

     bool "TEST support "

config CONFIG_TEST_USER

     tristate "TEST user-spaceinterface"

     depends on CONFIG_TEST

 

endmenu

由於 TEST  driver 對於內核來說是新的功能,所以首先需要創建一個菜單 TEST Driver;然後顯示“TEST support” ,等待用戶選擇;接下來判斷用戶是否選擇了 TEST Driver,如果是(CONFIG_TEST=y) ,則進一步顯示子功能:用戶接口與 CPU功能支持;由於用戶接口功能可以被編譯成內核模塊,所以這裏的詢問語句使用了tristate。

爲了使這個Kconfig文件能起作用,需要修改arch/arm/Kconfig文件,增加以下內容:

source "drivers/test/Kconfig"

腳本中的source意味着引用新的Kconfig文件。

在新增的test目錄下,應該包含如下Makefile文件:

# drivers/test/Makefile

#

# Makefile for the TEST.

#

obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o

obj-$(CONFIG_TEST_USER) += test_ioctl.o

obj-$(CONFIG_PROC_FS) += test_proc.o

 

obj-$(CONFIG_TEST_CPU) += cpu/

該腳本根據配置變量的取值構建obj-*列表。 由於 test目錄中包含一個子目錄cpu,

當CONFIG_TEST_CPU=y時,需要將cpu目錄加入列表。

test目錄中的cpu子目錄也需包含如下的Makefile文件:

# drivers/test/test/Makefile

#

# Makefile for the TEST CPU

#

obj-$(CONFIG_TEST_CPU) += cpu.o

爲了使得整個 test 目錄能夠被編譯命令作用到,test 目錄父目錄中的 Makefile 文件也需新增如下腳本:

obj-$(CONFIG_TEST) += test/

在drivers/Makefile中加入obj-$(CONFIG_TEST) += test/,使得用戶在進行內核編譯時能夠進入 test目錄。

增加了Kconfig和Makefile文件之後的新的 test樹型目錄如下所示:

test

├──cpu

│  ├── cpu.c

│  └── Makefile

├── test.c

├──test_client.c

├──test_ioctl.c

├──test_proc.c

├──test_queue.c

├──Makefile

└──Kconfig


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章