灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:3268回复:0

Android模拟器学framework和driver

楼主#
更多 发布于:2012-09-06 13:44


前面介绍了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 = %ldn",
                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 = %ldn",
    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, "%dn", 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 %lun", 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, "%dn", 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 %lun", 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 datan");
        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 staten");
        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 backlightn");
        ret = PTR_ERR(pb->pwm);
        goto err_pwm;
    } else
        dev_dbg(;pdev->dev, "got pwm for backlightn");
*/
    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 backlightn");
        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 datan");
  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 staten");
  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 backlightn");
  ret = PTR_ERR(pb->pwm);
  goto err_pwm;
} else
  dev_dbg(;pdev->dev, "got pwm for backlightn");
*/
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 backlightn");
  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.atcpu.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=%sn", 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=%sn", 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, "%dn", 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 %lun",
                 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, "%dn", 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 %lun",
     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 %lun",
                 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 %lun",
     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的专栏



喜欢0 评分0
游客

返回顶部