Linux内核LED模块分析,Linux设备模型之led子系统

一代分裂了,连led都成子系统了,针对内核提供的通用模型,解析一下,好久没写小说了也!

一代分歧了,连led都成子系统了,针对内核提供的通用模型,剖判一下,好久没写小说了也!

剖判三个内核模块。笔者前面就选了LED模块分析,LED模块解析不算难,但要说知道其实仍然很挑衅的。明日吾的篇章被引入到首页了。挺有成就感的。笔者的稿子纵然不登大雅之堂,但假诺能给到大家一道琼斯指数点,哪怕就一丢丢本人就喜气洋洋了。好了,闲话非常的少说了,初阶大家的linux内核之旅吧。

转自:

代码位于drivers/leds下,看一下Makefile 模型文件器重是:

代码位于drivers/leds下,看一下Makefile 模型文件重视是:

这一节是应群里三人兄弟的供给讲LED模块,笔者稍稍看了瞬间,就挑了叁个最软的朱果来捏。怎么着挑到八个最软的红嘟嘟呢?首先我们在解析那些模块此前率先件事便是想办法缩短范围,假诺不裁减范围,你本身的生命力都以缺乏的,大概会搞的可怜难熬,怎么压缩。观念宝:Kconfig Makefile。大家先是步入/drivers/leds的目录,然后查看Makefile,能够见到:
# LED Core
obj-$(CONFIG_NEW_LEDS)                  = led-core.o
obj-$(CONFIG_LEDS_CLASS)                = led-class.o
obj-$(CONFIG_LEDS_TRIGGERS)             = led-triggers.o

版权全体,转载必得表达转自  

# LED Core

# LED Core

# LED Platform Drivers
obj-$(CONFIG_LEDS_88PM860X)             = leds-88pm860x.o
obj-$(CONFIG_LEDS_ATMEL_PWM)            = leds-atmel-pwm.o
。。。。
很猛烈,前七个公文是逃可是的,那个东西叫core。在根本中凡是叫core的事物基本是属于必须要看的,没事,才八个文本,匹夫不怕(那都怕的就别玩了^_^)。哥多少个说要分析led的trigger,OK没难题,我就从头挑三个轻易领会点的模块了。ledtrig-heartbeat.c。哥为啥要挑这一个模块讲啊?在说原因在此之前自个儿要么重申,有的东西只好见招拆招,要多动脑子,多找规律。作者是如此找的:展开Kconfig查看一下对应的音信。Kconfig给了笔者们什么样音讯呢?首先正是注释,其次是它本人注重的模块。(又见潜准则了,大家的Computer之所以能跑起来就是一大堆的潜法则。)那是Kconfig对trigger_heartbeat的描述:
config LEDS_TRIGGER_HEARTBEAT
        tristate "LED Heartbeat Trigger"
        depends on LEDS_TRIGGERS
        help
          This allows LEDs to be controlled by a CPU load average.
          The flash frequency is a hyperbolic function of the 1-minute
          load average.
          If unsure, say Y.
咱俩得以窥见这么一句: depends on LEDS_TEvoqueIGGEKugaS,那玩意正是大家近来看到的makefile中的那三个core宏所包涵的,哥心中暗爽了一把。捡了个现成的平价。在对应的makefile中办理入LEDS_TRIGGER_HEARTBEAT查找后得:
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)    = ledtrig-heartbeat.o
近期找准了大家要分析的靶子后,就要打场硬仗了。
第一大家要有贰个意识:像读写锁等这么些用于共同互斥等的招数对于我们精通模块的系统布局的熏陶是相当小的,我们要先避实就虚,小编今后在深入分析模块的时候已经造成了一种语感,一眼扫过去,这种东西根本进不了小编的脑瓜儿中,能够比较急忙的扫到首要的东西,进而加快自已深入分析代码的快慢,在骨子里写可能改两个内核模块的时候,

原创笔者:德班邮政和邮电通讯学院  通讯与消息连串正式 研二 魏清

obj-$(CONFIG_NEW_LEDS)            = led-core.o

obj-$(CONFIG_NEW_LEDS)             = led-core.o

能够再细致剖判这一个共同或互斥的手法。
很刚强,哥一眼扫过去后,开掘那个函数做的事其实挺少的:
int led_trigger_register(struct led_trigger *trigger)
{
 struct led_classdev *led_cdev;
 struct led_trigger *trig;

 

obj-$(CONFIG_LEDS_CLASS)        = led-class.o

obj-$(CONFIG_LEDS_CLASS)         = led-class.o

 rwlock_init(&trigger->leddev_list_lock);
 INIT_LIST_HEAD(&trigger->led_cdevs);

 

obj-$(CONFIG_LEDS_TRIGGERS)        = led-triggers.o

obj-$(CONFIG_LEDS_TRIGGERS)         = led-triggers.o

 down_write(&triggers_list_lock);
 /* Make sure the trigger's name isn't already in use */
 list_for_each_entry(trig, &trigger_list, next_trig) {
  if (!strcmp(trig->name, trigger->name)) {
   up_write(&triggers_list_lock);
   return -EEXIST;
  }
 }
 /* Add to the list of led triggers */
 list_add_tail(&trigger->next_trig, &trigger_list);
 up_write(&triggers_list_lock);

三.Backlight宗旨驱动

     直接看led-core文件呢,那几个文件无比个性,主体内容四行

     直接看led-core文件呢,这么些文件无比本性,主体内容四行

 /* Register with any LEDs that have this as a default trigger */
 down_read(&leds_list_lock);
 list_for_each_entry(led_cdev, &leds_list, node) {
  down_write(&led_cdev->trigger_lock);
  if (!led_cdev->trigger && led_cdev->default_trigger &&
       !strcmp(led_cdev->default_trigger, trigger->name))
   led_trigger_set(led_cdev, trigger);
  up_write(&led_cdev->trigger_lock);
 }
 up_read(&leds_list_lock);

上面我们讲讲backlight子系统。背光子系统目录在/driver/video/backlight下,当中背光子系统主题代码是backlight.c

 DECLARE_RWSEM(leds_list_lock);
EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);                                         //链接全体led的全局链表
EXPORT_SYMBOL_GPL(leds_list);

 DECLARE_RWSEM(leds_list_lock);
EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);                                         //链接全体led的全局链表
EXPORT_SYMBOL_GPL(leds_list);

 return 0;
}
其一函数从哥的眼中看那便是四块:

先查看/driver/video/backlight/Makefile

    再来看下led-class.c函数,这里先来介绍一下描述led的为主结构体

    再来看下led-class.c函数,这里先来介绍一下汇报led的大旨结构体

  1. 初始化。
    2. 做容错判别,看是还是不是在此之前有被注册过,假诺注册过,则赶回-EEXIST;那样大家客商就足以拿走二个已存在的提醒音讯。
  2. 将其加盟链表,方便管理。
  3. 干活。
    对于我们清楚的话第3和4是最根本的。
    第3很简单,不深入分析了,看不懂的同志补补基础知识。
    第4块是最重要了。

obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) = backlight.o

struct led_classdev {
    const char        *name;                                                                                   //名字
    int             brightness;                                                                                     //亮度值,也能够用来表示开关特性
    int             max_brightness;                                                                           //允许的最大亮度值
    int             flags;                                                                                               //标志

struct led_classdev {
    const char        *name;                                                                                   //名字
    int             brightness;                                                                                     //亮度值,也得以用来表示开关性子
    int             max_brightness;                                                                           //允许的最大亮度值
    int             flags;                                                                                               //标志

down_read(&leds_list_lock);
 list_for_each_entry(led_cdev, &leds_list, node) {
  down_write(&led_cdev->trigger_lock);
  if (!led_cdev->trigger && led_cdev->default_trigger &&
       !strcmp(led_cdev->default_trigger, trigger->name))
   led_trigger_set(led_cdev, trigger);
  up_write(&led_cdev->trigger_lock);
 }
 up_read(&leds_list_lock);
前面假设大家有细致看作品的话,应该开掘了分析基础的贰个小才具,这就避难就易。很明朗我们由此避重逐轻后意识这一块代码最注重的也便是两句:
Linux内核LED模块分析,Linux设备模型之led子系统。if (!led_cdev->trigger && led_cdev->default_trigger &&
       !strcmp(led_cdev->default_trigger, trigger->name))
   led_trigger_set(led_cdev, trigger);
先是引出了一个新东西led_cdev。这么些事物我们会再讲,大家先延续往下看。这一块代码的野趣是:首先剖断led_cdev的触发器存不设有?假如存在,就一而再看她是否有设置私下认可触发器?借使有,则看他设置的私下认可触发器的名字和大家模块中触发器的名字是或不是同样,那样的论断够严刻吧?一来要是上一步的决断不成功就直接退出,

一连查看/driver/video/backlight/Kconfig

    /* Lower 16 bits reflect status */
#define LED_SUSPENDED        (1 << 0)
    /* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME    (1 << 16)

    /* Lower 16 bits reflect status */
#define LED_SUSPENDED        (1 << 0)
    /* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME    (1 << 16)

二来又是下一步的功底,防止空指针的援引等。倘使成功的话就调用led_trigger_set。
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
{
 unsigned long flags;

config BACKLIGHT_CLASS_DEVICE

    /* Set LED brightness level */
    /* Must not sleep, use a workqueue if needed */
    void        (*brightness_set)(struct led_classdev *led_cdev,                 //宗旨回调函数,当设置/sys/class/leds/下的led接口里的brightness属性文件时,会回调该函数
                      enum led_brightness brightness);
    /* Get LED brightness level */
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);         //主旨回调函数,当得到led当前值时会调用

    /* Set LED brightness level */
    /* Must not sleep, use a workqueue if needed */
    void        (*brightness_set)(struct led_classdev *led_cdev,                 //核心回调函数,当设置/sys/class/leds/下的led接口里的brightness属性文件时,会回调该函数
                      enum led_brightness brightness);
    /* Get LED brightness level */
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);         //宗旨回调函数,当获得led当前值时会调用

 /* Remove any existing trigger */
 if (led_cdev->trigger) {
  write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
  list_del(&led_cdev->trig_list);
  write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
   flags);
  if (led_cdev->trigger->deactivate)
   led_cdev->trigger->deactivate(led_cdev);
  led_cdev->trigger = NULL;
  led_brightness_set(led_cdev, LED_OFF);
 }
 if (trigger) {
  write_lock_irqsave(&trigger->leddev_list_lock, flags);
  list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
  write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
  led_cdev->trigger = trigger;
  if (trigger->activate)
   trigger->activate(led_cdev);
 }
}
探问,是否有种似前一般的感到?
哈哈。

        tristate "Lowlevel Backlight controls"

    /* 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 cant
     * match the values specified exactly. */
    int        (*blink_set)(struct led_classdev *led_cdev,
                     unsigned long *delay_on,
                     unsigned long *delay_off);

    /* 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);

  1. 初始化。
  2. 容错。
  3. 干活。
    无人不晓,第一步很轻易,只假诺道上混的弟兄要看懂都不是主题素材。
    接下去我们分析一下容错这一步。
    if (led_cdev->trigger) {
      write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
      list_del(&led_cdev->trig_list);
      write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
       flags);
      if (led_cdev->trigger->deactivate)
       led_cdev->trigger->deactivate(led_cdev);
      led_cdev->trigger = NULL;
      led_brightness_set(led_cdev, LED_OFF);
     }
    倘诺有触发器大家就先将其从led_cdev的trig_list中删掉,然后判别deactivate是还是不是留存,若是存在则调用

       depends on BACKLIGHT_LCD_SUPPORT

    struct device        *dev;                                                                                  //嵌入的正统配备模型
    struct list_head     node;            /* LED Device list */                                //下边提到的大局led设备的挂接点
    const char        *default_trigger;    /* Trigger to use */

    struct device        *dev;                                                                                  //嵌入的正式配备模型
    struct list_head     node;            /* LED Device list */                                //上边提到的全局led设备的挂接点
    const char        *default_trigger;    /* Trigger to use */

deactivate()。然后再调用led_brightness_set()。
void led_brightness_set(struct led_classdev *led_cdev,
   enum led_brightness brightness)
{
 led_stop_software_blink(led_cdev);
 led_cdev->brightness_set(led_cdev, brightness);
} static void led_stop_software_blink(struct led_classdev *led_cdev)
{
 /* deactivate previous settings */
 del_timer_sync(&led_cdev->blink_timer);
 led_cdev->blink_delay_on = 0;
 led_cdev->blink_delay_off = 0;
}
都是用led_cdev相关的,大家先忽略掉,继续回来在此之前的地点分析。
if (trigger) {
  write_lock_irqsave(&trigger->leddev_list_lock, flags);
  list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
  write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
  led_cdev->trigger = trigger;
  if (trigger->activate)
   trigger->activate(led_cdev);
 }

       default m

#ifdef CONFIG_LEDS_TRIGGERS
    /* Protects the trigger data below */
    struct rw_semaphore     trigger_lock;    

#ifdef CONFIG_LEDS_TRIGGERS
    /* Protects the trigger data below */
    struct rw_semaphore     trigger_lock;    

图片 1

因而安顿内核make menuconfig时,须求选中这一项。

    struct led_trigger    *trigger;
    struct list_head     trig_list;
    void            *trigger_data;
#endif
};

    struct led_trigger    *trigger;
    struct list_head     trig_list;
    void            *trigger_data;
#endif
};

 

 

好了,上边说下伊始化函数:

上面看backlight背光的主干代码backlight.c

好了,上面说下初阶化函数:

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;                                                 //属性文件,sys下的接口,注重看一下
    return 0;
}图片 2

static int __init backlight_class_init(void)

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;                                                 //属性文件,sys下的接口,着重看一下
    return 0;
}

{

函数在/sys/class目录下发出了leds这些子目录,根据led模型注册的设施都会冒出在该目录下

       backlight_class = class_create(THIS_MODULE, "backlight"); //注册backlight类

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

       if (IS_ERR(backlight_class)) {

};

              printk(KERN_WARNING "Unable to create backlight class; errno = %ldn",

足见,属性函数的通用设置函数为led_brightness_store,获得函数位led_brightness_show,大同小异:

                            PTR_ERR(backlight_class));

static ssize_t led_brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);                    //获得led设备信息
    ssize_t ret = -EINVAL;
    char *after;
    unsigned long state = simple_strtoul(buf, &after, 10);
    size_t count = after - buf;

              return PTR_ERR(backlight_class);

    if (isspace(*after))
        count ;

       }

    if (count == size) {
        ret = count;

       backlight_class->dev_attrs = bl_device_attributes;  //增加类属性

        if (state == LED_OFF)
            led_trigger_remove(led_cdev);
        led_set_brightness(led_cdev, state);                                             
    }

       backlight_class->suspend = backlight_suspend;

    return ret;

       backlight_class->resume = backlight_resume;

}

       return 0;

跟进一下led_set_brightness函数:

}

static inline void led_set_brightness(struct led_classdev *led_cdev,
                    enum led_brightness value)
{
    if (value > led_cdev->max_brightness)
        value = led_cdev->max_brightness;
    led_cdev->brightness = value;
    if (!(led_cdev->flags & LED_SUSPENDED)) {
#ifdef CONFIG_HAS_EARLYSUSPEND
        if (queue_brightness_change(led_cdev, value) != 0)
#endif
            led_cdev->brightness_set(led_cdev, value);
    }
}

大家知晓backlight背光子系统的首要性正是靠这几个类属性,当大家设置背光值正是向类属性中某些成员写背光值,这几个类属性便是给顾客的一种接口,我们入眼看看

看得出最后确实是回调的 led_cdev->brightness_set来产生的该属性的设置。

#define __ATTR(_name,_mode,_show,_store) {

下边看一下给大家留的接口,注册函数:

       .attr = {.name = __stringify(_name), .mode = _mode },    

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
    led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
                      "%s", led_cdev->name);                                                                           //在leds目录下发出该设施的目录项
    if (IS_ERR(led_cdev->dev))
        return PTR_ERR(led_cdev->dev);

       .show     = _show,                            

#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
    /* add to the list of leds */
    down_write(&leds_list_lock);
    list_add_tail(&led_cdev->node, &leds_list);                                                           //把该设备步入全局链表
    up_write(&leds_list_lock);

       .store      = _store,                            

    if (!led_cdev->max_brightness)
        led_cdev->max_brightness = LED_FULL;                                                         //最大值即使没设置,则设置成255

}

    led_update_brightness(led_cdev);                              

static struct device_attribute bl_device_attributes[] = {

#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif

       __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),

    printk(KERN_DEBUG "Registered led device: %s ",
            led_cdev->name);

       __ATTR(brightness, 0644, backlight_show_brightness,

    return 0;
}
小结:基本上海高校概剖判了弹指间led子系统,代码相当少,也很轻易看,首要本人端的probe函数要安顿好io口的效果与利益,填充好led_cdev那一个东东,然后注册就足以了 ^.^~

                   backlight_store_brightness),

 

       __ATTR(actual_brightness, 0444, backlight_show_actual_brightness,

代码位于drivers/leds下,看一下Makefile 模型文件...

                   NULL),

       __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),

       __ATTR_NULL,

};

很理解,在backlight类中大家成立了bl_power,brightness,actural_brightness,max_brightness多少个分子,当中brightness是日前亮度,max_brightness是最大亮度。当顾客层通过cat也许echo命令就能够接触这一个分子。对于这个属性的读写函数,大家先看看读的函数backlight_show_max_brightness吧

static ssize_t backlight_show_max_brightness(struct device *dev,

              struct device_attribute *attr, char *buf)

{

       struct backlight_device *bd = to_backlight_device(dev);

       return sprintf(buf, "%dn", bd->props.max_brightness);  //输出最大亮度

}

这几个函数很简短,然而主假使引进了多少个backlight背光子系统的多少个主要的数据结构,大家好好学习下。

首先是backlight背光子系统的设备结构体backlight_device

struct backlight_device {

       struct backlight_properties props;  //背光属性

       struct mutex update_lock;

       struct mutex ops_lock;

       struct backlight_ops *ops;    //背光操作函数,类似于file_operations

       struct notifier_block fb_notif;

       struct device dev;  //内嵌设备

};

上边先看看背光属性结构体backlight_properties

struct backlight_properties {

       int brightness;  //当前背光值

       int max_brightness;  //最大背光值

       int power;

       int fb_blank;

       unsigned int state;

};

再看看背光操作函数结构体

struct backlight_ops {

       unsigned int options;

#define BL_CORE_SUSPENDRESUME       (1 << 0)

       int (*update_status)(struct backlight_device *);   //改动背光状态

       int (*get_brightness)(struct backlight_device *);  //获取背光值

       int (*check_fb)(struct fb_info *);

};

好了,大家后续看backlight类属性中写的函数,举个例子设置当前背光值函数backlight_store_brightness吧

static ssize_t backlight_store_brightness(struct device *dev,

              struct device_attribute *attr, const char *buf, size_t count)

{

       int rc;

       struct backlight_device *bd = to_backlight_device(dev);

       unsigned long brightness;

       rc = strict_strtoul(buf, 0, &brightness);

       if (rc)

              return rc;

       rc = -ENXIO;

       mutex_lock(&bd->ops_lock);

       if (bd->ops) {

              if (brightness > bd->props.max_brightness)

                     rc = -EINVAL;

              else {

                     pr_debug("backlight: set brightness to %lun",

                             brightness);

                     bd->props.brightness =brightness;  //传入背光值

                     backlight_update_status(bd);  //调用backlight_update_status设备背光值

                     rc = count;

              }

       }

       mutex_unlock(&bd->ops_lock);

       backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);

       return rc;

}

跟踪backlight_update_status

static inline void backlight_update_status(struct backlight_device *bd)

{

       mutex_lock(&bd->update_lock);

       if (bd->ops && bd->ops->update_status)

              bd->ops->update_status(bd); //调用背光操作函数中改换背光状态函数update_status

       mutex_unlock(&bd->update_lock);

}

对此那个backlight背光主旨层驱动backlight.c,剩下的正是以此pwm.c给我们提供了怎么着接口函数了。

struct backlight_device *backlight_device_register(const char *name,

              struct device *parent, void *devdata, struct backlight_ops *ops)

void backlight_device_unregister(struct backlight_device *bd)

EXPORT_SYMBOL(backlight_device_register);  //注册背光设备

EXPORT_SYMBOL(backlight_device_unregister); //注销背光设备

这几个接口异常粗略,就不细说了,那样大家的backlight子系统的中央层就介绍完了。

 

四.基于PWM&Backlight的蜂鸣器驱动

上面大家构成方面包车型客车PWM大旨层和Backlight背光子系统宗旨层,根据基于pwm的背光驱动/driver/video/backlight/pwm_bl.c来修改成基于Mini2440的蜂鸣器驱动。

先查看/driver/video/backlight/Makefile

obj-$(CONFIG_BACKLIGHT_PWM)   = pwm_bl.o

连续查看/driver/video/backlight/Kconfig

config BACKLIGHT_PWM

       tristate "Generic PWM based Backlight Driver"

       depends on BACKLIGHT_CLASS_DEVICE && HAVE_PWM

       help

         If you have a LCD backlight adjustable by PWM, say Y to enable

         this driver.

我们的HAVE_PWM和BACKLIGHT_CLASS_DEVICE分别是在头里讲pwm宗旨和backlight宗旨时早就编写翻译了,所以布置内核make menuconfig 时,必要再选中"Generic PWM based Backlight Driver"那项。

 

好了,大家先把大家的蜂鸣器移植进去吧,首先大家清楚蜂鸣器使用的是GPB0端口,该端口借使职业在TOU0形式,就足以因此配备电磁关照计时器的TCNT和TCMP来控制订时器的波浪而来。先展开mini2440的BSP文件mach-mini2440.c,如下增多

static struct platform_device s3c_backlight_device = {

       .name             = "pwm-backlight",           //设备名

       .dev        = {

              .parent    = &s3c_device_timer[0].dev,  //该装置依照pwm中的0号电磁照望计时器

              .platform_data = &s3c_backlight_data,

       },

       .id=0,     //对应的正是pwm0

};

增多平台数量

static struct platform_pwm_backlight_data s3c_backlight_data = {

       .pwm_id         = 0,   //对应的正是Timer0

       .max_brightness     = 壹仟,  //最大亮度

       .dft_brightness       = 10 ,      //当前亮度

       .pwm_period_ns    = 八千00,  //那就是前方说的T0,即出口石英钟周期

       .init         = s3c_bl_init,  //端口起首化

};

留心到阳台数量中定义了init函数,由于在蜂鸣器的伊始化时,须求对GPB0设置为TOUT0情势,所以代码如下编写

static int s3c_bl_init(struct device *dev)

{

       s3c2410_gpio_pullup(S3C2410_GPB(0),0);   // GPB0不上拉

       s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPB0_TOUT0); // GPB0设置为TOUT0

       return 0;

}

然后把这些s3c_backlight_device加入到mini2440_devices数组

static struct platform_device *mini2440_devices[] __initdata = {

       ……

       &s3c_device_timer[0],

       &s3c_backlight_device, //添加

};

最后增加头文件

#include <linux/pwm_backlight.h>

那样布署完后,实行make zImage生成zImage内核镜像。

 

好了,上面我们深入分析下基于pwm的背光驱动/driver/video/backlight/pwm_bl.c

static struct platform_driver pwm_backlight_driver = {

       .driver            = {

              .name      = "pwm-backlight", //驱动名

              .owner    = THIS_MODULE,

       },

       .probe            = pwm_backlight_probe, //探测函数

       .remove          = pwm_backlight_remove,

       .suspend  = pwm_backlight_suspend,

       .resume          = pwm_backlight_resume,

};

static int __init pwm_backlight_init(void)

{

       return platform_driver_register(&pwm_backlight_driver);

}

留意上边的pwm_backlight_driver中的驱动名"pwm-backlight"和大家刚刚移植时加上的装置名"pwm-backlight"是同等的,那样设备和驱动就能够合营成功。上面看探测函数

static int pwm_backlight_probe(struct platform_device *pdev)

{

       struct platform_pwm_backlight_data *data = pdev->dev.platform_data;

       struct backlight_device *bl;

       struct pwm_bl_data *pb;  //本驱动的私有结构体

       int ret;

       if (!data) {

              dev_err(&pdev->dev, "failed to find platform datan");

              return -EINVAL;

       }

       if (data->init) {  //起首化端口,那一个端口函数在BSP中定义

              ret = data->init(&pdev->dev);

              if (ret < 0)

                     return ret;

       }

       pb = kzalloc(sizeof(*pb), GFP_KERNEL); //分配pwm_bl_data空间

       if (!pb) {

              dev_err(&pdev->dev, "no memory for staten");

              ret = -ENOMEM;

              goto err_alloc;

       }

       pb->period = data->pwm_period_ns;   //获取周期

       pb->notify = data->notify;

       pb->pwm = pwm_request(data->pwm_id, "backlight"); //注册pwm设备

       if (IS_ERR(pb->pwm)) {

              dev_err(&pdev->dev, "unable to request PWM for backlightn");

              ret = PTR_ERR(pb->pwm);

              goto err_pwm;

       } else

              dev_dbg(&pdev->dev, "got pwm for backlightn");

       bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev,

                     pb, &pwm_backlight_ops); //注册backlight设备

       if (IS_ERR(bl)) {

              dev_err(&pdev->dev, "failed to register backlightn");

              ret = PTR_ERR(bl);

              goto err_bl;

       }

       bl->props.max_brightness = data->max_brightness;

       bl->props.brightness = data->dft_brightness;

       backlight_update_status(bl);  //先点亮背光

       platform_set_drvdata(pdev, bl); //设置bl为私有数据

       return 0;

err_bl:

       pwm_free(pb->pwm);

err_pwm:

       kfree(pb);

err_alloc:

       if (data->exit)

              data->exit(&pdev->dev);

       return ret;

}

对此这几个驱动,大家重要关注的是注册backlight设备时传出的参数pwm_backlight_ops,因为大家后面深入分析backlight背光子系统时说过,背光设备结构体中有个操作背光的函数会集,在我们的pwm_bl.c中,就必要定义那些操作背光的函数集结,也等于pwm_backlight_ops

static struct backlight_ops pwm_backlight_ops = {

       .update_status = pwm_backlight_update_status, //更新背光亮度

       .get_brightness       = pwm_backlight_get_brightness, //获取背光亮度

};

赢得背光亮度函数pwm_backlight_get_brightness很粗大略,追踪得到

static int pwm_backlight_get_brightness(struct backlight_device *bl)

{

       return bl->props.brightness;

}

咱俩任重(英文名:rèn zhòng)而道远看更新背光亮度函数pwm_backlight_update_status

static int pwm_backlight_update_status(struct backlight_device *bl)

{

       struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);

       int brightness = bl->props.brightness;

       int max = bl->props.max_brightness;

       if (bl->props.power != FB_BLANK_UNBLANK)

              brightness = 0;

       if (bl->props.fb_blank != FB_BLANK_UNBLANK)

              brightness = 0;

       if (pb->notify)

              brightness = pb->notify(brightness);

       if (brightness == 0) {  //背光值为0,关闭背光

              pwm_config(pb->pwm, 0, pb->period);

              pwm_disable(pb->pwm);

       } else {   //调用pwm中的API设置背光

              pwm_config(pb->pwm, brightness * pb->period / max, pb->period);

              pwm_enable(pb->pwm);

       }

       return 0;

}

好了,那样我们的pwm_bl.c也分析完了。在选取backlight子系统的时候,大家只要求在probe函数中登记pwm和backlight设备,然后定义背光操作函数群集就可以。

 

五.使得测量试验

尝试情况:内核linux2.6.32.2,arm-linux-gcc交叉编写翻译器,mini2440开拓板

上面大家进行对地点的驱动举行测验,按照地点的步骤操作,将上文已经编写翻译好的zImage烧入开辟板,通过一流终端调节,能调整蜂鸣器的发生的动静频率。

 

 图片 3

本文由星彩网app下载发布于星彩网app下载,转载请注明出处:Linux内核LED模块分析,Linux设备模型之led子系统

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。