Linux驅動簡介和開發流程
【學習筆記】
Linux 驅動的分類
Linux三大設備驅動
1、字符設備驅動
IO的傳輸過程是以字符為單位的,沒有緩沖。比如I2C,SPI都是字符設備
2、塊設備驅動
IO傳輸過程中是以塊為單位的。跟存儲相關的,都屬於塊設備,比如:tf卡
3、網絡設備驅動
與前兩個不一樣,是以socket套接字來訪問的。
其中,理解和掌握字符設備驅動的概念最重要,因為在工作中我們遇到大部分都是字符設備
驅動的組成
驅動分為四個部分
(1)頭文件
(2)驅動模塊的入口和出口
(3)聲明信息
(4)功能實現
具體成分
第一步,包含頭文件
include <linux/init.h> 包含了宏定義的頭文件
include <linux/module.h> 包含了初始化加載模塊的頭文件
第二步,驅動模塊的入口和出口
module_init();
module_exit();
第三步,生命模塊具有開源許可證
MODULE_LICENSE("GPL");
第四步,功能的實現
實例
簡單的helloworld驅動程序編寫
/兩個必要的頭文件
#include <linux/init.h>
#include <linux/module.h>
//內核加載的時候打印hello world,內核模塊卸載的時候打印bye bye
static int hello_init(void){
printk("hello world\n"); //內核里不能使用c語言庫,所以不能用printf
return 0;
}
static void hello_exit(void){
printk("bye bye\n");
}
//入口和出口
module_init(hello_init);
module_exit(hello_exit);
//聲明許可證
MODULE_LICENSE("GPL");
Linux驅動編譯成模塊
方法一
把驅動編譯成模塊,然后使用命令把驅動加載到內核里面。
步驟
1、編譯成模塊
(1)先寫一個makefile
在linux下
touch Makefile #創建Makefile文件
obj-m +=HelloWorld.o #obj-m 意為把驅動編譯為模塊
KDIR:={core_path} #PATH根據開發板內核路徑來填寫
PWD?=$(shell pwd) #自動獲取當前位置路徑
all:
make -C $(KDIR) M=$(PWD) modules #切換到內核路徑,用make把代碼編譯成模塊
(2)編譯驅動
編譯驅動之前需要注意的問題:
-
內核源碼一定要編譯通過
-
編譯驅動用的內核源碼一定要和開發板上運行的內核鏡像是同一套
-
看一下開發代碼的ubuntu環境是不是arm,如果不是,要改成arm環境
查看環境的 方法
在內核源碼的目錄下輸入
make menuconfig #顯示系統環境
#左上角如果顯示是X86,而不是arm,則輸入
export ARCH=arm #改為arm環境
為了以防萬一,編譯前,先命令行輸入兩行代碼,設置環境和編譯器
export ARCH=arm
export CROSS_COMPILE={編譯器名稱} #可以直接輸入arm查看
#如果查看到:arm-linux-gnueabihf-gcc-4.9.4
編譯成功后就可以看到 ko 文件了,這個 ko 文件就是編譯好的驅動。
在開發板上加載驅動用insmod
insmod HelloWorld.ko
查看加載的模塊,使用lsmod命令
lsmod
卸載驅動模塊
rmmod HelloWorld #注意這里沒有后綴
卸載時,如果提示沒有相應的目錄,直接在對應的目錄下創建就可以了
方法二
直接把驅動編譯到內核。
在下面有實際的例子。
移植
如果芯片的內核不支持設備的驅動,那么就需要把這個驅動移植到內核。
移植驅動需要驅動源碼和makefile。
移植需求分析
(1)先去內核源碼搜索,如果有的話,可以直接選擇這個驅動,然后直接使用。
(2)假如沒有這個驅動,則許喲啊自己編譯一個驅動,然后加載到內核里邊去運行。
make menuconfig 圖形化配置
1、如何進入make menuconfig圖形化配置
首先進入到內核源碼的路徑下,然后輸入make menuconfig 即可打開這個界面。
2、make menuconfig圖形化界面的操作
(1)搜索功能
輸入“/”即可彈出搜索界面
(2)配置驅動狀態
-
把驅動編譯成模塊(用[ M ]來表示)
-
把驅動編譯到內核里(用[ * ]來表示)
-
不編譯(用[ ]來表示)
可以使用“空格”鍵來配置這三種不同的狀態
(3)如何退出
分為保存退出和不保存退出,按照提示選擇即可
(4)和make menuconfig 有關的文件
Makefile:里邊是編譯規則,高速我們在make的時候需要如何編譯(相當於菜的做法)
Kconfig:內核配置的選項(相當於服務員給的菜單)
.config:配置完內核以后生成的配置選項(相當於我門勾選的菜單)
(5)make menuconfig會讀取不同環境目錄下的Kconfig文件
會讀取Arch/$ARCH/目錄下(目錄下有很多不同的環境文件夾)的Kconfig文件(選擇不同的環境,和export ARCH=arm效果類似)。
/arch/arm/configs 下面有很多配置文件(相當於特色菜,默認配置,在不知道如何配置的時候可以按照這個來),如果移植內核的時候,配置文件太多,就可以把這個文件夾下面的配置復制成.config里邊(即默認系統的配置)【復制用cp命令】
(6)為什么要復制成.config,而不是其他名字
因為內核會默認讀取Linux內核根目錄下的.config作為默認的配置選項,所以不可以改名字
(7)如果復制的默認配置.config不滿足我們的要求,如何解決
直接輸入make menuconfig,進入Kconfig配置界面,來進行修改配置,保存退出,配置會自動更新到.config里面。
(8)配置文件選項怎樣和Makefile文件建立聯系
當make menuconfig保存退出以后,Linux會將所有的配置選項以宏定義的形式保存在include/generated/下面的autoconf.h頭文件中。
把驅動編譯到內核
Kconfig代碼例子
#例子
source "drivers/redled/Kconfig"
config LED_4412
tristate "Led Support for GPIO Led"
depends on LEDS_CLASS
help
This option enable support for led
#解讀
source "drivers/redled/Kconfig"#會讓config菜單里邊包含drivers/redled/這個路徑下的驅動文件,方便我們對菜單進行管理
config LED_4412 #配置選項的名稱,全名是CONFIG_LED_4412,這里做了一些省略
tristate "Led Support for GPIO Led"
#tristate表示驅動的狀態:1.把驅動編譯成模塊 2.把驅動編譯到內核 3.不編譯。與之對用的還有bool關鍵字:表示編譯到內核,和不編譯兩種狀態。
#"Led Support for GPIO Led"是make menuconfig里邊的某個菜單的名字
depends on LEDS_CLASS
# A depends on B表示只有在選擇B的時候才可以選擇A,即A依賴於B。
#比如想要去掉LED相關的驅動,我們雖然可以直接改.config文件,但是不推薦這樣做。因為如果有依賴項的話,直接改.config文件是不成功的。
#select:反向依賴,該選項被選中時,后面的定義也會被選中。
help
This option enable support for led #顯示幫助信息
實例
把HelloWorld驅動編譯到內核。
(1)首先進入到內核源碼的目錄下(內核根目錄)
(2)把HelloWorld.c驅動,復制到drivers/char/hello文件夾【char一般就是放字符設備的文件夾,hello文件夾使用mkdir命令創建】
(3)寫Kconfig文件
mkdir hello #創建hello文件夾
cd hello/ #切換到hello文件夾
cp /home/pyma/HelloWorld.c #把驅動文件復制到hello文件夾中
touch Kconfig #創建一個Kconfig文件
vi Kconfig #打開Kconfig進行編輯
#寫程序
config HELLO #起個名字叫HELLO
tristate "hello world" #選擇三種狀態的方式,並把菜單名字命名為hello world
#過於簡單沒有依賴,不用寫
help
hello help #幫助信息為hello help
保存退出
(4)寫Makefile文件
touch Makefile #創建Makefile文件夾
vi Makefile #打開Makefile編輯
#寫程序
obj-$(CONFIG_HELLO)+=HelloWorld.o
CONFIG_HELLO這個變量名字來源:是剛剛在Kconfig中命名的,但是Kconfig中相當於省略了CONFIG_,在這里相當於補上,即:makfile中要寫全名。
$(CONFIG_HELLO)變量作用:會根據我們選擇的狀態來改變,(1)如果是選擇把它編譯到內核中,那么表示-y(即:obj-y)(2)如果選擇編譯成模塊,那么它表示-m(即:obj-m)
到此,所有的文件都准備完成,接下來需要
(5)把hello驅動配置包,包含進去
需要修改上一級目錄的Makefile和Kconfig
cd .. #返回上一級目錄,即路徑.../char/
vi Makefile #修改Makefile,如果沒有則需要自己創建,自己寫
#寫程序,在首行添加以下代碼
obj-y +=hello/ #這里注意hello是個文件夾,因此最后要加"/"
... #原有的makefile代碼
#保存退出
vi Kconfig #打開Kconfig文件,把剛才寫的驅動代碼的Konfig包含進config菜單界面中
#在前面相應位置添加好Kconfig的路徑
source "drivers/char/hello/Kconfig" #Kconfig相對路徑
#保存退出
(6)回到內核根目錄下,打開菜單,選擇不同狀態
make menuconfig #進入配置菜單
在界面中選擇Device Drivers選項進入
再進入到Character devices目錄下
就會看到新添加的hello world驅動菜單項(名字是在Kconfig中命名的)
用空格鍵選擇狀態:
(7)編譯進內核,選擇<*>(編譯到內核)
保存退出,然后確保配置正確與否
打開.config文件,搜索
vi .config #打開配置文件
#搜索HELLO
#搜索命令
/HELLO
如果出現CONFIG_HELLO=y是編譯到內核中了,m是編譯成模塊
這里可以看出,如果沒有依賴項,也可以直接改.config文件的CONFIG_HELLO項
(8)編譯前更改
這時還不能立馬編譯,因為編譯會調用默認的的編譯配置,需要做相應修改
這里為了理解的透徹,先看一下編譯腳本:vi create.sh
#!/bin/bash
export ARCH=arm
...
make imx_v7_defconfig #這個就是內核的默認用法,意思是在編譯的時候會自己去找arch/arm/configs下面的imx_v7_defconfig配置文件,作為.config來編譯內核。
...
雖然前面我們改了.config文件,但不是直接用它來編譯的,而是使用默認的arch/arm/configs/imx_v7_defconfig文件來編譯的。
因此還要再改一下
-
make distclean,清除所有的編譯文件,也刪掉了.config文件
-
把缺省配置復制到.config中
cp arch/arm/configs/imx_v7_defconfig .config
- make menuconfig 配置菜單選項
再進行第(6)步操作(這里感覺有點亂,步驟重復了?(6)、(7)兩步可以不要)
(9)鍵修改后的.config復制回默認編譯菜單里
cd arch/arm/configs/ #切換到默認配置目錄
mv imx_v7_defconfig imx_v7_defconfig_noHELLO #更改默認配置的名字
cp ../../../.config imx_v7_defconfig #把修改好的配置,更改為默認的名稱
(10)運行腳本,等待編譯完成
./create.sh
(11)檢驗是否編譯成功
方法一:燒寫到開發板上,運行看看有沒有加載驅動
cp arch/arm/boot/zImage /home/pyma/ #把鏡像復制到好找的位置
方法二:進入到創建的驅動目錄,查看有沒有把.c文件編譯成.o文件
cd drivers/char/hello/
ls
如果沒有相應的.o文件,則說明編譯出錯,檢查Makefile和其他文件。
修改好,再回到根目錄編譯
.o文件檢查沒有問題后,再回到根目錄檢查是否有zImage鏡像
ls arch/arm/boot/