Android模擬器學framework和driver之battery & backlight—–5. backlight in linux

前面介紹瞭battery的相關的東西,現在我們來介紹下backlight模塊,背光主要是用來調節顯示屏亮度的,一般背光都是用PWM控制的,調節占空比達到改變有效電壓值來調節光的強弱。

背光的移植在linux中雖然不是那麼難,但是背光這個組件對我們嵌入式設備的續航能力有很大的影響,一般背光上面加的電壓會有20多的電壓,所以這部分會很耗電的,相當於是開瞭個大燈泡。

現在我們先來看下android goldfish中的背光代碼,哈哈,沒找到吧,沒有,我們打開模擬器,看sysfs中,也是沒有具體的背光的文件的,所以這裡我們得自己實現,自己寫代碼練習練習,畢竟這部分不是非常的難,參考drivers/video/backlight/下的pwm_bl.c文件,基本可以仿照,我們要做的事情很簡單,創建背光相關的文件系統即可,不需要去控制硬件做什麼動作,因為我們本來就沒有硬件。

首先看下video中的makefile,如果backlight/沒有選中就選中它,不然我們的模塊不會編譯進去。然後再看下backlight/下的Makefile

[cpp] obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o 
obj-$(CONFIG_LCD_CORGI)            += corgi_lcd.o 
obj-$(CONFIG_LCD_LTV350QV)         += ltv350qv.o 
obj-$(CONFIG_LCD_ILI9320)          += ili9320.o 
obj-$(CONFIG_LCD_PLATFORM)         += platform_lcd.o 
obj-$(CONFIG_LCD_VGG2432A4)        += vgg2432a4.o 
obj-$(CONFIG_LCD_TDO24M)           += tdo24m.o 
obj-$(CONFIG_LCD_TOSA)             += tosa_lcd.o 
 
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o 
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o 
obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o 
obj-$(CONFIG_BACKLIGHT_HP680)   += hp680_bl.o 
obj-$(CONFIG_BACKLIGHT_LOCOMO)  += locomolcd.o 
obj-$(CONFIG_BACKLIGHT_OMAP1)   += omap1_bl.o 
obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o 
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o 
obj-$(CONFIG_BACKLIGHT_PWM)     += pwm_bl.o 
obj-$(CONFIG_BACKLIGHT_DA903X)  += da903x_bl.o 
obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o 
obj-$(CONFIG_BACKLIGHT_TOSA)    += tosa_bl.o 
obj-$(CONFIG_BACKLIGHT_SAHARA)  += kb3886_bl.o 
obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
obj-$(CONFIG_LCD_CORGI)            += corgi_lcd.o
obj-$(CONFIG_LCD_LTV350QV)         += ltv350qv.o
obj-$(CONFIG_LCD_ILI9320)          += ili9320.o
obj-$(CONFIG_LCD_PLATFORM)         += platform_lcd.o
obj-$(CONFIG_LCD_VGG2432A4)        += vgg2432a4.o
obj-$(CONFIG_LCD_TDO24M)           += tdo24m.o
obj-$(CONFIG_LCD_TOSA)             += tosa_lcd.o

obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o
obj-$(CONFIG_BACKLIGHT_HP680)   += hp680_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO)  += locomolcd.o
obj-$(CONFIG_BACKLIGHT_OMAP1)   += omap1_bl.o
obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_PWM)     += pwm_bl.o
obj-$(CONFIG_BACKLIGHT_DA903X)  += da903x_bl.o
obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
obj-$(CONFIG_BACKLIGHT_TOSA)    += tosa_bl.o
obj-$(CONFIG_BACKLIGHT_SAHARA)  += kb3886_bl.o

這裡沒有一個文件被編譯進去的,我們要把backlight.c先編譯進去,直接這樣改,我比較懶 呵呵呵,

[cpp] obj-y += backlight.o 
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o 
obj-y += backlight.o
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
然後重新編譯下會生成backlight.o文件,並且在sysfs中會生成我們的backlight class
  

 

我們先來分析下backlight.c中的代碼是如何實現的。

養成好習慣,看見代碼多不用怕,首先看init函數:

[cpp] static int __init backlight_class_init(void) 

    backlight_class = class_create(THIS_MODULE, "backlight"); 
    if (IS_ERR(backlight_class)) { 
        printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n", 
                PTR_ERR(backlight_class)); 
        return PTR_ERR(backlight_class); 
    } 
 
    backlight_class->dev_attrs = bl_device_attributes; 
    backlight_class->suspend = backlight_suspend; 
    backlight_class->resume = backlight_resume; 
    return 0; 

 
/*
 * if this is compiled into the kernel, we need to ensure that the
 * class is registered before users of the class try to register lcd's
 */ 
postcore_initcall(backlight_class_init); 
static int __init backlight_class_init(void)
{
 backlight_class = class_create(THIS_MODULE, "backlight");
 if (IS_ERR(backlight_class)) {
  printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
    PTR_ERR(backlight_class));
  return PTR_ERR(backlight_class);
 }

 backlight_class->dev_attrs = bl_device_attributes;
 backlight_class->suspend = backlight_suspend;
 backlight_class->resume = backlight_resume;
 return 0;
}

/*
 * if this is compiled into the kernel, we need to ensure that the
 * class is registered before users of the class try to register lcd's
 */
postcore_initcall(backlight_class_init);
很簡單,這裡隻是用瞭class_create函數在sys/class下創建瞭backlight文件夾,然後是
backlight_class->dev_attrs = bl_device_attributes;
在backlight class中創建瞭一系列的文件系統,

[cpp] <pre name="code" class="cpp">static ssize_t backlight_show_power(struct device *dev, 
        struct device_attribute *attr,char *buf) 

    struct backlight_device *bd = to_backlight_device(dev); 
 
    return sprintf(buf, "%d\n", bd->props.power); 

 
static ssize_t backlight_store_power(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 power; 
 
    rc = strict_strtoul(buf, 0, &power); 
    if (rc) 
        return rc; 
 
    rc = -ENXIO; 
    mutex_lock(&bd->ops_lock); 
    if (bd->ops) { 
        pr_debug("backlight: set power to %lu\n", power); 
        if (bd->props.power != power) { 
            bd->props.power = power; 
            backlight_update_status(bd); 
        } 
        rc = count; 
    } 
    mutex_unlock(&bd->ops_lock); 
 
    return rc; 

<pre name="code" class="cpp">static ssize_t backlight_show_power(struct device *dev,
  struct device_attribute *attr,char *buf)
{
 struct backlight_device *bd = to_backlight_device(dev);

 return sprintf(buf, "%d\n", bd->props.power);
}

static ssize_t backlight_store_power(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 power;

 rc = strict_strtoul(buf, 0, &power);
 if (rc)
  return rc;

 rc = -ENXIO;
 mutex_lock(&bd->ops_lock);
 if (bd->ops) {
  pr_debug("backlight: set power to %lu\n", power);
  if (bd->props.power != power) {
   bd->props.power = power;
   backlight_update_status(bd);
  }
  rc = count;
 }
 mutex_unlock(&bd->ops_lock);

 return rc;
}
所以我們的驅動隻要填充好具體的結構體,初始化好文件系統就夠瞭,在sysfs中生成可以讓user space調用的接口,接下來的事情就交給上層開發人員去做。

ok,我們來看下我們自己寫的驅動,在backlight文件夾下新建一個文件叫  android-backlight.c,我是參照pwm_bl.c來寫的,具體先來看下代碼,init函數

 

[cpp] static int __init android_backlight_init(void) 

    return platform_driver_register(&android_backlight_driver); 

 
static void __exit android_backlight_exit(void) 

    platform_driver_unregister(&android_backlight_driver); 

 
module_init(android_backlight_init); 
module_exit(android_backlight_exit); 
static int __init android_backlight_init(void)
{
 return platform_driver_register(&android_backlight_driver);
}

static void __exit android_backlight_exit(void)
{
 platform_driver_unregister(&android_backlight_driver);
}

module_init(android_backlight_init);
module_exit(android_backlight_exit);
使用platform_driver_register註冊平臺驅動,看下傳入的參數:

[cpp] static struct platform_driver android_backlight_driver = { 
    .driver ={ 
        .name = "android-backlight", 
        .owner = THIS_MODULE, 
    }, 
    .probe  =   android_backlight_probe, 
//  .remove =   ……..  
//  .suspend  
//  .resume  
 
 
}; 
static struct platform_driver android_backlight_driver = {
 .driver ={
  .name = "android-backlight",
  .owner = THIS_MODULE,
 },
 .probe = android_backlight_probe,
// .remove = ……..
// .suspend
// .resume

};
這裡我偷懶沒寫remove suspend和resume'回調函數,在移植具體驅動的時候我們都應該寫上,特別是suspend和resume函數,來看下我們paltform驅動的device_register是在哪做的,在arch/arm/mach-goldfish/board-goldfish.c

[cpp] struct platform_device android_backlight_device = { 
    .name = "android-backlight", 
    .id = 0, 
}; 
 
static struct platform_pwm_backlight_data android_backlight_data = { 
    .pwm_id = 0, 
    .max_brightness = 255, 
    .dft_brightness = 128, 
//  .pwm_period_ns = …;  
}; 
struct platform_device android_backlight_device = {
 .name = "android-backlight",
 .id = 0,
};

static struct platform_pwm_backlight_data android_backlight_data = {
 .pwm_id = 0,
 .max_brightness = 255,
 .dft_brightness = 128,
// .pwm_period_ns = …;
};

在init中進行註冊:

[cpp] static void __init goldfish_init(void) 

    platform_device_register(&goldfish_pdev_bus_device); 
    platform_device_register(&android_light_device); 
    platform_device_register(&android_switch_device); 
    platform_device_register(&vh_device); 
    platform_device_register(&android_temperature_device); 
    <span style="color:#ff0000;">android_register_device(&android_backlight_device, &android_backlight_data);</span> 

static void __init goldfish_init(void)
{
 platform_device_register(&goldfish_pdev_bus_device);
 platform_device_register(&android_light_device);
 platform_device_register(&android_switch_device);
 platform_device_register(&vh_device);
 platform_device_register(&android_temperature_device);
 <span style="color:#ff0000;">android_register_device(&android_backlight_device, &android_backlight_data);</span>
}
這邊android_backlight_data結構體主要是做一個背光的初始化。

接下來我們看一下probe函數,

[cpp] static int android_backlight_probe(struct platform_device *pdev) 

    //pass the struct from board-goldfish.c —-> init platform data  
    struct platform_pwm_backlight_data *data=pdev->dev.platform_data; 
    //local private struct  
    struct android_pwm_data *pd; 
    //backlight properties struct—-> defined in include/linux/backlight.h  
 
    struct backlight_properties props; 
    struct backlight_device *bl;    //struct infomation defined in include/linux/backlight.h  
 
    int ret; 
 
    if (!data) { 
        dev_err(&pdev->dev, "failed to find platform data\n"); 
        return -EINVAL; 
    } 
    //—-for here we haven't set init pointer function…  
    if(data->init) 
    { 
        ret=data->init(&pdev->dev); 
        if(ret<0) 
            return ret; 
    } 
     
    pd = kzalloc(sizeof(*pd),GFP_KERNEL); 
    if(!pd) 
    { 
        dev_err(&pdev->dev, "no memory for state\n"); 
        ret = -ENOMEM; 
        goto err_alloc; 
    } 
 
//  pd->period = data->pwm_period_ns;  
    pd->notify = data->notify; 
    pd->dev = &pdev->dev; 
 
/*  pd->pwm = pwm_request(data->pwm_id, "backlight");
    if (IS_ERR(pb->pwm)) {
        dev_err(&pdev->dev, "unable to request PWM for backlight\n");
        ret = PTR_ERR(pb->pwm);
        goto err_pwm;
    } else
        dev_dbg(&pdev->dev, "got pwm for backlight\n");
*/ 
    memset(&props,0,sizeof(struct backlight_properties)); 
    bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pd,&android_backlight_ops); 
    if (IS_ERR(bl)) { 
        dev_err(&pdev->dev, "failed to register backlight\n"); 
        ret = PTR_ERR(bl); 
//      goto err_bl;  
    } 
    bl->props.max_brightness = data->max_brightness; 
 
    bl->props.brightness=data->dft_brightness; 
 
    platform_set_drvdata(pdev,bl); 
 
//err_bl:  
//  pwm_free(pd->pwm);  
//err_pwm:  
//  kfree(pb);  
err_alloc: 
    if (data->exit) 
        data->exit(&pdev->dev); 
    return ret; 

static int android_backlight_probe(struct platform_device *pdev)
{
 //pass the struct from board-goldfish.c —-> init platform data
 struct platform_pwm_backlight_data *data=pdev->dev.platform_data;
 //local private struct
 struct android_pwm_data *pd;
 //backlight properties struct—-> defined in include/linux/backlight.h

 struct backlight_properties props;
 struct backlight_device *bl; //struct infomation defined in include/linux/backlight.h

 int ret;

 if (!data) {
  dev_err(&pdev->dev, "failed to find platform data\n");
  return -EINVAL;
 }
 //—-for here we haven't set init pointer function…
 if(data->init)
 {
  ret=data->init(&pdev->dev);
  if(ret<0)
   return ret;
 }
 
 pd = kzalloc(sizeof(*pd),GFP_KERNEL);
 if(!pd)
 {
  dev_err(&pdev->dev, "no memory for state\n");
  ret = -ENOMEM;
  goto err_alloc;
 }

// pd->period = data->pwm_period_ns;
 pd->notify = data->notify;
 pd->dev = &pdev->dev;

/* pd->pwm = pwm_request(data->pwm_id, "backlight");
 if (IS_ERR(pb->pwm)) {
  dev_err(&pdev->dev, "unable to request PWM for backlight\n");
  ret = PTR_ERR(pb->pwm);
  goto err_pwm;
 } else
  dev_dbg(&pdev->dev, "got pwm for backlight\n");
*/
 memset(&props,0,sizeof(struct backlight_properties));
 bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pd,&android_backlight_ops);
 if (IS_ERR(bl)) {
  dev_err(&pdev->dev, "failed to register backlight\n");
  ret = PTR_ERR(bl);
//  goto err_bl;
 }
 bl->props.max_brightness = data->max_brightness;

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

 platform_set_drvdata(pdev,bl);

//err_bl:
// pwm_free(pd->pwm);
//err_pwm:
// kfree(pb);
err_alloc:
 if (data->exit)
  data->exit(&pdev->dev);
 return ret;
}
首先檢查我們得到的platform_data結構體中有沒有init回調函數,有的話執行,沒有的話跳過。

[cpp] if(data->init) 

    ret=data->init(&pdev->dev); 
    if(ret<0) 
        return ret; 

 if(data->init)
 {
  ret=data->init(&pdev->dev);
  if(ret<0)
   return ret;
 }
這邊比較重要的是backlight_device_register函數www.aiwalls.com

[cpp] struct backlight_device *backlight_device_register(const char *name, 
        struct device *parent, void *devdata, struct backlight_ops *ops) 

    struct backlight_device *new_bd; 
    int rc; 
 
    pr_debug("backlight_device_register: name=%s\n", name); 
 
    new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); 
    if (!new_bd) 
        return ERR_PTR(-ENOMEM); 
 
    mutex_init(&new_bd->update_lock); 
    mutex_init(&new_bd->ops_lock); 
 
    new_bd->dev.class = backlight_class; 
    new_bd->dev.parent = parent; 
    new_bd->dev.release = bl_device_release; 
    dev_set_name(&new_bd->dev, name); 
    dev_set_drvdata(&new_bd->dev, devdata); 
 
    rc = device_register(&new_bd->dev); 
    if (rc) { 
        kfree(new_bd); 
        return ERR_PTR(rc); 
    } 
 
    rc = backlight_register_fb(new_bd); 
    if (rc) { 
        device_unregister(&new_bd->dev); 
        return ERR_PTR(rc); 
    } 
 
    new_bd->ops = ops; 
 
#ifdef CONFIG_PMAC_BACKLIGHT  
    mutex_lock(&pmac_backlight_mutex); 
    if (!pmac_backlight) 
        pmac_backlight = new_bd; 
    mutex_unlock(&pmac_backlight_mutex); 
#endif  
 
    return new_bd; 

EXPORT_SYMBOL(backlight_device_register); 
struct backlight_device *backlight_device_register(const char *name,
  struct device *parent, void *devdata, struct backlight_ops *ops)
{
 struct backlight_device *new_bd;
 int rc;

 pr_debug("backlight_device_register: name=%s\n", name);

 new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
 if (!new_bd)
  return ERR_PTR(-ENOMEM);

 mutex_init(&new_bd->update_lock);
 mutex_init(&new_bd->ops_lock);

 new_bd->dev.class = backlight_class;
 new_bd->dev.parent = parent;
 new_bd->dev.release = bl_device_release;
 dev_set_name(&new_bd->dev, name);
 dev_set_drvdata(&new_bd->dev, devdata);

 rc = device_register(&new_bd->dev);
 if (rc) {
  kfree(new_bd);
  return ERR_PTR(rc);
 }

 rc = backlight_register_fb(new_bd);
 if (rc) {
  device_unregister(&new_bd->dev);
  return ERR_PTR(rc);
 }

 new_bd->ops = ops;

#ifdef CONFIG_PMAC_BACKLIGHT
 mutex_lock(&pmac_backlight_mutex);
 if (!pmac_backlight)
  pmac_backlight = new_bd;
 mutex_unlock(&pmac_backlight_mutex);
#endif

 return new_bd;
}
EXPORT_SYMBOL(backlight_device_register);

這裡做的最主要的事情就是對一些結構體的初始化,然後調用device_register把我們具體的device掛到我們的backlight class下,具體的是如何實現的我這裡不多說,我這裡隻做一些簡單的介紹。這裡大傢可以看到最重要的是backlight_device_register函數的最後一個參數,這裡提供瞭我們可以自己定義的幾個回調函數,

[cpp] struct backlight_ops { 
    unsigned int options; 
 
#define BL_CORE_SUSPENDRESUME   (1 << 0)  
 
    /* Notify the backlight driver some property has changed */ 
    int (*update_status)(struct backlight_device *); 
    /* Return the current backlight brightness (accounting for power,
       fb_blank etc.) */ 
    int (*get_brightness)(struct backlight_device *); 
    /* Check if given framebuffer device is the one bound to this backlight;
       return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */ 
    int (*check_fb)(struct fb_info *); 
}; 
struct backlight_ops {
 unsigned int options;

#define BL_CORE_SUSPENDRESUME (1 << 0)

 /* Notify the backlight driver some property has changed */
 int (*update_status)(struct backlight_device *);
 /* Return the current backlight brightness (accounting for power,
    fb_blank etc.) */
 int (*get_brightness)(struct backlight_device *);
 /* Check if given framebuffer device is the one bound to this backlight;
    return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
 int (*check_fb)(struct fb_info *);
};
我們這邊定義瞭2個回調函數掛上去:

[cpp] static const struct backlight_ops android_backlight_ops = { 
    .update_status  = android_backlight_update_status, 
    .get_brightness = android_backlight_get_brightness, 
//        .check_fb…    
}; 
static const struct backlight_ops android_backlight_ops = {
 .update_status  = android_backlight_update_status,
 .get_brightness = android_backlight_get_brightness,
//        .check_fb… 
};
然後我們去實現這2個函數,就基本完成瞭我們的驅動瞭,看函數名字就知道這2個函數的作用,一個是用來更新我們的背光亮度,還有一個是用來得到我們的光強。

[cpp] static int android_backlight_get_brightness(struct backlight_device *bl) 

    printk(KERN_INFO "[android]—get brightness…\n"); 
    return bl->props.brightness; 

static int android_backlight_get_brightness(struct backlight_device *bl)
{
 printk(KERN_INFO "[android]—get brightness…\n");
 return bl->props.brightness;
}

這個函數比較簡單,就是返回backlight_device->props->brightness,我們來看下最終我們的brightness是哪裡寫進去的。這裡比較繞,我們還是結合update函數一起分析:

[cpp] static int android_backlight_update_status(struct backlight_device *bl) 

    struct android_pwm_data *pd = 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;
*/ 
    printk(KERN_INFO "update brightness…\n"); 
    if (pd->notify) 
        brightness = pd->notify(pd->dev, brightness); 
    //+++add  
    global_brightness = brightness; 
//  complete(&priv_event);  
    printk(KERN_INFO "complete event….\n"); 
    return 0; 

static int android_backlight_update_status(struct backlight_device *bl)
{
 struct android_pwm_data *pd = 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;
*/
 printk(KERN_INFO "update brightness…\n");
 if (pd->notify)
  brightness = pd->notify(pd->dev, brightness);
 //+++add
 global_brightness = brightness;
// complete(&priv_event);
 printk(KERN_INFO "complete event….\n");
 return 0;
}

我們姑且這麼理解,我們有一個結構體,brightness_properity用來存放backlight的一些屬性信息,比如說brightness,當我們要get_brightness的時候就是去返回這個brightness,當我們要調節光強的時候就是給這個結構體中的成員變量賦值。

首先我們要瞭解android中用戶層是怎麼做的,因為我們linux driver最終的目標就是服務用戶層,所以我們要瞭解。

其實android HAL層就是open backlight中的brightness這個節點,然後進行讀寫來設置背光的亮度的,好吧,先來看下讀寫這個節點會呼叫的回調函數

在backlight.c中實現:

[cpp] static ssize_t backlight_show_brightness(struct device *dev, 
        struct device_attribute *attr, char *buf) 

    struct backlight_device *bd = to_backlight_device(dev); 
 
    return sprintf(buf, "%d\n", bd->props.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 %lu\n", 
                 brightness); 
            bd->props.brightness = brightness; 
            backlight_update_status(bd); 
            rc = count; 
        } 
    } 
    mutex_unlock(&bd->ops_lock); 
 
    return rc; 

static ssize_t backlight_show_brightness(struct device *dev,
  struct device_attribute *attr, char *buf)
{
 struct backlight_device *bd = to_backlight_device(dev);

 return sprintf(buf, "%d\n", bd->props.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 %lu\n",
     brightness);
   bd->props.brightness = brightness;
   backlight_update_status(bd);
   rc = count;
  }
 }
 mutex_unlock(&bd->ops_lock);

 return rc;
}

當我們向brightness這個文件節點中寫入我們要設置的背光亮度的時候會調用store這個回調函數,我們來看下主要做瞭哪些事情,跟我們在driver層自己寫的update函數到底有什麼關系呢?

前面都是一大堆不用看的代碼,這裡最重要的看這個

[cpp]     if (bd->ops) { 
        if (brightness > bd->props.max_brightness) 
            rc = -EINVAL; 
        else { 
            pr_debug("backlight: set brightness to %lu\n", 
                 brightness); 
<span style="color:#ff0000;">         bd->props.brightness = brightness; 
            backlight_update_status(bd);</span> 
            rc = count; 
        } 
    } 
 if (bd->ops) {
  if (brightness > bd->props.max_brightness)
   rc = -EINVAL;
  else {
   pr_debug("backlight: set brightness to %lu\n",
     brightness);
<span style="color:#ff0000;">   bd->props.brightness = brightness;
   backlight_update_status(bd);</span>
   rc = count;
  }
 }
首先是把brightness寫進我們的背光屬性結構體中,這樣就更新瞭我們數據結構中的背光亮度在值,但是這樣做是不夠的,因為我們最終要控制的是硬件,所以看下之後我們調用瞭backlight_update_status函數,ok,看下這個函數的定義:

/include/linux/backlight.h

[cpp] 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); 
    mutex_unlock(&bd->update_lock); 

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);
 mutex_unlock(&bd->update_lock);
}
看下這個內聯函數,看到ops就知道瞭吧,這邊調用瞭bd->ops->update_status這裡就調用到瞭我們自己寫的update_status回調函數:

[cpp] static const struct backlight_ops android_backlight_ops = { 
    .update_status  = android_backlight_update_status, 
    .get_brightness = android_backlight_get_brightness, 
//        .check_fb…    
}; 
static const struct backlight_ops android_backlight_ops = {
 .update_status  = android_backlight_update_status,
 .get_brightness = android_backlight_get_brightness,
//        .check_fb… 
};
[cpp] static int android_backlight_update_status(struct backlight_device *bl) 

    struct android_pwm_data *pd = 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;
*/ 
    printk(KERN_INFO "update brightness…\n"); 
    if (pd->notify) 
        brightness = pd->notify(pd->dev, brightness); 
    //+++add  
    global_brightness = brightness; 
//  complete(&priv_event);  
    printk(KERN_INFO "complete event….\n"); 
    return 0; 

static int android_backlight_update_status(struct backlight_device *bl)
{
 struct android_pwm_data *pd = 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;
*/
 printk(KERN_INFO "update brightness…\n");
 if (pd->notify)
  brightness = pd->notify(pd->dev, brightness);
 //+++add
 global_brightness = brightness;
// complete(&priv_event);
 printk(KERN_INFO "complete event….\n");
 return 0;
}
這裡咱也沒做什麼,因為android模擬器沒有真正的背光的設備,我們打印瞭信息,還有就是一個notify回調函數,這裡我們也沒有實現,這裡我猜想就是這邊背光如果涉及到別的deivce的行為的話,這個notify函數可以通知到別的設備。

ok,這邊就介紹結束瞭,我們來啟動我們的android模擬器來看下sysfs中backlight下我們自己的節點。

  

 

大傢可以看到我們自己的device的文件系統,我們cat 出來的brightness就是我們在board-goldfish.c中設置的初始值。

=====================================================

OK,這部分就介紹到這,下面一篇會介紹到我們HAL層中是如何封裝我們driver中的接口的。

 

摘自  zhangjie201412的專欄
 

發佈留言