您的位置:首页 > 移动开发 > Android开发

展讯7731C_M Android6.0 充电指示灯实现(二)------开机充电实现

2016-08-13 15:34 316 查看
   上一节已经了解了展讯7731C_M Android6.0 充电指示灯实的关机部分,这一节主要介绍开机部分,也就是kernel部分。

kernel 部分主要功能是提供对硬件led控制接口,如设置亮度,获取亮度,设置颜色等。

一、整个guide-led 驱动调用逻辑序列图下



二、驱动框架的具体实现

1.定义平台驱动结构,注册platform 驱动
文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c
static int __init sprd_leds_bltc_rgb_init(void)
{
PRINT_INFO("Locate in sprd_leds_bltc_rgb_init!\n");

return platform_driver_register(&sprd_leds_bltc_rgb_driver);
}
module_init(sprd_leds_bltc_rgb_init);
//匹配函数结构体
static const struct of_device_id sprd_leds_bltc_rgb_of_match[] = {
{ .compatible = "sprd,sprd-leds-bltc-rgb", },
{ }
};
//注册用的sprd_leds_bltc_rgb_driver
static struct platform_driver sprd_leds_bltc_rgb_driver = {
.driver = {
.name  = "sprd-leds-bltc-rgb",
.owner = THIS_MODULE,
.of_match_table = sprd_leds_bltc_rgb_of_match,
},
.probe    = sprd_leds_bltc_rgb_probe,
.remove   = sprd_leds_bltc_rgb_remove,
.shutdown = sprd_leds_bltc_rgb_shutdown,
};
2. 设备dts定义:
sprd-leds-bltc-rgb {
compatible = "sprd,sprd-leds-bltc-rgb";
};
3.设备驱动屏匹配后进入probe
static int sprd_leds_bltc_rgb_probe(struct platform_device *dev)
{
.....
for (i = 0; i < SPRD_LED_TYPE_TOTAL; i++) {
.....
//将本地方法封装到brgb结构中
brgb->cdev.brightness_set = sprd_leds_bltc_rgb_set;
brgb->cdev.name = sprd_leds_rgb_name[i];
brgb->cdev.brightness_get = NULL;
brgb->enable = 0;
//设置操作whiteled的寄存器地址
#if CONFIG_OF
brgb->sprd_bltc_base_addr = SPRD_ADI_BASE + 0x8440;		/***0x40038440***/
brgb->sprd_arm_module_en_addr = SPRD_ADI_BASE + 0x8800;	/***0x40038800***/
#endif
spin_lock_init(&brgb->value_lock);
mutex_init(&brgb->mutex);
//默认关闭guide-led
brgb->value = LED_OFF;
//将本地私有的driver数据地址付给platform_dev中的driver_data
platform_set_drvdata(dev, brgb);

//创建class 下面的brightness节点
ret = led_classdev_register(&dev->dev, &brgb->cdev);
//创建设备节点 rising_time,falling_time,
.....
}
return 0;
err:
if (i) {
for (i = i-1; i >=0; i--) {
if (!brgb)
continue;
led_classdev_unregister(&brgb->cdev);
kfree(brgb);
brgb = NULL;
--brgb;
}
}

return ret;
}

4.led 创建brightneess  等节点

文件:kernel/drivers/leds/led-class.c

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

/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
* @led_cdev: the led_classdev structure for this device.
*/
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);
.......

return 0;
}
5.分析:device_create
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
"%s", led_cdev->name);
leds_class 对应leds 类,其中<span style="font-family: Arial, Helvetica, sans-serif;">leds_class->dev_attr 对应brightness</span>
parents 对应platform 的dev
led_dev 对应led_classdev 对象
led_cdev 对应参数
led_cdev->name:设备节点的名称

创建leds_class ==> 对应/sys/class/leds
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
....
leds_class->dev_attrs = led_class_attrs; //这里为属性节点brightness定义信息
return 0;
}

led_cdev->name ->
brgb->cdev.name = sprd_leds_rgb_name[i];
==>对应 /sys/class/leds/red
/sys/class/leds/red
/sys/class/leds/red
.....
static char *sprd_leds_rgb_name[SPRD_LED_TYPE_TOTAL] = {
"red",
"green",
"blue",
"red_bl",
"green_bl",
"blue_bl",
};
//定义brightness属性 ===> 对应 /sys/class/leds/xxx/brightness
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,
};
//定义brightness 的读写方法
static ssize_t led_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

/* no lock needed for this */
led_update_brightness(led_cdev);

return sprintf(buf, "%u\n", led_cdev->brightness);
}
//将读取的值,传入__led_set_brightness
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);
.....
ret = kstrtoul(buf, 10, &state);
.....
__led_set_brightness(led_cdev, state);

return size;
}
创建完节点之后,再看led_update_brightness ,将实际的rgb灯中的brightness值存放在led_cdev->brightness中

5.led_update_brightness的实现
static void led_update_brightness(struct led_classdev *led_cdev)
{
if (led_cdev->brightness_get)
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}
由于brgb->cdev.brightness_get = NULL;所以 led_update_brightness 对该guide-led 没有多少实际意义
6.创建工作队列
INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
static void set_brightness_delayed(struct work_struct *ws)
{
struct led_classdev *led_cdev =
container_of(ws, struct led_classdev, set_brightness_work);

led_stop_software_blink(led_cdev);

__led_set_brightness(led_cdev, led_cdev->delayed_set_value);
}
这里调用led_stop_software_blink删除定时器
void led_stop_software_blink(struct led_classdev *led_cdev)
{
del_timer_sync(&led_cdev->blink_timer);
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;  //这两个个延迟时间用来控制指示灯闪灭程度的
}
判断brightness 的上下限值,重新设定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))
led_cdev->brightness_set(led_cdev, value);
}
这里的led_cdev->brightness_set最终会调用leds-sprd-bltc-rgb.c的sprd_leds_bltc_rgb_set_brightness,将设置值写入寄存器
7.定义定时器,定时更新 brightness 值
init_timer(&led_cdev->blink_timer);
led_cdev->blink_timer.function = led_timer_function;
led_cdev->blink_timer.data = (unsigned long)led_cdev;

static void led_timer_function(unsigned long data)
{
struct led_classdev *led_cdev = (void *)data;
unsigned long brightness;
unsigned long delay;

if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
__led_set_brightness(led_cdev, LED_OFF);
return;
}

if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
return;
}

brightness = led_get_brightness(led_cdev);
if (!brightness) {
/* Time to switch the LED on. */
brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
*/
led_cdev->blink_brightness = brightness;
brightness = LED_OFF;
delay = led_cdev->blink_delay_off;
}

__led_set_brightness(led_cdev, brightness);

/* Return in next iteration if led is in one-shot mode and we are in
* the final blink state so that the led is toggled each delay_on +
* delay_off milliseconds in worst case.
*/
if (led_cdev->flags & LED_BLINK_ONESHOT) {
if (led_cdev->flags & LED_BLINK_INVERT) {
if (brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
} else {
if (!brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
}
}

mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
//所以如果没有这mod_timer,func 只会执行一次,也就是timer第一次超时时执行的那次
}

8.sprd_leds_bltc_rgb_set的实现

文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c

static void sprd_leds_bltc_rgb_set(struct led_classdev *bltc_rgb_cdev,enum led_brightness value)
{
struct sprd_leds_bltc_rgb *brgb;
unsigned long flags;

brgb = to_sprd_bltc_rgb(bltc_rgb_cdev);
spin_lock_irqsave(&brgb->value_lock, flags);
brgb->leds_flag = bltc_rgb_cdev->flags;
brgb->value = value;
spin_unlock_irqrestore(&brgb->value_lock, flags);

if(1 == brgb->suspend) {
PRINT_WARN("Do NOT change brightness in suspend mode\n");
return;
}
if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0 || \
strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0 || \
strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0)
sprd_leds_rgb_work(brgb);
else
sprd_leds_bltc_work(brgb);
}
9.sprd_leds_rgb_work的实现
static void sprd_leds_rgb_work(struct sprd_leds_bltc_rgb *brgb)
{
unsigned long flags;

mutex_lock(&brgb->mutex);
spin_lock_irqsave(&brgb->value_lock, flags);
if (brgb->value == LED_OFF) {
spin_unlock_irqrestore(&brgb->value_lock, flags);
sprd_leds_bltc_rgb_set_brightness(brgb);
goto out;
}
spin_unlock_irqrestore(&brgb->value_lock, flags);
sprd_leds_bltc_rgb_enable(brgb);
PRINT_INFO("sprd_leds_bltc_rgb_work_for rgb!\n");

out:
mutex_unlock(&brgb->mutex);
}
10.sprd_leds_bltc_rgb_enable的实现:
static void sprd_leds_bltc_rgb_enable(struct sprd_leds_bltc_rgb *brgb)
{
sprd_bltc_rgb_init(brgb);

if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0) {
sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<0)|(0x1<<1));
brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_R_PRESCL + BLTC_DUTY_OFFSET;
sprd_leds_bltc_rgb_set_brightness(brgb);
}
if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0) {
sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<4)|(0x1<<5));
brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_G_PRESCL + BLTC_DUTY_OFFSET;
sprd_leds_bltc_rgb_set_brightness(brgb);
}
if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0) {
sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<8)|(0x1<<9));
brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_B_PRESCL + BLTC_DUTY_OFFSET;
sprd_leds_bltc_rgb_set_brightness(brgb);
}
....
}
11.sprd_leds_bltc_rgb_set_brightness的实现:
static void sprd_leds_bltc_rgb_set_brightness(struct sprd_leds_bltc_rgb *brgb)
{
unsigned long brightness = brgb->value;
unsigned long pwm_duty;

pwm_duty = brightness;
if(pwm_duty > 255)
pwm_duty = 255;
sci_adi_write(brgb->bltc_addr, (pwm_duty<<8)|PWM_MOD_COUNTER,0xffff);
PRINT_INFO("reg:0x%1LX set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15)\n", \
brgb->bltc_addr, sprd_leds_bltc_rgb_read(brgb->bltc_addr),brightness, pwm_duty);
}

三、总结

    本文着重分析开机状态充电指示灯kernel部分驱动实现,在上述的分析中,我们可以看到驱动主要做了以下工作:

1).建立相关的/sys节点,如/sys/class/leds/red/brightness ,并且提供相应的读写方法

2).提供对led whiteled寄存器控制方式,如本篇中有指定whiteled寄存器的地址,并提供了操作写寄存器的方法

sci_adi_write等

      通过上述的工作,上层应用程序只需要操作相关的/sys节点即控制led的亮灭,当然这只是最基本的功能,该驱

动还提供了led 周期性亮灭灯的实现,可以用来实现呼吸灯灯操作,本文不再赘述!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: