14 Linux裝置驅動基礎程式設計
阿新 • • 發佈:2018-11-25
Linux裝置驅動基礎程式設計
核心功能模組:程序排程,記憶體管理(mmu,分配程序記憶體),檔案系統管理(如:支援的檔案系統格式),裝置驅動(硬體驅動由核心來統一管理),網路協議棧。
模組機制:
靜態載入:把驅動模組編進核心,在核心啟動時自動載入。
動態載入:把驅動模組編為.ko檔案,在核心啟動後,需要用時手動載入。
核心驅動標頭檔案中所定義有關的巨集:
#define __init __section(.init.text)
#define __initdata __section(.init.data)
#define __section(S) __attribute__ ((__section__(#S)))
//段".init*"其實就是表示只要初始化後不會再使用,核心可以把這段裡的空間回收使用
#define __exitdata __section(.exit.data)
#define __exit __section(.exit.text)
//段".exit*"應是用於集中管理只有驅動模組解除安裝時才會觸發呼叫的資源,防止被誤呼叫
char __initdata buf[] = "hello world";//表示此字元陣列在驅動初始化後可以回收空間
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
//module_init這個巨集其實就是把我們的初始化函式名多加個別名("init_module")
//module_exit巨集用於把解除安裝函式多加個別名("cleanup_module") //module_exit具體的巨集定義沒列出來
//linux核心2.4版本時,裝置驅動模組的初始化函式名必須是"init_module",解除安裝函式名必須是"cleanup_module"。
//現在我們寫驅動模組,初始化函式和解除安裝函式名可以隨便命名,但其實核心裡還是沒變。
簡單的事例程式碼(xxx.c)(xxx為具體的.c檔名):
#include <linux/module.h>
#include <linux/init.h>
//__init為了把test_init的函式程式碼放入統一的初始化段裡,當核心把驅動初始化完後,自動釋放此函式的程式碼指令空間
static int __init test_init(void)
{
printk("This is the test init function\n");//printk為核心預設的列印函式
return 0;//返回0表示成功,返回負數退出載入模組,返回正數,會有警告但還是會載入
}
//__exit為了指定此函式只在驅動解除安裝時使用,用完後自動釋放
static void __exit test_exit(void)
{
printk("This is the test exit function\n");
}
module_init(test_init);//指定test_init為模組初始化函式
module_exit(test_exit);//指定test_exit為模組退出時執行的解除安裝函式
MODULE_LICENSE("GPL");//指定所支援的協議
MODULE_AUTHOR("作者");
MODULE_DESCRIPTION("描述");
MODULE_VERSION("版本");
編譯驅動模組,需要呼叫核心原始碼目錄裡的Makefile。
在程式碼目錄下建立一個Makefile檔案來指定編譯目標。
Makefile檔案內容:
obj-m += xxx.o
KSRC := /目錄路徑/orangepi_sdk/source/linux-3.4.112/
export ARCH := arm
export CROSS_COMPILE := arm-linux-gnueabihf-
all:
make -C $(KSRC) modules M=`pwd`
.PHONY : clean
clean:
make -C $(KSRC) modules clean M=`pwd`
編譯完成後會生成xxx.ko檔案,可以將其載入到驅動中(相當於執行該檔案):
insmod xxx.ko //載入驅動模組
rmmod xxx //解除安裝驅動模組
檢視驅動模組資訊:
modinfo xxx.ko //檢視模組的資訊
cat /proc/modules //檢視當前系統的動態載入模組(相當於lsmod)
如:xxx 1768 0 - Live 0xbf03c000
(模組名 使用的記憶體大小 正在被呼叫次數 - 有效 模組所在的記憶體地址)
ls /sys/module //檢視系統裡現有的驅動模組(包括動靜態驅動模組)
因為printk的輸出級別問題,如果不進行設定,我們是看不到輸出的。
我們可以通過以下命令來檢視驅動輸出的訊息:
cat /var/log/messages
tail /var/log/messages
dmesg | tail
printk的輸出級別控制:
#include <linux/kernel.h>
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
//預設的級別為13: "<d>"
#define KERN_DEFAULT "<d>"
使用:printk(KERN_INFO”內容”);//相當於:printk(“<6>kskdlfj”);//指定輸出級別為6
cat /proc/sys/kernel/printk //檢視當前核心的輸出級別
7 7 1 7
7: console_loglevel
7: default_message_loglevel
1: minimum_console_loglevel
7: default_console_loglevel
當printk函式使用的級別小於當前console_loglevel級別時, 則可以輸出, 否則不輸出
echo "8 4" > /proc/sys/kernel/printk //修改級別輸出
//輸出級別只要小於8就可以輸出(否則會看不到輸出,要通過檢視驅動資訊才能看到),預設級別為4(即不指定級別時使用此級別)
程式碼裡用於除錯輸出的巨集:
#ifdef DEBUG
#define TS_DEBUG(fmt,args...) do { printk(fmt, ##args); } while (0)
#else
#define TS_DEBUG(fmt,args...) do { } while (0)
#endif
用法:
TS_DEBUG("hello\n");
TS_DEBUG("%d, %d\n", num , num2);