1. 程式人生 > >led子系統實現led驅動

led子系統實現led驅動

知識整理–Linux驅動框架概念之LED

驅動框架也用模組這種’機制’實現,為了在不需要的時候也能夠解除安裝

何謂驅動框架

什麼是驅動框架,為什麼需要驅動框架,基於驅動框架寫驅動有什麼優勢

驅動程式設計協作要求:(1)介面標準化;(2)儘量降低驅動開發者難度

什麼是驅動框架:標準化的驅動實現``統一管控系統資源,維護系統穩定

(1)核心中驅動部分維護者針對每個種類的驅動設計一套成熟的、標準的、典型的驅動實現,並把不同廠家的同類硬體驅動中相同的部分抽出來自己實現好,再把不同部分留出介面給具體的驅動開發工程師來實現,這就叫驅動框架。

譬如LED 亮滅肯定都會有,這就是同類硬體的相同部分,用核心開發工程師開發的這套成熟的、標準的、典型的驅動去實現。
A廠家的LED能調亮度,B廠家的LED就只有亮滅,那A廠家的調亮度就是同類硬體的不同部分,驅動工程師就要在基本的驅動實現上去新增

(2)核心維護者在核心中設計了一些統一管控系統資源的體系,這些體系讓核心能夠對資源在各個驅動之間的使用統一協調和分配,保證整個核心的穩定健康執行。譬如系統中所有的GPIO就屬於系統資源,每個驅動模組如果要使用某個GPIO就要先呼叫特殊的介面先申請,申請到後使用,使用完後要釋放。又譬如中斷號也是一種資源,驅動在使用前也必須去申請。其他比如框架中的裝置鎖等。這些也是驅動框架的組成部分

(3)一些特定的介面函式、一些特定的資料結構,這些是驅動框架的直接表現。

驅動框架這個概念單靠文字說明很難理解,應在實際驅動程式設計中去體會上面的這幾點

LED驅動框架分析

核心驅動框架中LED的基本情況

開始一件事,不盲目,簡單分析後再去分析原始碼。像分析uboot和kernel先看地圖(Makefile)那樣

相關檔案:
(1)drivers/leds目錄,這個目錄就是驅動框架規定的LED這種硬體的驅動應該待的地方。
(2)led-class.c和led-core.c,這兩個檔案加起來屬於LED驅動框架的第一部分,這兩個檔案是核心開發者提供的,他們描述的是核心中所有廠家的不同LED硬體的相同部分的邏輯。必要的需要花時間的就是核心提供的led-class.c和led-core.c檔案。
(3)leds-xxxx.c,這個檔案是LED驅動框架的第2部分,是由不同廠商的驅動工程師編寫新增的,廠商驅動工程師結合自己公司的硬體的不同情況來對LED進行操作,使用第一部分提供的介面來和驅動框架進行互動,最終實現驅動的功能。

我自己學習使用的開發板是九鼎廠商生產的s5pv210,核心原始碼樹為其提供的linux+qt的kernel
其(九鼎移植的核心)led驅動沒有使用核心推薦的led驅動框架,檔案放在放在drivers/char/led/x210-led.c

案例分析驅動框架的使用:
(1)以leds-s3c24xx.c為例。leds-s3c24xx.c中通過呼叫led_classdev_register來完成LED驅動的註冊,而led_classdev_register是在drivers/leds/led-class.c中定義的。所以其實SoC廠商的驅動工程師是呼叫核心開發者在驅動框架中提供的介面來實現自己的驅動的。
(2)驅動框架的關鍵點就是:分清楚核心開發者提供了什麼,驅動開發者自己要提供什麼

典型的驅動開發行業現狀:
(1)核心開發者對驅動框架進行開發和維護、升級,對應led-class.c和led-core.c
(2)SoC廠商的驅動工程師對裝置驅動原始碼進行編寫、除錯,提供參考版本,對應leds-s3c24xx.c
(3)做產品的廠商的驅動工程師以SoC廠商提供的驅動原始碼為基礎,來做移植和除錯

LED驅動框架原始碼

涉及到的檔案:led-core.c和led-class.c

經過基本分析經過基本分析,發現LED驅動框架中核心開發者實現的部分主要是led-class.c。
led-class.c就是一個核心模組,對led-class.c分析應該從下往上,遵從對模組的基本分析方法。
為什麼LED驅動框架中核心開發者實現的部分要實現成一個模組?因為核心開發者希望這個驅動框架是可以被裝載/解除安裝的。

blog009

leds_init
blog010

類的建立。leds_init除了對class結構體變數進行賦值,還做了class_create,所以到開發板下 ls /sys/class可以看到多了一個類leds,但是裡面是空的。裡面的用來與應用層進行互動的檔案是驅動程式要去建立的,這就是驅動工程師要做的(用提供的介面去建立)

led_class_attrs:
(1)attribute是什麼,對應將來/sys/class/leds/xxx目錄裡的內容,一般是檔案和資料夾。這些檔案其實就是sysfs開放給應用層的一些操作介面(非常類似於/dev/目錄下的那些裝置檔案)
(2)attribute有什麼用,作用就是讓應用程式可以通過/sys/class/leds/xxx目錄下面的屬性檔案來操作驅動進而操作硬體裝置。
(3)attribute其實是另一條驅動實現的路線。有區別於之前講的file_operations那條線。不同裝置驅動實現走的路不一定,像這邊的led走的是attribute,而LCD走的是file_operations

struct class {
    const char      *name;
    struct module       *owner;

    struct class_attribute      *class_attrs;
    struct device_attribute     *dev_attrs;
    struct kobject          *dev_kobj;

    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, mode_t *mode);

    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct kobj_ns_type_operations *ns_type;
    const void *(*namespace)(struct device *dev);

    const struct dev_pm_ops *pm;

    struct class_private *p;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

喚醒(resume)、掛起(suspend):

led_classdev_register
blog011

屬於某一類的裝置的建立。分析可知,led_classdev_register這個函式其實就是去建立一個屬於leds這個類的一個裝置。其實就是去註冊一個裝置。所以這個函式其實就是led驅動框架中核心開發者提供給SoC廠家驅動開發者的一個註冊驅動的介面
當使用led驅動框架去編寫驅動的時候,這個led_classdev_register函式的作用類似於驅動程式使用file_operations方式去註冊字元裝置驅動時的register_chrdev函式。

struct led_classdev {
    const char      *name;
    int          brightness;
    int          max_brightness;
    int          flags;
    ...
    /* Set LED brightness level */
    /* Must not sleep, use a workqueue if needed */
    void        (*brightness_set)(struct led_classdev *led_cdev,
                      enum led_brightness brightness);
    /* Get LED brightness level */
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
    ...
    struct device       *dev;
    struct list_head     node;          /* LED Device list */
    const char      *default_trigger;   /* Trigger to use */

    ...
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

基於驅動框架寫LED驅動

實踐之前需要確認核心是否新增led驅動框架支援(通過make menuconfig配置,設定成功後到開發板下ls /sys/class可以看到多了一個類leds 但是裡面是空的)

一般不從零開始寫,參考哪裡? drivers/leds/leds-s3c24xx.c;
思路/關鍵點:led_classdev_register,我們之前寫的led驅動,直接看的是核心裡面提供的操作介面,譬如register_chardev,cdev結構體等那些
仍然是驅動模組,module_init(XXX),insmod時會去呼叫XXX去註冊,在xxx模組安裝函式中去使用專用的介面

注:以前看註冊成功與否,cat     /proc/devices去看資訊變化。現在要看/sys/class/leds 如果註冊成功該目錄下會多一些檔案

圖片.5-2-05

現象: 
第1:寫的驅動能夠工作了,被載入了,/sys/class/leds/目錄下多出來了一個表示裝置的資料夾。資料夾裡面有相應的操控led硬體的2個屬性brightness和max_brightness
第2:led-class.c中brightness方法有一個show方法和store方法,這兩個方法對應使用者在/sys/class/leds/myled/brightness目錄下直接去讀寫這個檔案時實際執行的程式碼。
show brightness ...時,實際就會執行led_brightness_show函式
echo 1 > brightness時,實際就會執行led_brightness_store函式

show方法實際要做的就是讀取LED硬體資訊,然後把硬體資訊返回給使用者即可。所以show方法和store方法必須能夠操控硬體。但led-class.c檔案屬於驅動框架中的檔案,本身無法直接讀取具體硬體,因此在show和store方法中使用函式指標的方式呼叫了struct led_classdev結構體中的相應的讀取/寫入硬體資訊的方法(在Linux/leds.h中,寫驅動就一定要包含這個標頭檔案和對應的led_classdev結構體型別變數。當有多個個同類裝置(即多個led_classdev結構體型別變數)它是怎麼判段的呢?–不難,文章不想寫太長,自行分析)(算了還是寫一下吧,都整理出來了)。
struct led_classdev結構體中的實際用來讀寫硬體資訊的函式,就是驅動檔案leds-s5pv210.c中要提供的(如示例程式)。

分析echo 1 > b brightness 是如何傳遞的:
p.5-2-06

在硬體操作上驅動只應該提供機制而不是策略

在驅動中將4個LED分開,使應用層可以完全按照自己的需要對LED進行控制

好處:驅動層實現對各個LED裝置的獨立訪問,並嚮應用層展示出4個操作介面led1、led2、led3、led4,這樣應用層可以完全按照自己的需要對LED進行控制。

驅動的設計理念:不要對最終需求功能進行假定,而應該只是直接的對硬體的操作。有一個概念就是:機制和策略的問題。在硬體操作上驅動只應該提供機制而不是策略。策略由應用程式來做。比如有人要造反,提供槍支彈藥這就是機制;比如馬克思主義,沒有槍支彈藥,但是有策略

總結:LED驅動開發

不是很複雜的一個框架,多分析思考;關鍵點:細節另外,要明白寫的驅動如何與框架結合(即應用到硬體是怎樣的一條路線)

p.5-2-07