在我们的安卓手机中,有多种灯光,在SDK/hardware/libhardware/include/hardware文件中,可以看到如下定义

#define LIGHT_ID_BACKLIGHT          "backlight"
#define LIGHT_ID_KEYBOARD           "keyboard"
#define LIGHT_ID_BUTTONS            "buttons"
#define LIGHT_ID_BATTERY            "battery"
#define LIGHT_ID_NOTIFICATIONS      "notifications"
#define LIGHT_ID_ATTENTION          "attention"
#define LIGHT_ID_BLUETOOTH          "bluetooth"
#define LIGHT_ID_WIFI               "wifi"

这些都是android系统逻辑上支持的灯光,他到底对应开发板上的那个LED,由我们的HAL和驱动决定。灯光的使用方式有以下几种:

1.brightness:灯光亮度,可调节0~255;
2.cclor:RGB颜色设定
3.blink:闪烁,onms(亮的时间),offms(灭的时间),通过定时器实现

led_class接口分析

在linux内核中,有一个led子系统,该系统为我们提供了很多接口,在编写灯光系统驱动的时候,我们并不需要从零开始编写,在/drivers/leds目录下,存在文件led_class.c,该文件为我们提供了很多led子系统的接口,其中包括了设置brightness与blink的功能,先查看源码:

static int __init leds_init(void)
{leds_class = class_create(THIS_MODULE, "leds");if (IS_ERR(leds_class))return PTR_ERR(leds_class);leds_class->pm = &leds_class_dev_pm_ops;leds_class->dev_groups = led_groups;return 0;
}

入口函数中,在sys/class目录下创建一个leds类,往上我们可以看到一个SIMPLE_DEV_PM_OPS宏, 将led-class中的suspend中的指针,以及resume的指针初始化当系统休眠或唤醒的时候,遍历class,调用其中的pm的书suspend() 或resume()

 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);==>static const struct dev_pm_ops leds_class_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(led_suspend, led_resume) }==>static const struct dev_pm_ops leds_class_dev_pm_ops = {.suspend = led_suspend,.resume = led_resume,}

然后还会在sys/class/leds目录具体的led下创建3个子节点brightness、max_brightness、trigge,

static DEVICE_ATTR_RW(brightness);           // 可读可写
static DEVICE_ATTR_RO(max_brightness);      // 只读
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); // 可读

我们可以用其创建的子节点对LED进行控制,如

 读 cat /sys/class/leds/xxx/brightness写 echo 255 > /sys/class/leds/xxx/brightness

我们就能查询或者控制led的亮度了,那么为什么呢?

static ssize_t brightness_show(struct device *dev,struct device_attribute *attr, char *buf)
static ssize_t brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
static DEVICE_ATTR_RW(brightness);

当往brightness文件写入时,会调用 brightness_store函数,当从brightness读出时,会调用brightness_show函数,我们brightness_store函数中会调用led_set_brightness(led_cdev, state)函数,然后根据led_cdev->flags 判断之后,如果为SET_BRIGHTNESS_ASYNC则最终调用led_cdev->brightness_set(led_cdev, value),如果为SET_BRIGHTNESS_SYNC则最终调用 led_cdev->brightness_set_sync(led_cdev,led_cdev->brightness);从这里可以看出来,我们编写驱动程序的时候,必须根据led_cdev->flags实现一个led_cdev->brightness_set或者brightness_set_sync函数。

编写led驱动程序

根据前面的分析,如果我们通过led子系统提供的框架编写驱动程序,那么我们至少需要实现一下几点,

1.分配一个struct led_classdev  led_cdev,
2.设置 led_cdev->max_brightness最大亮度值。led_cdev->brightnessled_cdev->flgs(SET_BRIGHTNESS_SYNC,SET_BRIGHTNESS_ASYNC)led_cdev->brightness_set_sync或者led_cdev->brightness_set函数。
3.注册:led_classdev_register(struct device *parent, struct led_classdev *led_cdev)

编写led_gec3399_drv.c具体代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/slab.h>struct led_desc {int gpio;char *name;
};struct led_classdev_gec3399 {struct led_classdev cdev;int gpio;
};static struct led_desc led_gpios[] = {{(32*0 + 8*1 + 4),"led1"},{(32*0 + 8*1 + 0),"led2"},
};static struct led_classdev_gec3399 *led_devs;static void brightness_set_gec3399(struct led_classdev *led_cdev,enum led_brightness brightness)
{struct led_classdev_gec3399 *dev = (struct led_classdev_gec3399*)led_cdev;if(brightness != LED_OFF)gpio_set_value(dev->gpio,0);elsegpio_set_value(dev->gpio,1);led_cdev->brightness = brightness;
}static int __init gec3399_leds_init(void)
{int i=0;int ret = 0;/*1.alloc led_classdev*/led_devs = (void*)kzalloc(sizeof(struct led_classdev_gec3399)*sizeof(led_gpios)/sizeof(led_gpios[0]),GFP_KERNEL);if (!led_devs){printk("%s %d NO memory for device\n",__FUNCTION__,__LINE__);return -ENOMEM;}for(i=0; i<sizeof(led_gpios)/sizeof(led_gpios[0]); i++){gpio_direction_output(led_gpios[i].gpio,1);      //鍒濆鍒扡ED1涓虹唲鐏姸鎬?/*2.set*/led_devs[i].cdev.max_brightness = LED_FULL;led_devs[i].cdev.brightness_set = brightness_set_gec3399;led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;//设置具有休眠和唤醒功能led_devs[i].cdev.brightness = LED_OFF;led_devs[i].cdev.name = led_gpios[i].name;led_devs[i].gpio = led_gpios[i].gpio;/*3.led_classdev_register*/ret = led_classdev_register(NULL, &led_devs[i].cdev);if(ret){i--;while(i >= 0){led_classdev_unregister(&led_devs[i].cdev);i--;}kfree(led_devs);return -EIO;}}return 0;}
static void __exit gec3399_leds_exit(void)
{int i;for(i=0; i<sizeof(led_gpios)/sizeof(led_gpios[0]); i++){led_classdev_unregister(&led_devs[i].cdev);}kfree(led_devs);
}module_init(gec3399_leds_init);
module_exit(gec3399_leds_exit);MODULE_LICENSE("GPL");

上述驱动程序,使用for(i=0; i<sizeof(led_gpios)/sizeof(led_gpios[0]); i++)循环,注册了两个leds,编写完成以后,我们把改驱动编译进内核

1.拷贝led_gec3399_drv.c文件到源码目录SDK/drivers/leds目录下。2.修改SDK/drivers/leds/Makefile,添加obj-y += led_gec3399_drv.o3.修改menuconfig,打开一下选项CONFIG_LEDS_TRIGGERSDevice Drivers  ---> LED Support  --->LED Trigger support  ---> LED Timer Trigger打开该配置是为了后续实现闪烁功能4.在kernel目录下执行make ARCH=arm64 rk3399-sapphire-excavator-edp.img -j12等待编译完成之后,执行source build/envsetup.shlunch rk3399_all-userdebugmake bootimage -j3

编译完成之后,把新生成的out/target/product/rk3399/boot.img烧写到开发板中, 在sys/class/leds下会存在两个目录,分别为led1,与led2。然后我们操控目录下的文件,就可以实现对LED的控制了

命令操控

led控制

我们cd到 /sys/class/leds/led1目录下可以看到:
brightness max_brightness power subsystem trigger uevent
我们执行
echo 255 > /sys/class/leds/led1/brightness
即可点亮LED1
echo 0 > /sys/class/leds/led1/brightness
即可熄灭LED1
执行cat trigger可以看到:
[none] rc-feedback test_ac-online test_battery-charging-or-full test_battery-charging test_battery-full test_battery-charging-blink-full-solid test_usb-online mmc0 mmc1 timer backlight default-on rfkill0 mmc2
其中[]代表现在闪烁的模式,none代表还没有设置
执行
echo > timer /sys/class/leds/led1/trigger
即可设置led以定时器模式闪烁
此时我们再次查看/sys/class/leds/led1:
brightness delay_off delay_on max_brightness power subsystem trigger uevent
可以看到多了delay_on,delay_off文件,我们可以通过echo往里面写值,控制灯亮和灯灭的时间
同样我们可以往max_brightness写入或者读取,去获取或者设置亮度值
echo 255 > /sys/class/leds/led1/brightness

backlight

和上面相似,在sys/class目录下存在backlight文件夹,我们cd到该目录下可以看到如下文件:

actual_brightness      brightness    max_brightness
subsystem uevent      bl_power          device     power          type

和前面一样,我们可以通过
echo xxx > /sys/class/backlight/backlight/brightness可以改变屏幕的亮度

trigger分析

为什么我们通过echo > timer /sys/class/leds/led1/trigger就能控制led进行闪烁呢?在前面我们提到过led_class.c文件中,存在宏static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store),他会在/sys/classled1或led2目录下(我们注册的led)创建文件trigger,当我们往该文件写入时,会调用led_trigger_store,读取时会调用led_trigger_show。
在led_trigger.c文件中,我们可以看到led_trigger_store函数的定义:

ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);char trigger_name[TRIG_NAME_MAX];struct led_trigger *trig;size_t len;int ret = count;mutex_lock(&led_cdev->led_access);if (led_sysfs_is_disabled(led_cdev)) {ret = -EBUSY;goto unlock;}trigger_name[sizeof(trigger_name) - 1] = '\0';strncpy(trigger_name, buf, sizeof(trigger_name) - 1);len = strlen(trigger_name);if (len && trigger_name[len - 1] == '\n')trigger_name[len - 1] = '\0';if (!strcmp(trigger_name, "none")) {led_trigger_remove(led_cdev);goto unlock;}down_read(&triggers_list_lock);list_for_each_entry(trig, &trigger_list, next_trig) {if (!strcmp(trigger_name, trig->name)) {down_write(&led_cdev->trigger_lock);led_trigger_set(led_cdev, trig);up_write(&led_cdev->trigger_lock);up_read(&triggers_list_lock);goto unlock;}}up_read(&triggers_list_lock);unlock:mutex_unlock(&led_cdev->led_access);return ret;

调用strncpy(trigger_name, buf, sizeof(trigger_name) - 1);从buf中取出trigger,在之前的实验中我们设置为timer。然后执行list_for_each_entry(trig, &trigger_list, next_trig) ,即用取出的trigger与trigger_list中的每一项进行匹配,如果能匹配成功,则执行led_trigger_set(led_cdev, trig),其中有代码如下:

     led_cdev->trigger = trig;if (trig->activate)trig->activate(led_cdev);

如果存在trig->activate,则会调用该函数,下面我们查看ledtrig-timer.c文件的入口函数:

static struct led_trigger timer_led_trigger = {.name     = "timer",.activate = timer_trig_activate,.deactivate = timer_trig_deactivate,
};static int __init timer_trig_init(void)
{return led_trigger_register(&timer_led_trigger);
}

可知其设定了.activate = timer_trig_activate,其 timer_trig_activate函数如下:

static void timer_trig_activate(struct led_classdev *led_cdev)
{int rc;led_cdev->trigger_data = NULL;rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);if (rc)return;rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);if (rc)goto err_out_delayon;led_blink_set(led_cdev, &led_cdev->blink_delay_on,&led_cdev->blink_delay_off);led_cdev->activated = true;return;err_out_delayon:device_remove_file(led_cdev->dev, &dev_attr_delay_on);
}

可以知道其创建了两个文件即前面出现的delay_on与delay_off。并设置这两个文件的读写函数,并执行led_blink_set(led_cdev, &led_cdev->blink_delay_on,&led_cdev->blink_delay_off)使LED进行闪烁,led_blink_set中调用了led_set_software_blink,其led_set_software_blink代码如下:

static void led_set_software_blink(struct led_classdev *led_cdev,unsigned long delay_on,unsigned long delay_off)
{int current_brightness;current_brightness = led_get_brightness(led_cdev);if (current_brightness)led_cdev->blink_brightness = current_brightness;if (!led_cdev->blink_brightness)led_cdev->blink_brightness = led_cdev->max_brightness;led_cdev->blink_delay_on = delay_on;led_cdev->blink_delay_off = delay_off;/* never on - just set to off */if (!delay_on) {led_set_brightness_async(led_cdev, LED_OFF);return;}/* never off - just set to brightness */if (!delay_off) {led_set_brightness_async(led_cdev, led_cdev->blink_brightness);return;}mod_timer(&led_cdev->blink_timer, jiffies + 1);
}

可以知道,最终使用mod_timer(&led_cdev->blink_timer, jiffies + 1);实现了闪烁功能。

小节结语

该小节,主要分析了led子系统提供的接口,然后利用该接口实现我们的驱动程序,以及如何在命令行控制LED,最后在对led子系统闪烁功能进行了讲解

第四章:Android灯光系统(2)-led_class驱动相关推荐

  1. 浅入浅出 Android 安全:第四章 Android 框架层安全

    第四章 Android 框架层安全 来源:Yury Zhauniarovich | Publications 译者:飞龙 协议:CC BY-NC-SA 4.0 如我们在第1.2节中所描述的那样,应用程 ...

  2. 第四章 Android WiFi基础知识

    系列文章目录 第一章 国内下载AOSP最新源码的方法 第二章 下载AOSP WiFi相关的代码 第三章 将源码导入Android Studio(无需编译idegen) 文章目录 系列文章目录 前言 一 ...

  3. 第四章案例研究--------------------- 基于业务驱动的企业安全架构(翻译,原作者John Sherwood ;Andrew Clark; David Lynas)---仅学习使用

    第 4 章:案例研究 本书的目的是提供一个高度实用的指南,尽可能将作品与读者的现实生活环境和经历联系起来. 因此,它借鉴了尽可能多的案例研究. 一些案例研究是一次性的小事,通常来自作者在许多不同行业领 ...

  4. 第四章 android 命名规范和编码规范

    书里面讲的比较常见,单个人也是有不同的观点: 因为android绝大部分使用java开发的,因此java相关规范适用于android: Google Style: 英文地址:http://google ...

  5. Android深度探索(卷1)HAL与驱动开发 第四章 源代码的下载和编译 读书笔记

    Android深度探索(卷1)HAL与驱动开发 第四章 源代码的下载和编译 读书笔记     本章学习了使用git下载两套源代码并搭建两个开发环境.分别为Android源代码和Linux内核源代码.A ...

  6. Android系统移植与驱动开发--第四章

    第四章 源代码的下载和编译 一个android内核相当于4G,而一个Linux内个只有几百M,Linux内核相对于android内核来说实在是小巫见大巫.了解android源代码不一定要详细了解,只去 ...

  7. Android深度探索(卷1)HAL与驱动开发 读书笔记(第四章)

    第四章  源代码的下载和编译 本章主要介绍使用Git下载两套源代码.一套是Android 源代码,另一套是Linux 内核源代码.主要介绍如何下载和编译Android源代码和Linux内核源代码. 4 ...

  8. 《android深入探索》第四章心得

    看了本书第四章,我学会了下载.编译.测试android源代码和linux内核源代码: android源代码的下载.编译.测试: ① 配置Android源代码的下载环境: 创建一个用于存放下载脚本文件( ...

  9. 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0  ...

最新文章

  1. Java并发编程之CAS
  2. 【CTF大赛】2021 DASCTF July cybercms 一探再探
  3. Nginx反向代理负载均衡时,验证码不正确
  4. php curl安装检查,如何判断php的curl是否已安装
  5. wamp2.2-64位 localhost和localhost/phpmyadmin不能访问问题解决
  6. Java Web 之Token+Cookie+Session
  7. 10-300-020-简介-架构-简介
  8. 属性数量限制android,骑马与砍杀2军队数量上限属性加成MOD
  9. layui 读取本地excel内容_layui之数据表格--与后台交互获取数据的方法
  10. 仓储扫描管理系统服务器价格,仓储条码管理系统解决方案报告书.doc
  11. 软件概要设计的过程和内容
  12. Android Toast的立即取消与显示
  13. 华为H3CNE认证题库、教材-热门下载帖汇总!
  14. Set Similarity
  15. 微信服务器是否记录视频信息,微信视频号有访客记录吗 微信视频号可以查看浏览记录吗...
  16. 百度地图API学习---隐藏百度版权标志
  17. 食物과 學問의 萃聚
  18. c语言自学听不懂,为什么C语言这么难学,怎么才能学好呢?
  19. 计算机无法打开声音,电脑打不开声音合成器如何修复
  20. airpak模拟案例_airpak气流组织模拟教程.pdf

热门文章

  1. 一个人做网站需要学习哪些知识?
  2. 【Java——打印杨辉三角】
  3. Flink实时数仓数据采集流程和技术架构
  4. linux程序 tty没了,linux – 提示自定义:如何检测何时没有tty
  5. 云南初中计算机学业水平要求,云南省初中信息技术学业水平考试复习合集
  6. 泡泡龙游戏算法实现简介
  7. Window Installer Clean Up好用的软件管理工具
  8. Ubuntu 18.04 LTS 命令行方式安装中文语言包
  9. 测井等数据的噪声处理及图例
  10. 我下载了python所有包,24个G,用以备份