日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

设备树下的platform 驱动编写

發布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设备树下的platform 驱动编写 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 設備樹下的platform 驅動簡介
  • 硬件原理圖分析
  • 實驗程序編寫
    • 修改設備樹文件
    • platform 驅動程序編寫
    • 編寫測試APP
  • 運行測試
    • 編譯驅動程序和測試APP
    • 運行測試

上一章我們詳細的講解了Linux 下的驅動分離與分層,以及總線、設備和驅動這樣的驅動框架。基于總線、設備和驅動這樣的驅動框架,Linux 內核提出來platform 這個虛擬總線,相應的也有platform 設備和platform 驅動。上一章我們講解了傳統的、未采用設備樹的platform 設備和驅動編寫方法。最新的Linux 內核已經支持了設備樹,因此在設備樹下如何編寫platform驅動就顯得尤為重要,本章我們就來學習一下如何在設備樹下編寫platform 驅動。

設備樹下的platform 驅動簡介

platform 驅動框架分為總線、設備和驅動,其中總線不需要我們這些驅動程序員去管理,這個是Linux 內核提供的,我們在編寫驅動的時候只要關注于設備和驅動的具體實現即可。在沒有設備樹的Linux 內核下,我們需要分別編寫并注冊platform_device 和platform_driver,分別代表設備和驅動。在使用設備樹的時候,設備的描述被放到了設備樹中,因此platform_device 就不需要我們去編寫了,我們只需要實現platform_driver 即可。在編寫基于設備樹的platform 驅動的時候我們需要注意一下幾點:

1、在設備樹中創建設備節點
毫無疑問,肯定要先在設備樹中創建設備節點來描述設備信息,重點是要設置好compatible屬性的值,因為platform 總線需要通過設備節點的compatible 屬性值來匹配驅動!這點要切記。
比如,我們可以編寫如下所示的設備節點來描述我們本章實驗要用到的LED 這個設備:

1 gpioled { 2 #address-cells = <1>; 3 #size-cells = <1>; 4 compatible = "atkalpha-gpioled"; 5 pinctrl-names = "default"; 6 pinctrl-0 = <&pinctrl_led>; 7 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; 8 status = "okay"; 9 };

示例55.1.1 中的gpioled 節點其實就是45.4.1.2 小節中創建的gpioled 設備節點,我們可以直接拿過來用。注意第4 行的compatible 屬性值為“atkalpha-gpioled”,因此一會在編寫platform驅動的時候of_match_table 屬性表中要有“atkalpha-gpioled”。

2、編寫platform 驅動的時候要注意兼容屬性
上一章已經詳細的講解過了,在使用設備樹的時候platform 驅動會通過of_match_table 來保存兼容性值,也就是表明此驅動兼容哪些設備。所以,of_match_table 將會尤為重要,比如本例程的platform 驅動中platform_driver 就可以按照如下所示設置:

1 static const struct of_device_id leds_of_match[] = { 2 { .compatible = "atkalpha-gpioled" }, /* 兼容屬性*/ 3 { /* Sentinel */ } 4 }; 5 6 MODULE_DEVICE_TABLE(of, leds_of_match); 7 8 static struct platform_driver leds_platform_driver = { 9 .driver = { 10 .name = "imx6ul-led", 11 .of_match_table = leds_of_match, 12 }, 13 .probe = leds_probe, 14 .remove = leds_remove, 15 };

第1~4 行,of_device_id 表,也就是驅動的兼容表,是一個數組,每個數組元素為of_device_id類型。每個數組元素都是一個兼容屬性,表示兼容的設備,一個驅動可以跟多個設備匹配。這里我們僅僅匹配了一個設備,那就是55.1.1 中創建的gpioled 這個設備。第2 行的compatible 值為“atkalpha-gpioled”,驅動中的compatible 屬性和設備中的compatible 屬性相匹配,因此驅動中對應的probe 函數就會執行。注意第3 行是一個空元素,在編寫of_device_id 的時候最后一個元素一定要為空!

第6 行,通過MODULE_DEVICE_TABLE 聲明一下leds_of_match 這個設備匹配表。

第11 行,設置platform_driver 中的of_match_table 匹配表為上面創建的leds_of_match,至此我們就設置好了platform 驅動的匹配表了。

3、編寫platform 驅動
基于設備樹的platform 驅動和上一章無設備樹的platform 驅動基本一樣,都是當驅動和設備匹配成功以后就會執行probe 函數。我們需要在probe 函數里面執行字符設備驅動那一套,
當注銷驅動模塊的時候remove 函數就會執行,都是大同小異的。

硬件原理圖分析

本章實驗我們只使用到IMX6U-ALPHA 開發板上的LED 燈,因此實驗硬件原理圖參考8.3小節即可。

實驗程序編寫

本實驗對應的例程路徑為:開發板光盤-> 2、Linux 驅動例程-> 18_dtsplatform。
本章實驗我們編寫基于設備樹的platform 驅動,所以需要在設備樹中添加設備節點,然后我們只需要編寫platform 驅動即可。

修改設備樹文件

首先修改設備樹文件,加上我們需要的設備信息,本章我們就使用到一個LED 燈,因此可以直接使用45.4.1 小節編寫的gpioled 子節點即可,不需要再重復添加。

platform 驅動程序編寫

設備已經準備好了,接下來就要編寫相應的platform 驅動了,新建名為“18_dtsplatform”的文件夾,然后在18_dtsplatform 文件夾里面創建vscode 工程,工作區命名為“dtsplatform”。
新建名為leddriver.c 的驅動文件,在leddriver.c 中輸入如下所示內容:

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/irq.h> #include <linux/wait.h> #include <linux/poll.h> #include <linux/fs.h> #include <linux/fcntl.h> #include <linux/platform_device.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : leddriver.c 作者 : 左忠凱 版本 : V1.0 描述 : 設備樹下的platform驅動 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/8/13 左忠凱創建 ***************************************************************/#define LEDDEV_CNT 1 /* 設備號長度 */ #define LEDDEV_NAME "dtsplatled" /* 設備名字 */ #define LEDOFF 0 #define LEDON 1/* leddev設備結構體 */ struct leddev_dev{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */int major; /* 主設備號 */ struct device_node *node; /* LED設備節點 */int led0; /* LED燈GPIO標號 */ };struct leddev_dev leddev; /* led設備 *//** @description : LED打開/關閉* @param - sta : LEDON(0) 打開LED,LEDOFF(1) 關閉LED* @return : 無*/ void led0_switch(u8 sta) {if (sta == LEDON )gpio_set_value(leddev.led0, 0);else if (sta == LEDOFF)gpio_set_value(leddev.led0, 1); }/** @description : 打開設備* @param - inode : 傳遞給驅動的inode* @param - filp : 設備文件,file結構體有個叫做private_data的成員變量* 一般在open的時候將private_data指向設備結構體。* @return : 0 成功;其他 失敗*/ static int led_open(struct inode *inode, struct file *filp) {filp->private_data = &leddev; /* 設置私有數據 */return 0; }/** @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符* @param - buf : 要寫給設備寫入的數據* @param - cnt : 要寫入的數據長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節數,如果為負值,表示寫入失敗*/ static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[2];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];if (ledstat == LEDON) {led0_switch(LEDON);} else if (ledstat == LEDOFF) {led0_switch(LEDOFF);}return 0; }/* 設備操作函數 */ static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write, };/** @description : flatform驅動的probe函數,當驅動與* 設備匹配以后此函數就會執行* @param - dev : platform設備* @return : 0,成功;其他負值,失敗*/ static int led_probe(struct platform_device *dev) { printk("led driver and device was matched!\r\n");/* 1、設置設備號 */if (leddev.major) {leddev.devid = MKDEV(leddev.major, 0);register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);} else {alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);leddev.major = MAJOR(leddev.devid);}/* 2、注冊設備 */cdev_init(&leddev.cdev, &led_fops);cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);/* 3、創建類 */leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)) {return PTR_ERR(leddev.class);}/* 4、創建設備 */leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {return PTR_ERR(leddev.device);}/* 5、初始化IO */ leddev.node = of_find_node_by_path("/gpioled");if (leddev.node == NULL){printk("gpioled node nost find!\r\n");return -EINVAL;} leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);if (leddev.led0 < 0) {printk("can't get led-gpio\r\n");return -EINVAL;}gpio_request(leddev.led0, "led0");gpio_direction_output(leddev.led0, 1); /* led0 IO設置為輸出,默認高電平 */return 0; }/** @description : platform驅動的remove函數,移除platform驅動的時候此函數會執行* @param - dev : platform設備* @return : 0,成功;其他負值,失敗*/ static int led_remove(struct platform_device *dev) {gpio_set_value(leddev.led0, 1); /* 卸載驅動的時候關閉LED */cdev_del(&leddev.cdev); /* 刪除cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注銷設備號 */device_destroy(leddev.class, leddev.devid);class_destroy(leddev.class);return 0; }/* 匹配列表 */ static const struct of_device_id led_of_match[] = {{ .compatible = "atkalpha-gpioled" },{ /* Sentinel */ } };/* platform驅動結構體 */ static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led", /* 驅動名字,用于和設備匹配 */.of_match_table = led_of_match, /* 設備樹匹配表 */},.probe = led_probe,.remove = led_remove, };/** @description : 驅動模塊加載函數* @param : 無* @return : 無*/ static int __init leddriver_init(void) {return platform_driver_register(&led_driver); }/** @description : 驅動模塊卸載函數* @param : 無* @return : 無*/ static void __exit leddriver_exit(void) {platform_driver_unregister(&led_driver); }module_init(leddriver_init); module_exit(leddriver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");

第33~112 行,傳統的字符設備驅動,沒什么要說的。

第120~164 行,platform 驅動的probe 函數,當設備樹中的設備節點與驅動之間匹配成功以后此函數就會執行,原來在驅動加載函數里面做的工作現在全部放到probe 函數里面完成。

第171~180 行,remobe 函數,當卸載platform 驅動的時候此函數就會執行。在此函數里面釋放內存、注銷字符設備等,也就是將原來驅動卸載函數里面的工作全部都放到remove 函數中完成。

第183~186 行,匹配表,描述了此驅動都和什么樣的設備匹配,第184 行添加了一條值為"atkalpha-gpioled"的compatible 屬性值,當設備樹中某個設備節點的compatible 屬性值也為“atkalpha-gpioled”的時候就會與此驅動匹配。

第189~196 行,platform_driver 驅動結構體,191 行設置這個platform 驅動的名字為“imx6ul-led”,因此,當驅動加載成功以后就會在/sys/bus/platform/drivers/目錄下存在一個名為“imx6u-
led”的文件。第192 行設置of_match_table 為上面的led_of_match。

第203~206 行,驅動模塊加載函數,在此函數里面通過platform_driver_register 向Linux 內核注冊led_driver 驅動。

第213~216 行,驅動模塊卸載函數,在此函數里面通過platform_driver_unregister 從Linux內核卸載led_driver 驅動。

編寫測試APP

測試APP 就直接使用上一章54.4.2 小節編寫的ledApp.c 即可。

運行測試

編譯驅動程序和測試APP

1、編譯驅動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為“leddriver.o”,Makefile 內容如下所示:

KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd)obj-m := leddriver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4 行,設置obj-m 變量的值為“leddriver.o”。
輸入如下命令編譯出驅動模塊文件:

make -j32

編譯成功以后就會生成一個名為“leddriver.o”的驅動模塊文件。
2、編譯測試APP

測試APP 直接使用上一章的ledApp 這個測試軟件即可。

運行測試

將上一小節編譯出來leddriver.ko 拷貝到rootfs/lib/modules/4.1.15 目錄中,重啟開發板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載leddriver.ko 這個驅動模塊。

depmod //第一次加載驅動的時候需要運行此命令 modprobe leddriver.ko //加載驅動模塊

驅動模塊加載完成以后到/sys/bus/platform/drivers/目錄下查看驅動是否存在,我們在leddriver.c 中設置led_driver (platform_driver 類型)的name 字段為“imx6ul-led”,因此會在/sys/bus/platform/drivers/目錄下存在名為“imx6ul-led”這個文件,結果如圖55.4.2.1 所示:


同理,在/sys/bus/platform/devices/目錄下也存在led 的設備文件,也就是設備樹中gpioled 這個節點,如圖55.4.2.2 所示:

驅動和模塊都存在,當驅動和設備匹配成功以后就會輸出如圖55.4.2.3 所示一行語句:

驅動和設備匹配成功以后就可以測試LED 燈驅動了,輸入如下命令打開LED 燈:

./ledApp /dev/dtsplatled 1 //打開LED 燈

在輸入如下命令關閉LED 燈:

./ledApp /dev/dtsplatled 0 //關閉LED 燈

觀察一下LED 燈能否打開和關閉,如果可以的話就說明驅動工作正常,如果要卸載驅動的話輸入如下命令即可:

rmmod leddriver.ko

總結

以上是生活随笔為你收集整理的设备树下的platform 驱动编写的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。