linux驅動(六)led驅動框架

1:在linux2.6板本內核開發人員開始創建驅動框架,以led驅動爲例:html

沒有驅動框架的時候咱們須要作一下事情:java

module_init:   node

    1:alloc_chrdev_region 註冊字符驅動linux

    2:cdev_alloc、cdev_init、cdev_add來向內核中添加驅動;c++

    3:class_create 建立類編程

    4:device_create建立驅動設備cdevapi

    5:ioremap/使用靜態的虛擬地址數組

module_exit:框架

    1:iounmap編程語言

    2:release_mem_region

    3:device_destroy

    4:class_destroy

    5:cdev_del

    6:unregister_chrdev_region

咱們在不使用驅動框架的狀況下須要本身來作以上事情,比較繁瑣;下面來分析一下led的驅動框架:

內核工程師創建linux驅動框架的兩個文件在/drivers/leds目錄下文件名爲:led-core.c、led-class.c

咱們首先分下一下led-class.c文件,看一下內核工程師關於led驅動都作了哪些事情,

subsys_initcall(leds_init);
module_exit(leds_exit);

subsys_initcall 在/include/linux/init.h頭文件中:

#define subsys_initcall(fn) __define_initcall("4",fn,4)

#define __define_initcall(level,fn,id) static initcall_t __initcall_##fn##id  __used __attribute__((__section__(".initcall" level ".init"))) = fn

展開之後:

static initcall_t  __initcall_leds_init4  __used __attribute__((__section__(".initcall"4."init"))) = leds_init

static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); if (IS_ERR(leds_class)) return PTR_ERR(leds_class); leds_class->suspend = led_suspend; leds_class->resume = led_resume; leds_class->dev_attrs = led_class_attrs; return 0; }

首先是內核工程師創建的模塊初始化:

leds_init 這個函數中調用的是class_create函數,建立了leds這個類,因此在/sys/class目錄下會有leds這個類目錄;

而且對這個類初始化

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; };

 

static struct device_attribute led_class_attrs[] = { __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), #endif __ATTR_NULL, };

/************************************

#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}

*************************************************************/

 

這裏是一個結構體數組,裏面每一個__ATTR宏表達式都是一個結構體,這個宏的結構以下所示,這個宏就是對device_attribute裏的元素賦值;實際上device_attribute

結構體最終會經過device_create函數建立成具備必定權限的文件,文件名爲 name 權限 mode_t 文件讀的方式:.show 寫的方式.store 

這個文件直接表明了硬件的一個特性,以  __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),爲例會在leds目錄下建立一個brightness的文件,這個

文件使用來控制硬件led的brightness屬性的,這個文件的執行權限位0644 即110100100 文件全部者權限可讀可寫。。。咱們最終操做硬件就是經過讀寫這個文件來實現的,

這個文件的讀寫方法就是.show 和 .store 方法,

struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };
struct attribute { const char        *name; struct module        *owner; mode_t mode; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key    *key; struct lock_class_key skey; #endif };

下面咱們看一下linux內核爲咱們構建的一個led設備結構:

struct led_classdev { const char        *name; int brightness; int max_brightness; int flags; /* Lower 16 bits reflect status */
#define LED_SUSPENDED        (1 << 0)
    /* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME    (1 << 16)

    /* 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); /* Activate hardware accelerated blink, delays are in * miliseconds and if none is provided then a sensible default * should be chosen. The call can adjust the timings if it can't * match the values specified exactly. */
    int        (*blink_set)(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off); struct device        *dev; struct list_head     node;            /* LED Device list */
    const char        *default_trigger;    /* Trigger to use */ #ifdef CONFIG_LEDS_TRIGGERS /* Protects the trigger data below */
    struct rw_semaphore trigger_lock; struct led_trigger    *trigger; struct list_head trig_list; void            *trigger_data; #endif };

上面這個結構體就是linux內核爲咱們建立的通用的led設備模型,一個結構體對應一個led設備,這種思想就是面向對象的思想,其實c++以及java這種面向對象的編程語言,底層的實現

就是基於這方方式的,把一個對象的全部名稱、屬性,以及操做方法封裝在一個結構體中,咱們對這個對象執行的操做就至關於對這個結構體執行操做就能夠了;

下面對這個結構體中的元素分析一下 

*name:設備命

 brightness 亮度

max_brightness:最大亮度

brightness_set函數指針,這個設置指向真正的設置亮度的函數

led_brightness_store

    led_set_brightness

        led_cdev->brightness_set(led_cdev, value);   

//能夠看出linux內核中調用是這麼調用的應用層對brightness文件的寫操做執行的是.store對應的led_brightness_store 而後調用led_set_brightness而後在調用結構體中的brightness_set函數真正的執行led的寫操做這裏一共有4層調用;一樣show也是這個原理;

。。。。。。。。。。。。。。。。。。。

在來看一下驅動工程師應該作的事情,

這個函數中作的事情:

1: 

struct s3c24xx_gpio_led *led;
struct s3c24xx_gpio_led {
    struct led_classdev         cdev;
    struct s3c24xx_led_platdata    *pdata;
};

建立led驅動設備的結構體指針, struct led_classdev cdev; 這個結構體就是咱們上面分析的led設備結構體

led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);

用kzalloc函數對這個指針分配內存;

led->cdev.brightness_set = s3c24xx_led_set;
    led->cdev.default_trigger = pdata->def_trigger;
    led->cdev.name = pdata->name;
    led->cdev.flags |= LED_CORE_SUSPENDRESUME;

    led->pdata = pdata;

    /* no point in having a pull-up if we are always driving */

    if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
        s3c2410_gpio_setpin(pdata->gpio, 0);
        s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
    } else {
        s3c2410_gpio_pullup(pdata->gpio, 0);
        s3c2410_gpio_setpin(pdata->gpio, 0);
        s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
    }

對這個結構體賦值,關鍵的最經常使用的幾個值:

name

brightness_set 

ret = led_classdev_register(&dev->dev, &led->cdev);

接下來就是註冊led設備了;

led_classdev_register

    device_create

在調用device_create函數建立相應的文件;

------------------------------------------------------------------------------------------------------------------------------------------

最後在分析一下本身寫的led驅動與使用驅動框架的本質區別在哪裏:

本身寫的使用通用的設備模型cdev這個結構體,對設備操做時使用內核提供的file_opetations操做函數結構體,而應用層執行使用api調用/dev/目錄下的設備文件節點直接操做便可;

而使用框架的話,就是內核專門爲led提供了一個led的設備驅動模型,把全部的關於led的操做屬性,都放到了這個驅動模型中,而後驅動工程師在對這個驅動模型進行初始化便可;

這就是使用和不使用驅動模型的本質區別;

參考文章:http://www.2cto.com/kf/201609/545540.html 

-----------------------------------------------------------------------------------------------------------------------------------------------------

補充:對九鼎寫的x210驅動進行分析(未完待續)

相關文章
相關標籤/搜索