1. 程式人生 > >Linux驅動開發09:【裝置樹】nanopi的LED驅動

Linux驅動開發09:【裝置樹】nanopi的LED驅動

介紹

這節通過在nanopi的裝置樹中新增LED子節點,來實現一個LED驅動。由於linux核心支援LED驅動框架並且有通用的LED驅動,因此這裡只需按照驅動要求新增裝置樹節點就可以了,不用我們自己重寫LED驅動。這一節先在裝置樹中新增一個LED節點,重新編譯裝置樹測試效果,然後對核心中drivers/leds/leds-gpio.c進行分析,看該LED驅動是如何實現的。

新增裝置樹節點

我們在sun8i-h3-nanopi-neo-air.dts中新增如下內容:

/ {
    ...
    mygpio-leds {
        compatible = "gpio-leds"
; /* 才能被識別為Led */ pinctrl-names = "default"; pinctrl-0 = <&test_pin>; test { label = "nanopi:red:test"; gpios = <&pio 6 11 GPIO_ACTIVE_HIGH>; /* PG11 */ default-state = "on"; }; }; } &pio { ... test_pin:
[email protected]
0 { pins = "PG11"; function = "gpio_out"; }; }

該節點分為兩部分,第一部分是mygpio-leds子節點,第二部分是[email protected]子節點。第一個節點是根節點下的一個子節點,根據上一節的分析,該節點將被系統解析為一個platform_device。第二個節點是pio的子節點,該節點表明了對應的GPIO引腳編號和該GPIO的模式,這是pinctrl相關的內容,對於pinctrl不同的平臺實現不同,這裡nanopi的pinctrl是用字串來表示引腳號和模式(似乎nanopi的pinctrl只支援設定pin的模式,並不能直接指定該引腳的輸出值)。

再看mygpio-leds節點,其中的compatible = "gpio-leds",用於與platform_drivers匹配,pinctrl-0 = <&test_pin>引用了[email protected]子節點,表明要設定PG11為輸出模式,緊接著是一個子節點test,其中label = "nanopi:red:test"表示該LED名稱,gpios = <&pio 6 11 GPIO_ACTIVE_HIGH>引用了pio標號,該標號定義在sun8i-h3-h5.dtsi檔案中:

pio: [email protected]01c20800 {
            /* compatible is in per SoC .dtsi file */
            reg = <0x01c20800 0x400>;
            interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
            clock-names = "apb", "hosc", "losc";
            gpio-controller;
            #gpio-cells = <3>;
            interrupt-controller;
            #interrupt-cells = <3>;
            ...
}

注意其中的#gpio-cells = <3>屬性,它表明引用該節點時需要三個引數,這在前面有提到,這三個引數的分別表示該GPIO的bank、編號和初始化後預設輸出模式。這裡bank=6,按照資料手冊上的定義,0表示A,1表示B…6就表示G,編號為11,也就是PG11,初始化為高電平。

測試

我們先進行測試,重新編譯寫好的裝置樹,然後拷貝到SD卡中,啟動nanopi,進入/sys/class/leds目錄可以看到我們定義的”nanopi:red:test”檔案
這裡寫圖片描述
進入該資料夾的device/of_node目錄可以看到我們定義的屬性
這裡寫圖片描述
最後退回到/sys/class/leds/nanopi:red:test目錄,對brightness檔案進行操作,可控制PG11輸出高電平和低電平,如果在PG11上接一個LED可以看到該LED的亮滅
這裡寫圖片描述

LED驅動的實現分析

核心中實現LED驅動的檔案是drivers/leds/leds-gpio.c,這是一個platform驅動,下面對其進行分析,先看platform_driver的定義。

static const struct of_device_id of_gpio_leds_match[] = {
    { .compatible = "gpio-leds", },
    {},
};

static struct platform_driver gpio_led_driver = {
    .probe      = gpio_led_probe,
    .shutdown   = gpio_led_shutdown,
    .driver     = {
        .name   = "leds-gpio",
        .of_match_table = of_gpio_leds_match,
    },
};

module_platform_driver(gpio_led_driver);

我們定義的mygpio-leds中,compatible = "gpio-leds",因此可以執行gpio_led_probe函式

struct gpio_leds_priv {
    int num_leds;
    struct gpio_led_data leds[];        // 不定長陣列,使用時才分配長度
};

static inline int sizeof_gpio_leds_priv(int num_leds)
{
    return sizeof(struct gpio_leds_priv) +
        (sizeof(struct gpio_led_data) * num_leds);
}

 drivers --- leds --- leds-gpio.c --- gpio_led_probe(                 --- struct gpio_led_platform_data *pdata
                                   |    struct platform_device *pdev)  |- pdata = dev_get_platdata(&pdev->dev)
                                   |                                   |- if (pdata && pdata->num_leds) {
                                   |                                   |      ...
                                   |                                   |  } else {
                                   |                                   |      priv = gpio_leds_create(pdev)
                                   |                                   |  }
                                   |                                   |- platform_set_drvdata(pdev, priv)
                                   |- gpio_leds_create(               --- int count
                                   |    struct platform_device *pdev)  |- struct gpio_leds_priv *priv
                                   |                                   |- struct fwnode_handle *child
                                   |                                   |- count = device_get_child_node_count(dev)
                                   |                                   |- priv = devm_kzalloc(dev, 
                                   |                                   |      sizeof_gpio_leds_priv(count), GFP_KERNEL)
                                   |                                   |- device_for_each_child_node(dev, child) {
                                   |                                   |      struct gpio_led_data *led_dat;
                                   |                                   |      struct gpio_led led = {};
                                   |                                   |      struct device_node *np;
                                   |                                   |      np = to_of_node(child); 
                                   |                                   |      led_dat = &priv->leds[priv->num_leds];
                                   |                                   |*     fwnode_property_read_string(child, 
                                   |                                   |              "label", &led.name);
                                   |                                   |*     led.gpiod = devm_fwnode_get_gpiod_from_child(
                                   |                                   |              dev, NULL, child, GPIOD_ASIS, led.name);
                                   |                                   |      // 獲取gpio_desc
                                   |                                   |*     create_gpio_led(&led, led_dat, dev, NULL);
                                   |                                   |      // 註冊到LED驅動框架
                                   |                                   |      priv->num_leds++;
                                   |                                   |- }
                                   |- create_gpio_led(                  --- led_dat->gpiod = template->gpiod;
                                        const struct gpio_led *template, |- if (!led_dat->gpiod) { // 這裡不是空
                                        struct gpio_led_data *led_dat,   |      ...
                                        struct device *parent,           |  }
                                        gpio_blink_set_t blink_set)      |- led_dat->cdev.name = template->name
                                                                         |- 對led_dat的一系列賦值
                                                                         |* gpiod_direction_output(led_dat->gpiod, state);
                                                                         |* devm_led_classdev_register(parent, &led_dat->cdev);

由於我們解析裝置樹為platform_device時並沒有設定platform_data,因此執行gpio_leds_create函式對裝置樹節點進行解析。首先獲取子節點的數目,對gpio_leds_priv進行動態初始化,遍歷子節點(這裡只有一個子節點),獲取每個子節點的label屬性,然後通過lable屬性獲取gpio_desc,然後呼叫create_gpio_led對LED驅動進行註冊,其中對led_dat->cdev進行了設定,然後利用LED驅動框架將其註冊到系統

linux的LED框架這裡不展開了,該框架與應用層的介面是sysfs,通過讀寫sysfs的檔案,來控制LED的亮滅。

相關推薦

Linux驅動開發09裝置nanopi的LED驅動

介紹 這節通過在nanopi的裝置樹中新增LED子節點,來實現一個LED驅動。由於linux核心支援LED驅動框架並且有通用的LED驅動,因此這裡只需按照驅動要求新增裝置樹節點就可以了,不用我們自己重寫LED驅動。這一節先在裝置樹中新增一個LED節點,重新編譯

Linux驅動開發08裝置MPU6050驅動和i2c驅動

介紹 上一節在nanopi裝置樹的I2C節點下增加了一個MPU6050的子節點,並在sysfs中檢視到了該節點已經被正確解析,這一節我們來修改之前的MPU6050驅動,使之能夠匹配到我們的裝置樹節點,然後再分析裝置樹節點是如何載入到i2c總線上的。 MP

Linux驅動開發11裝置nanopi的PWM驅動

介紹 前兩節利用裝置樹實現了nanopi的LED驅動和按鍵驅動,這一節來實現nonapi的PWM驅動。PWM驅動在核心中也有相應的實現,因此這裡只是按照要求新增裝置樹檔案即可。這一節和之前一樣,首先修改裝置樹檔案進行測試,然後分析核心相應的軟體實現。 新增裝置樹節點 因為在s

Linux驅動開發10裝置nanopi的按鍵驅動

介紹 這一節在nanopi上實現按鍵驅動,和LED驅動一樣,通用的按鍵驅動在linux核心中已經實現好,我們只需要按照要求寫好裝置樹即可,不用我們自己實現按鍵驅動。這一節中首先修改裝置樹並測試按鍵驅動,然後分析drivers/input/keyboard/gpio_keys.c檔案,

Linux驅動開發04裝置驅動和網路裝置驅動

介紹 因為塊裝置驅動和網路裝置驅動實際中用得較少,所以只給出驅動模板,我也沒有具體測試,等到實際用到是再研究吧,溜了溜了。 塊裝置驅動模板 struct xxx_dev { int size; struct request_q

ARM+Linux嵌入式開發05uboot-2017移植重定位

概述 上一節初始化好了串列埠和LED,我們可以用它們進行除錯;也設定好了時鐘和DRAM,為uboot的重定位做好準備。之前所做的所有工作都是在BL1中,也就是uboot的前16KB,而大部分uboot的程式碼還在SD卡中沒有載入記憶體,沒有載入記憶體的原因是之前我們使用的是內部SRA

Exynos4412 中斷驅動開發(三)—— 裝置中中斷節點的建立

https://www.cnblogs.com/tureno/articles/6403946.html 轉載於 :  http://blog.csdn.net/zqixiao_09/article/details/50916212 題目: Exynos4412 中斷驅動

Linux核心移植 part2uboot裝置--生成過程分析

本文從裝置樹軟體控制相關程式碼進行分析,進而理清裝置樹相關的知識。 先放一個裝置樹在記憶體中的結構圖: 分析來源為$(tree)/lib/fdtdec_test.c 一、資料結構 1.1 檔案頭 每個dtb都包含如下結構的檔案頭,用來表示裝

Linux核心移植 part2uboot 裝置--基本概念和原始碼介紹

arm uboot的裝置樹原始檔位於arch/arm/dts/目錄下,網路上有很多介紹Linux裝置樹概念的文章,這裡以dts相關的API為切入點,如果都懂了,裝置樹的東西就迎刃而解了。本篇文章首先記錄一些基本知識,下一篇進行原始碼分析。 一、裝置樹檔案基

TINY4412LINUX移植筆記(23)裝置LCD觸控式螢幕驅動

【TINY4412】LINUX移植筆記:(23)裝置樹 LCD觸控式螢幕驅動 宿主機 : 虛擬機器 Ubuntu 16.04 LTS / X64 目標板[底板]: Tiny4412SDK - 1506 目標板[核心板]

TINY4412LINUX移植筆記(22)裝置LCD按鍵驅動

【TINY4412】LINUX移植筆記:(22)裝置樹 LCD按鍵驅動 宿主機 : 虛擬機器 Ubuntu 16.04 LTS / X64 目標板[底板]: Tiny4412SDK - 1506 目標板[核心板]:

嵌入式Linux裝置驅動開發按鍵驅動程式例項

11.6  按鍵驅動程式例項 11.6.1  按鍵工作原理 高電平和低電平相接怎麼會變成低電平呢 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。 LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部

04-Linux裝置系列-GPIO驅動實踐

1. 前言 GPIO驅動開發可能算是Linux核心裝置驅動開發中最為簡單、最常見的一個方向,對於開發板的按鍵、LED、蜂鳴器、電源控制等模組,可能都是使用GPIO實現的。Linux核心的GPIO子系統在核心不斷的演進過程中進行了多次的重構,本文的第二

linux驅動由淺入深系列裝置驅動之三(塊裝置驅動結構分析,以mmc為例)

linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)前一篇文章介紹了塊裝置驅動在linux框架張的位置關係,本文來分析一下驅動本身。塊裝置驅動的模型還是基本基於字元裝置驅動的,可以簡單理解為塊裝置僅僅增加了操作緩衝區,對使用者操作請求進行佇列重排。因此只在有了

Linux ALSA音效卡驅動之三PCM裝置的建立

1. PCM是什麼         PCM是英文Pulse-code modulation的縮寫,中文譯名是脈衝編碼調製。我們知道在現實生活中,人耳聽到的聲音是模擬訊號,PCM就是要把聲音從模擬轉換成數字訊號的一種技術,他的原理簡單地說就是利用一個固定的頻率對模擬訊號進行取

linux驅動開發-檔案系統與裝置檔案

目錄 1.Linux檔案系統操作 Linux檔案建立,開啟,關閉函式 #檔案許可權最終由mode&umask決定 int creat (const char *filename,mode_t mod

Linux ALSA音效卡驅動之五移動裝置中的ALSA

1.  ASoC的由來 ASoC--ALSA System on Chip ,是建立在標準ALSA驅動層上,為了更好地支援嵌入式處理器和移動裝置中的音訊Codec的一套軟體體系。在ASoc出現之前,核心對於SoC中的音訊已經有部分的支援,不過會有一些侷限性:  

ZYNQ Linux驅動開發——第一個字元裝置驅動

硬體平臺:XCZ7020 CLG484-1 完全適配Zedboard 開發環境:Widows下Vivado 2016.2 、 SDK2016.2 、 Linux機器:debin 目的:操作板載的LED燈LD9,受PS部分的MIO7控制 linux裝置驅

Linux ALSA音效卡驅動之五移動裝置中的ALSA(ASoC)

1.  ASoC的由來 ASoC--ALSA System on Chip ,是建立在標準ALSA驅動層上,為了更好地支援嵌入式處理器和移動裝置中的音訊Codec的一套軟體體系。在ASoc出現之前,核心對於SoC中的音訊已經有部分的支援,不過會有一些侷限性:    C

Linux ALSA音效卡驅動之四Control裝置的建立

Control介面 Control介面主要讓使用者空間的應用程式(alsa-lib)可以訪問和控制音訊codec晶片中的多路開關,滑動控制元件等。對於Mixer(混音)來說,Control介面顯得尤為重要,從ALSA 0.9.x版本開始,所有的mixer工作都是通過co