platform 驅動
linux驅動最開始採用的是純驅動的寫法,驅動工程師只需實現一個功能驅動(driver),所有功能都集中在這個驅動。接着linux內核將設備(device)和驅動(driver)分離,引入了platform
總線驅動框架,內核實現platform
總線,驅動工程師實現platform device
和platform driver
,方便內核管理驅動和提升驅動開發效率。到後來,linux內核將板級信息剝離,不再將板級信息編譯到內核,而引入設備樹(dtb)概念,以設備樹文件描述板級信息,驅動通過獲取設備樹描述信息執行相關過程。此時,驅動工程師編寫驅動時則需要實現設備樹dts
和platform driver
,即是用設備樹代替platform device
。
目前,現在linux主流驅動開發都是採用第三種方式,linux內核較低版本或者歷史維護性項目會採用第二種,甚至仍採用第一種的傳統方式。對於第二種驅動開發方式,我們一般建立兩個源碼文件,分別是dev_xxx.c
和drv_xxx.c
,dev_xxx.c
用於描述匹配驅動的信息,包括驅動名稱(name)、id、內存映射(memory),然後註冊當前platform device
信息,提供給platform driver
匹配(probe);drv_xxx.c
則爲實際驅動的功能實現,設備驅動的初始化、資源分配(主次設備號、內存空間)、註冊、註銷以及提供linux標準虛擬文件系統接口(open/read/write/ioctl/mmap
)。
實現
不僅僅是platform驅動,對於包含多個源碼文件(.c)的,編譯成一個驅動模塊文件(.ko)的思路和方法都是類似的,都可以使用該方式。
【1】 各個源碼文件的初始化函數和析構函數聲明爲全局,最終在一個文件調用,並由module_init
和module_exit
註冊到內核
【2】Makefile將所有目標文件鏈接爲一個文件
在“Linux 字符驅動之platform框架”文章中的platform字符設備驅動,把dev_mem.c
和drv_mem.c
分別編譯成dev_mem.ko
和drv_mem.ko
模塊文件,使用時也需將兩個模塊文件分別insmod
到內核。現修改源碼和Makefile
將其編譯爲一個模塊文件。
【1】屏蔽dev_mem.c
模塊初始化和析構函數
module_init(memory_dev_init);
module_exit(memory_dev_exit);
MODULE_LICENSE("GPL");
注
一個模塊只能擁有一個初始化和析構接口,即是一對module_init和module_exit,如果存在多個、接口,會導致編譯失敗。
【2】增加dev_mem.h
頭文件,將構造初始化和析構函數聲明爲全局
extern int __init memory_dev_init(void);
extern void __exit memory_dev_exit(void);
【3】 在drv_mem.c
模塊初始化和析構函數分別增加上dev_mem.h
聲明的函數
static int __init memory_drv_init(void)
{
memory_dev_init();
return platform_driver_register(&memory_drv);
}
static void __exit memory_drv_exit(void)
{
memory_dev_exit();
platform_driver_unregister(&memory_drv);
}
【4】 修改Makefile,把所有編譯的目標文件鏈接成一個可執行文件
ifeq ($(KERNELRELEASE),)
KERNELDIR = /usr/src/linux-headers-4.15.0-91-generic
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko .mod.o *.mod.c *.symvers *.order
else
obj-m := devmem.o
devmem-objs = dev_mem.o drv_mem.o
endif
-
obj-m := devmem.o
,將代碼編譯爲devmem.ko
模塊,-m
表示編譯成模塊,-y
表示編譯到內核 -
devmem-objs = dev_mem.o drv_mem.o
,將所有目標文件(dev_mem.o、drv_mem.o)鏈接爲一個執行文件
假設有更多個源碼文件,Makefile
文件也是類似的
obj-m := object_name.o
devmem-objs = source0.o source1.o source2.o source3.o
【5】執行make -j4
編譯,在當前文件生成指定文件名的驅動模塊
接下來的加載驅動步驟是相同的,執行sudo insmod devmem.ko
即可把dev_mem
和drv_mem
加載到內核。
代碼倉庫
【1】https://github.com/Prry/linux-drivers/tree/master/devmem_platform