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

   上一节已经了解了展讯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 周期性亮灭灯的实现,可以用来实现呼吸灯灯操作,本文不再赘述!



  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值