展讯7731C_M Android6.0 充电指示灯实现(二)------开机充电实现
2016-08-13 15:34
316 查看
上一节已经了解了展讯7731C_M Android6.0 充电指示灯实的关机部分,这一节主要介绍开机部分,也就是kernel部分。
kernel 部分主要功能是提供对硬件led控制接口,如设置亮度,获取亮度,设置颜色等。
文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c
4.led 创建brightneess 等节点
文件:kernel/drivers/leds/led-class.c
5.led_update_brightness的实现
6.创建工作队列
7.定义定时器,定时更新 brightness 值
8.sprd_leds_bltc_rgb_set的实现
文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c
1).建立相关的/sys节点,如/sys/class/leds/red/brightness ,并且提供相应的读写方法
2).提供对led whiteled寄存器控制方式,如本篇中有指定whiteled寄存器的地址,并提供了操作写寄存器的方法
sci_adi_write等
通过上述的工作,上层应用程序只需要操作相关的/sys节点即控制led的亮灭,当然这只是最基本的功能,该驱
动还提供了led 周期性亮灭灯的实现,可以用来实现呼吸灯灯操作,本文不再赘述!
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 周期性亮灭灯的实现,可以用来实现呼吸灯灯操作,本文不再赘述!
相关文章推荐
- 展讯7731C_M Android6.0 充电指示灯实现
- 展讯7731C_M Android6.0 充电指示灯实现(一)------关机充电实现
- DELL 1330复活计内存条的插槽出问题就会使本本出现:开机后只有电源灯和那个A灯(网上查的是充电、放电的指示灯,我想这个是不是跟电源灯作用一样,只要插电源就会亮),在亮47秒后,断电、关机。
- 接上文,实现boot的开机引导
- 让电脑准时上班 自动开机功能的实现
- c#开机自动启动程序实现代码
- c#开机自动启动程序实现代码
- 如何使用delphi实现取得开机时间
- 怎么实现定时开机
- Linux 下实现远程开机
- 开机杀毒实现
- [转载自csdn]StatusBar上实现指示灯表示启/停,正常/故障等信息
- symbian 利用Recognizer编写自己的MDL实现开机自启动程序
- vbScript实现开机后的开心网自动登陆
- c#开机自动启动程序实现代码
- 如何实现手机中应用程序的开机自启动,附源代码
- 编程实现针对S60手机的自动开机
- c#实现程序开机自动运行
- VC实现程序开机自启动
- Virtual PC虚拟机开机自启动的实现