【嵌入式Linux驅動開發】學習筆記(彙總版)


QEMU虛擬板使用

  • 編譯好驅動程序和應用程序後,複製到板子掛載的文件系統。如:
    • cp hello_drv.ko hello_drv_test /home/clay/linux/qemu/NFS
  • 更新內核和設備樹
    • 編譯成功後,可在./arch/arm/boot/zImage目錄下得到內核文件,在./arch/arm/boot/dts/100ask_imx6ull_qemu.dtb得到設備樹文件
    • 用上述2個文件去替換qemu中的zImage和100ask_imx6ull_qemu.dtb即可達到更新內核和設備樹的目的。
      • qemu的內核和設備樹文件在其安裝目錄下的./imx6ull-system-image文件夾下。
  • 啓動QEMU(qemu終端操作)
    • ./qemu-imx6ull-gui.sh
    • mount -t nfs -o nolock,vers=3 10.0.2.2:/home/clay/linux/qemu/NFS /mnt
    • cd /mnt && ls /mnt
    • insmod hello_drv.ko - 加載驅動模塊
    • lsmod - 列出系統所有的驅動模塊
    • cat /proc/devices - 查看分配到的主設備號
  • 關於qemu小燈狀態
    • 低電平有效
    • 打開-紅色-寫1
    • 關閉-白色-寫0

GPIO電器屬性值

  • GPIO爲輸入時,配置爲0xF080
  • GPIO爲輸出時,配置爲0x10B0

查看終端是否註冊成功

cat /proc/interrupts

設備樹相關

查看設備樹節點:

  • ls /sys/firmware/devicetree/base
    • 根節點對應base目錄, 每一個節點對應一個目錄, 每一個屬性對應一個文件
  • ls /proc/device-tree
    • 鏈接文件, 指向 /sys/firmware/devicetree/base

platform平臺(name字段)

  • 設備文件在:/sys/bus/platform/devices/
  • 驅動文件在:/sys/bus/platform/drivers/

關於platform_device

  • 系統中所有的platform_device, 有來自設備樹的, 也有來有.c文件中註冊的
  • ls /sys/devices/platform
  • 對於來自設備樹的platform_device,可以進入 /sys/devices/platform/<設備名>/of_node中查看它的設備樹屬性
    • 例如進入/sys/devices/platform/led/後,若該目錄下有of_node節點,就表明該platform_device來自設備樹
    • 正常來說platform_device都在目錄/sys/devices/platform下,但是qemu的platform_device竟然沒有在,而是在目錄/sys/devices/soc0!(這一點要特別注意!)
    • 進入myled節點裏,可以看到platform_divice的各種屬性,需要注意的是其中的driver目錄,這個是隻有在platform_device和platform_driver綁定後纔會生成的
    • 另外通過ls -l driver,可以看到drivver也是個軟鏈接,qemu中指向/sys/bus/platform/drivers/100ask_led目錄!
    • 關於設備樹節點與驅動配對查看方法,參考這篇
    • 關於platform_device爲什麼叫100ask_led,是由驅動程序決定的,如下圖所示。

在這裏插入圖片描述


GCC

  • 預處理(.c/.cpp)、編譯()、彙編、鏈接
    在這裏插入圖片描述

頭文件

  • 默認頭文件路徑(程序中用<>包含頭文件,會到這個默認路徑查找文件)
    • 系統gcc編譯器默認頭文件路徑:一般在/usr/include目錄下,進入該目錄,然後使用find -name "stdio.h"表示在當前目錄下查找stdio.h頭文件! == find /usr/include/ -name "stdio.h"
    • 交叉編譯gcc編譯器默認頭文件路徑:一般在交叉編譯工具的bin文件所在目錄,如/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf,然後搜索find -name "stdio.h此時找到的頭文件是交叉編譯gcc編譯器對應的頭文件!
  • 不在默認頭文件路徑(使用雙引號)
    • 不指定路徑,則默認到當前路徑下查找對應頭文件
    • 指定路徑,加上-I ×××路徑

函數庫

  • 默認路徑
    • 系統gcc編譯器默認函數庫路徑:一般在/lib或者/usr/lib目錄。
    • 交叉編譯gcc編譯器默認函數庫路徑:一般在交叉編譯工具的bin文件所在目錄,如/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf,然後搜索然後搜索find -name lib即可看到對應的lib目錄路徑,一般開發板使用的是/lib或者/usr/lib目錄
  • 不在默認頭文件路徑
    • 編譯時使用-L選項指定目錄,用-l選項指定庫。如 -labc,即想找到名字爲libabc.so的庫!

使用動態鏈接庫

  • 生成.o文件:arm-linux-gnueabihf-gcc -c -o sub.o sub.c
  • 生成動態鏈接庫:arm-linux-gnueabihf-gcc -shared -o libsub.so sub.o
  • 使用動態鏈接庫生成可執行文件:arm-linux-gnueabihf-gcc -o test main.o -lsub -L ./
    • 注意-lsub之間沒有空格
    • sub庫並沒有位於工具鏈的lib目錄,所以我們需要指定目錄,-L ./指定庫所在的目錄爲當前目錄
  • 注意:使用動態鏈接庫交叉編譯生成的可執行程序,傳輸到板子上時,需要把動態鏈接庫libusb.so放到板子對應的/lib目錄
    • 如果不放到板子對應的/lib目錄,則需要重新運行以下程序(放到/a目錄爲例)
      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/a 然後運行./test

使用靜態鏈接庫

  • 生成.o文件:arm-linux-gnueabihf-gcc -c -o sub.o sub.c
  • 生成靜態鏈接庫:ar crs libsub.a sub.o(可以使用多個.o生成靜態庫)
  • 使用靜態鏈接庫生成可執行文件rm-linux-gnueabihf-gcc -o test main.o libsub.a (如果.a不在當`前目錄下,需要指定它的絕對或相對路徑)
  • 注意:靜態鏈接庫不需要放到板子上!!!

GCC編譯一些很有用的選項

  • gcc -E main.c // 查看預處理結果,比如頭文件是哪個
  • gcc -E -dM main.c > 1.txt // 把所有的宏展開,存在1.txt裏
  • gcc -Wp,-MD,abc.dep -c -o main.o main.c // 生成依賴文件abc.dep,後面Makefile會用

Makefile

  • $@ 表示目標
  • $^表示所有依賴,$<表示第一個依賴
  • -Wp,-MD,[email protected],生成所有的目標依賴頭文件

驅動程序

  • 驅動程序 = 驅動框架+硬件操作(單片機)
    在這裏插入圖片描述
  • 驅動程序書寫步驟
    • ① 確定主設備號,也可以讓內核分配
    • ② 定義自己的 file_operations 結構體
    • ③ 實現對應的 drv_open/drv_read/drv_write 等函數,填入 file_operations 結構體
    • ④ 把 file_operations 結構體告訴內核: register_chrdev
    • ⑤ 誰來註冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數
    • ⑥ 有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用 unregister_chrdev
    • ⑦ 其他完善:提供設備信息,自動創建設備節點: class_create, device_create
  • 函數查找參考內核中的其他程序
  • register_chrdev第一個參數爲主設備號,傳入0,讓內核爲你分配一個主設備號。也可以指定主設備號(前提:這個主設備號沒被佔用!)
    • 加載的時候先創建類,再創建設備。
    • 先摧毀設備,再摧毀類,最後取消註冊。

應用程序

  • 函數使用,利用man手冊。如查找printf依賴的頭文件 man 3 printf
  • 應用程序不能直接訪問內核數據,必須藉助其他數據。

設備樹基礎

  • DTS - Device Tree Source
  • DTC - Device Tree Compiler
  • DTB - Device Tree Binary
  • .dtsi是SOC的公共設備樹文件(i是include),.dts是我們寫的設備樹文件,會追加到了.dtsi文件中!
  • /dts-v1/;這個是.dts設備樹文件開頭都要寫的
  • 命名[label:] node-name[@unit-address]中括號中內容可以省略。
    • label 的作用是爲了方便地引用 node,如uart0: uart@fe001000,可以有以下兩種訪問方式。
      • &uart0
      • &{/uart@fe001000}
  • 系統啓動後可以在根文件系統中看到設備樹的節點信息。/proc/device-tree
  • 編譯設備樹:make dtbs
  • 設備樹的處理過程是: dtb -> device_node -> platform_device

設備樹屬性

  • compatible
    • 根節點下的 compatible 屬性
      • 用來選擇哪一個“machine desc”: 一個內核可以支持machine A,也支持 machine B,內核啓動後會根據根節點的 compatible 屬性找到對應的machine desc (機器描述)結構體,執行其中的初始化函數。
      • compatible 的值,建議取這樣的形式: “manufacturer,model”,即“廠家名,模塊名”。
    • 設備節點下的compatible 屬性
      • 用來表示兼容的驅動。比如對於某個 LED,內核中可能有 A、 B、 C 三個驅動都支持它,那可以這樣寫。內核啓動時,就會爲這個 LED 按這樣的優先順序爲它找到驅動程序: A、 B、 C。
led {
	compatible = “A”, “B”, “C”;
};
  • model

    • model 用來準確地定義這個硬件是什麼
    • 比如一個單板的設備文件的根節點下compatible屬性,可以兼容內核中的“smdk2440”,也兼容“mini2440”。但是它到底是什麼板?用 model 屬性來明確。
    • 比如有 2 款板子配置基本一致, 它們的 compatible 是一樣的,那麼就通過 model 來分辨這 2 款板子
  • status

    • 最後追加的值會覆蓋前面的值,即只有最後一次賦的值纔有效!
    • dtsi 文件中定義了很多設備,但是在你的板子上某些設備是沒有的。這時你可以給這個設備節點添加一個 status 屬性,設置爲“disabled”。
    • 通常使用的屬性值:“okay”和“disabled”

示例

//address-cells 爲 1,所以 reg 中用 1 個數來表示地址,即用 0x80000000 來表示地址
//size-cells 爲 1,所以 reg 中用 1 個數來表示大小,即用 0x20000000 表示大小
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		reg = <0x80000000 0x20000000>;
	};
};
  • #address-cells、 #size-cells

    • cell 指一個 32 位的數值
    • address-cells: address 要用多少個 32 位數來表示;size-cells: size 要用多少個 32 位數來表示。
  • reg

    • 本意是 register,用來表示寄存器地址。但在設備樹裏,它可以用來描述一段空間。
    • reg 屬性的值,是一系列的“address size”,用多少個 32 位的數來表示 address 和 size,由其父節點的#address-cells、 #size-cells 決定。

示例:

/dts-v1/;
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		reg = <0x80000000 0x20000000>;
	};
};

對於 ARM 系統,寄存器和內存是統一編址的,即訪問寄存器時用某塊地址,訪問內存時用某塊地址,在訪問方法上沒有區別。

  • name(已過時)

    • 它的值是字符串,用來表示節點的名字。在跟 platform_driver 匹配時,優先級最低。compatible 屬性在匹配過程中,優先級最高。
  • device_type(已過時)

    • 它的值是字符串,用來表示節點的類型。在跟 platform_driver 匹配時,優先級爲中。compatible 屬性在匹配過程中,優先級最高。

OF函數【Open Firmware 開放固件】

  • 內核源碼中 include/linux/目錄下有很多 of 開頭的頭文件。
  • 驅動通過OF函數獲取設備樹節點信息
    • 要獲取節點內容,需要先獲取節點
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章