前言
該 Makefile 已經通過基於核心 Linux5.4 版本驗證通過。
因為編寫這通用驅動 Makefile 時遇到了標頭檔案指定路徑失敗的問題。使用過 ccflags-y 、INCDIR 、EXTRA_CFLAGS 、-L 等等引數都無效。就是因為我使用了 $(shell pwd)
。導致這些引數的路徑都為核心原始碼路徑下,而非模組路徑。後面重新檢視核心文件,看核心的推薦寫法才解決了,使用 $(src) 來獲取模組原始碼路徑。正確指向自定義的標頭檔案路徑。所以有以下建議:
- 建議:對於不同的 Linux 核心,應該去該核心文件看看 makefiel 和 Kbuild 的語法及特點。(Documentation/kbuild)**
參考連線:
李柱明部落格園
1. 特點
- 支援編譯多目錄Linux核心驅動
- 支援多目錄原始檔及標頭檔案編譯
- 相容性高,修改介面巨集即可
2. 分析
2.1 簡要原理
由於驅動程式中包含了很多來自核心的標頭檔案,編譯驅動程式時需要指定板子所用的同一版本的核心原始碼(編譯後,以下無特別說明也是編譯後)路徑。
簡要原理其實是主要由核心原始碼中的 Makefile 來進行編譯並生成驅動檔案。所以當前 Makefile 只需要提供引數和跳轉到核心原始碼路徑執行其頂層 Makefile 即可。
2.2 具體分析
核心路徑:
KERNEL_DIR = /home/lss/work/kernel/imx6/ebf-buster-linux/build_image/build
:指出編譯後的核心原始碼路徑。
架構及編譯器:
ARCH = arm
:提供架構名稱。
CROSS_COMPILE = arm-linux-gnueabihf-
:提供交叉編譯器名稱。
CC = $(CROSS_COMPILE)gcc
:用於測試用例APP使用的編譯器(無測試用例可遮蔽)。
export ARCH CROSS_COMPILE
:共享架構名稱及交叉編譯器名稱到 sub-Makefile,這裡即是核心原始碼中的頂層Makefile及其sub-Makefile。
路徑變數:
PWD := $(shell pwd)
:執行時的 make 路徑。並不是當前檔案路徑。
MODDIR := $(src)
:當前模組的頂層路徑。
- 核心原始碼原話:$(src) provides the absolute path by pointing to the directory where the currently executing kbuild file is located.
- Kbuild可以看作Makefile。(雖然不嚴謹)
- $(src) 是由核心 Makefile 提供的。為當前被執行的 子Makefile 的絕對路徑。在這裡也可以看出 M= 的路徑。
- 因為當核心頂層 Makefile 使用 -C 跳到核心頂層 Makefile ,如果使用
$(shell pwd)
的話,該命令的值為 核心頂層 Makefile 的絕對路徑,而不是核心頂層 Makefile 的路徑。( $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules 中的 M=$(CURDIR) 例外,因為它在當前 Makefile 生效後再跳轉到 -C 的 )
- 因為當核心頂層 Makefile 使用 -C 跳到核心頂層 Makefile ,如果使用
目標:
TARGET_DRV := led_device_driver
:模組目標名稱。
TARGET_APP := led_app
:測試用例目標名稱。
資原始檔和模組目標定義
$(TARGET_DRV)-y += led_module.o
:目標模組所需原始檔。
$(TARGET_DRV)-y += ./device/led_dev_a.o
:目標模組所需原始檔。
$(TARGET_DRV)-y += ./driver/led_drv.o
:目標模組所需原始檔。
obj-m := $(TARGET_DRV).o
:目標。告訴核心要編譯成模組。
- obj-y:編譯驅動到核心。 obj-m:編譯驅動為模組。 obj-n:不編譯。
- 驅動模組的多原始檔編譯:obj-m := $(TARGET).o 是告訴 makefile 最總的編譯目標。而 $(TARGET)-y 則是告訴 makefile 該總目標依賴哪些目標檔案。(為固定格式,如總目標為
xxx.o
,那麼它依賴的原始檔應該這樣指定xxx-y +=
)(也可以使用 xxx-objs)
編譯引數:
ccflags-y := -I$(MODDIR)/include
:指定自定義標頭檔案路徑。這裡只能使用 $(src) 來獲取模組檔案路徑。
- External modules tend to place header files in a separate include/ directory where their source is located, although this is not the usual kernel style. To inform kbuild of the directory, use either ccflags-y or CFLAGS_
.o .
目標 all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
:原型是 make modules
,即是編譯模組目標,其它都是引數。
- -C $(KERNEL_DIR):把工作目錄跳轉到核心原始碼中。
- M=$(CURDIR):表示編譯模組時,可以到該路徑尋找模組原始碼進行編譯。
- 推薦:觀看核心文件 Documentation/kbuild/modules.rst
3. 原始碼
# @file Makefile
# @brief 驅動。
# @details led 驅動模組 Makefile 例程。
# @author lzm
# @date 2021-03-14 10:23:03
# @version v1.1
# @copyright Copyright By lizhuming, All Rights Reserved
#
# ********************************************************
# @LOG 修改日誌:
# ********************************************************
# 編譯後核心路徑
KERNEL_DIR = /home/lss/work/kernel/imx6/ebf-buster-linux/build_image/build
# 定義框架
# ARCH 為 x86 時,編譯鏈頭為
# ARCH 為 arm 時,編譯鏈頭為 arm-linux-gnueabihf-
ARCH = arm
ifeq ($(ARCH),x86)
CROSS_COMPILE = #
else
CROSS_COMPILE = arm-linux-gnueabihf-#
endif
CC = $(CROSS_COMPILE)gcc #
# 共享到sub-Makefile
export ARCH CROSS_COMPILE
# 路徑
PWD := $(shell pwd)
MODDIR := $(src)
# 注意:驅動目標不要和檔名相同
TARGET_DRV := led_device_driver
TARGET_APP := led_app
# 本次整個編譯需要源 檔案 和 目錄
$(TARGET_DRV)-y += led_module.o
$(TARGET_DRV)-y += ./device/led_dev_a.o
$(TARGET_DRV)-y += ./driver/led_drv.o
obj-m := $(TARGET_DRV).o
# obj-m += $(patsubst %.c,%.o,$(shell ls *.c))
# 編譯條件處理
ccflags-y := -I$(MODDIR)/include
ccflags-y += -I$(MODDIR)/device
ccflags-y += -I$(MODDIR)/driver
# 第一個目標
all :
@$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
# $(CROSS_COMPILE)gcc -o $(TARGET_APP) $(TARGET_APP).c
# 清理
.PHONY:clean
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
# rm $(TARGET_APP)