Camera | 10.闪光灯SGM3141概述
来源:一口Linux发布时间:2023-05-30784浏览
询问 AI
前面文章我们简单给大家介绍了如何移植闪光灯芯片sgm3141,该驱动依赖了led子系统和v4l2子系统。V4L2可以参考前面camera系列文章,本文主要讲述led子系统。
Linux内核的 led 子系统主要功能:
- 为每个设备在/sys/class/leds下创建不同的文件节点,用于操作led
- 抽象出所有的灯基本操作,设置亮、灭,光强、闪烁等

框架所处的位置,正如上图所示,由下往上看:
Hardware:硬件设备,指的是LED,可以是各种设备上的led灯
硬件驱动层:是直接操作硬件的实现,用于驱动硬件,实现相应的功能,并且将硬件设备注册进框架之中。
核心层:将LED进行统一管理,向下提供注册接口,向上提供统一访问接口,方便用户访问
用户层:用户通过指定的文件节点,能够直接控制LED的亮灭。
不同的led位于不同的外设上,有的可能通过gpio控制,也可能由其他的芯片控制,有的led只需要控制亮灭,有的需要设置为闪烁,只需要基于架构设置对应的回调函数即可。
了解完LED子系统框架之后,我们来分析一下其相关的目录结构!
kernel
│└──driver
││└──leds
│││├──Makefile
│││├──led-core.c*
│││├──led-gpio.c
│││├──led-class.c*
│││├──led-class-flash.c*
│││├──led-triggers.c*
│││├──......
│││└──trigger
││││├──ledtrig-cpu.c
││││├──ledtrig-heartbeat.c
││││├──.......
include
│└──linux
││├──leds.h*
【*表示核心文件】
上面即为LED子系统的目录结构,其主要核心文件有:
- led-core.c:核心层实现,抽象软件实现的相关功能,如闪烁,亮度设置等等,并管理LED设备
- led-gpio.c:直接控制硬件设备,并且将其硬件设备注册进入LED驱动框架
- led-class.c:定义用户访问的相关接口
- led-class-flash.c:灯闪烁相关功能函数实现
- led-triggers.c:LED出发功能的抽象
- ledtrig-cpu.c:将LED作为CPU灯
- ledtrig-heartbeat.c:将LED作为心跳灯
打开了LED子系统目录下的kernel/drivers/leds/Makefile,我们看到
#SPDX-License-Identifier:GPL-2.0
#LEDCore
obj-$(CONFIG_NEW_LEDS)+=led-core.o
obj-$(CONFIG_LEDS_CLASS)+=led-class.o
obj-$(CONFIG_LEDS_CLASS_FLASH)+=led-class-flash.o
obj-$(CONFIG_LEDS_TRIGGERS)+=led-triggers.o
我们必须在内核的配置中,通过 make menuconfig打开LED的相关配置,才支持LED相关功能。
我们在开发板中输入ls /sys/class/leds/,可以查看LED子系统生成的文件信息。
rk3568_r:/#ls/sys/class/leds
bluegpio-flashgreenmmc0::red
- blue:板子的RGB灯的蓝色
- green:板子的RGB灯的绿色
- red: 板子的RGB灯的红色
- gpio-flash:camera gpio闪光灯
- mmc0:: :SD卡指示灯
根据打开配置的不同,生成不同的文件节点,比如red目录下信息:
rk3568_r:/sys/class/leds#lsred
brightnessmax_brightnessred_bri_regsubsystemuevent
devicepowerred_delaytrigger
相关属性文件有:brightness、max_brightness、trigger等
- max_brightness:表示LED灯的最大亮度值。
- brightness:表示当前LED灯的亮度值,它的可取 值范围为[0~max_brightness],一些LED设备不支持多级亮度,直接以非0值来 表示LED为点亮状态,0值表示灭状态。
@kernel/include/linux/leds.h
enumled_brightness{
LED_OFF=0,//全暗
LED_HALF=127,//一半亮度
LED_FULL=255,//最大亮度
};
- delay_off、delay_on:trigger为timer时,LED亮灭的时间,单位ms
- trigger:则指示了LED灯的触发方式,查看该文件的内容时,该文件会 列出它的所有可用触方式,而当前使用的触发方式会以“[]”符号括起。
常见的触 发方式如下表所示:
| 触发方式 | 说明 |
|---|---|
| none | 无触发方式 |
| disk-activity | 硬盘活动 |
| nand-disk | nand flash活动 |
| mtd | mtd设备活动 |
| timer | 定时器 |
| heartbeat | 系统心跳 |
echo255>/sys/class/leds/red/brightness
cat/sys/class/leds/red/brightness
cat/sys/class/leds/red/max_brightness
echo0>/sys/class/leds/red/delay_on
或
echo0>/sys/class/leds/red/brightness
@drivers/leds/led-class.c
staticDEVICE_ATTR_RO(max_brightness);
#ifdefCONFIG_LEDS_TRIGGERS
staticDEVICE_ATTR(trigger,0644,led_trigger_show,led_trigger_store);
staticstructattribute*led_trigger_attrs[]={
dev_attr_trigger.attr,
NULL,
};
staticconststructattribute_groupled_trigger_group={
.attrs=led_trigger_attrs,
};
#endif
staticstructattribute*led_class_attrs[]={
dev_attr_brightness.attr,
dev_attr_max_brightness.attr,
NULL,
};
staticconststructattribute_groupled_group={
.attrs=led_class_attrs,
};
staticconststructattribute_group*led_groups[]={
led_group,
#ifdefCONFIG_LEDS_TRIGGERS
led_trigger_group,
#endif
NULL,
};
创建位置:
intof_led_classdev_register(structdevice*parent,structdevice_node*np,
structled_classdev*led_cdev)
{
……
led_cdev->dev=device_create_with_groups(leds_class,parent,0,
led_cdev,led_cdev->groups,"%s",name);
……
}
rk3568_r:/sys/class/leds/gpio-flash#ls
brightnessflash_strobemax_brightnesspowertrigger
deviceflash_timeoutmax_flash_timeoutsubsystemuevent
创建代码:
@drivers/leds/led-class-flash.c
staticstructattribute*led_flash_strobe_attrs[]={
dev_attr_flash_strobe.attr,
NULL,
};
staticstructattribute*led_flash_timeout_attrs[]={
dev_attr_flash_timeout.attr,
dev_attr_max_flash_timeout.attr,
NULL,
};
staticstructattribute*led_flash_brightness_attrs[]={
dev_attr_flash_brightness.attr,
dev_attr_max_flash_brightness.attr,
NULL,
};
staticstructattribute*led_flash_fault_attrs[]={
dev_attr_flash_fault.attr,
NULL,
};
staticconststructattribute_groupled_flash_strobe_group={
.attrs=led_flash_strobe_attrs,
};
staticconststructattribute_groupled_flash_timeout_group={
.attrs=led_flash_timeout_attrs,
};
staticconststructattribute_groupled_flash_brightness_group={
.attrs=led_flash_brightness_attrs,
};
staticconststructattribute_groupled_flash_fault_group={
.attrs=led_flash_fault_attrs,
};
注册代码
intled_classdev_flash_register(structdevice*parent,
structled_classdev_flash*fled_cdev)
{
……
if(led_cdev->flagsLED_DEV_CAP_FLASH){
……
/*Selectthesysfsattributestobecreatedforthedevice*/
led_flash_init_sysfs_groups(fled_cdev);
}
/*Registerledclassdevice*/
ret=led_classdev_register(parent,led_cdev);
……
}
测试gpio闪光灯
echo1>/sys/class/leds/gpio-flash/flash_strobe
“注意,实际操作摄像头闪光灯,并不是通过sysfs下的文件节点操作,而是通过v4l2架构下发ioctl的命令来实现的
”
下面介绍led相关的重要的结构体
structled_classdev{
constchar*name;
enumled_brightnessbrightness;//光强
enumled_brightnessmax_brightness;//最大光强
intflags;
…………
/*set_brightness_work/blink_timerflags,atomic,private.*/
unsignedlongwork_flags;
…………
/*SetLEDbrightnesslevel
*Mustnotsleep.Usebrightness_set_blockingfordrivers
*thatcansleepwhilesettingbrightness.
*/
void(*brightness_set)(structled_classdev*led_cdev,
enumled_brightnessbrightness);//设置光强
/*
*SetLEDbrightnesslevelimmediately-itcanblockthecallerfor
*thetimerequiredforaccessingaLEDdeviceregister.
*/
int(*brightness_set_blocking)(structled_classdev*led_cdev,
enumled_brightnessbrightness);
/*GetLEDbrightnesslevel*/
enumled_brightness(*brightness_get)(structled_classdev*led_cdev);//获取光强
/*
*Activatehardwareacceleratedblink,delaysareinmilliseconds
*andifbotharezerothenasensibledefaultshouldbechosen.
*Thecallshouldadjustthetimingsinthatcaseandifitcan't
*matchthevaluesspecifiedexactly.
*DeactivateblinkingagainwhenthebrightnessissettoLED_OFF
*viathebrightness_set()callback.
*/
int(*blink_set)(structled_classdev*led_cdev,
unsignedlong*delay_on,
unsignedlong*delay_off);
structdevice*dev;
conststructattribute_group**groups;
structlist_headnode;/*LEDDevicelist*/
constchar*default_trigger;/*Triggertouse*/
unsignedlongblink_delay_on,blink_delay_off;
structtimer_listblink_timer;
intblink_brightness;
intnew_blink_brightness;
void(*flash_resume)(structled_classdev*led_cdev);
structwork_structset_brightness_work;
intdelayed_set_value;
#ifdefCONFIG_LEDS_TRIGGERS
/*Protectsthetriggerdatabelow*/
structrw_semaphoretrigger_lock;
structled_trigger*trigger;
structlist_headtrig_list;
void*trigger_data;
/*trueifactivated-deactivateroutineusesittodocleanup*/
boolactivated;
#endif
#ifdefCONFIG_LEDS_BRIGHTNESS_HW_CHANGED
intbrightness_hw_changed;
structkernfs_node*brightness_hw_changed_kn;
#endif
/*EnsuresconsistentaccesstotheLEDFlashClassdevice*/
structmutexled_access;
};
该结构体包括led操作的所有信息,和回调函数
注册struct led_classdev结构图变量:
#defineled_classdev_register(parent,led_cdev)\
of_led_classdev_register(parent,NULL,led_cdev)
对于gpio闪光灯,则需要填充一下结构体:
structled_classdev_flash{
/*ledclassdevice*/
structled_classdevled_cdev;
/*flashledspecificops*/
conststructled_flash_ops*ops;
/*flashbrightnessvalueinmicroamperesalongwithitsconstraints*/
structled_flash_settingbrightness;
/*flashtimeoutvalueinmicrosecondsalongwithitsconstraints*/
structled_flash_settingtimeout;
/*LEDFlashclasssysfsgroups*/
conststructattribute_group*sysfs_groups[LED_FLASH_SYSFS_GROUPS_SIZE];
};
gpio闪光灯注册函数:
intled_classdev_flash_register(structdevice*parent,
structled_classdev_flash*fled_cdev)
看上图:
- sgm3141驱动通过函数led_classdev_flash_register()->led_classdev_register()向led子系统注册该设备
- sgm3141驱动通过函数v4l2_async_register_subdev()向v4l2子系统注册该设备
- 如果用户直接通过/sys/class/leds/gpio-flash/flash_strobe文件操作led灯,则会直接调用struct led_flash_ops flash_ops的 .strobe_set方法,即sgm3141_led_flash_strobe_set()
操作log:
[492.026391]sgm3141_led_flash_strobe_set+0x24/0x78
[492.026453]flash_strobe_store+0x88/0xd8
[492.026517]dev_attr_store+0x18/0x28
[492.026571]sysfs_kf_write+0x48/0x58
[492.026620]kernfs_fop_write+0xf4/0x220
[492.026683]__vfs_write+0x34/0x158
[492.026733]vfs_write+0xb0/0x1d0
[492.026784]ksys_write+0x64/0xe0
[492.026833]__arm64_sys_write+0x14/0x20
[492.026867]el0_svc_common.constprop.0+0x64/0x178
[492.026912]el0_svc_handler+0x28/0x78
[492.026966]el0_svc+0x8/0xc
- 如果用户的app拍照时操作闪光灯,则是通过v4l2子系统调用下发ioctl命令命令序列:
V4L2_CID_FLASH_LED_MODE :设置led mod为 V4L2_FLASH_LED_MODE_TORCH(2),并点灯
V4L2_CID_FLASH_LED_MODE:到达指定超时时间(2.7秒),设置led mod为 V4L2_FLASH_LED_MODE_NONE 0
V4L2_CID_FLASH_LED_MODE:在此设置led mod为V4L2_FLASH_LED_MODE_FLASH(1)
V4L2_CID_FLASH_STROBE_STOP:停止闪光
操作log:
[90.246203]sgm3141V4L2_CID_FLASH_LED_MODE2
[90.246251]sgm3141_set_ctrl(),376
[90.246262]sgm3141_set_output(),780
[90.246277]sgm3141_set_output(),781
[92.902746]sgm3141V4L2_CID_FLASH_LED_MODE0
[92.902775]sgm3141_set_ctrl(),376
[92.902781]sgm3141_set_output(),780
[93.034903]sgm3141V4L2_CID_FLASH_LED_MODE1
[93.034929]sgm3141_set_ctrl(),376
[93.034934]sgm3141_set_output(),780
[93.034943]sgm3141_led_flash_strobe_set(),166state=1
[93.034959]sgm3141_set_output(),781
[93.034977]sgm3141V4L2_CID_FLASH_STROBE_STOP1
[93.034988]sgm3141_set_ctrl(),406
[93.034993]sgm3141_led_flash_strobe_set(),166state=0
[93.035002]sgm3141_set_output(),780
[93.035058]sgm3141_timeout_work(),117
- sgm驱动注册流程分析驱动架构基于platform总线,platform_driver 结构体如下:
staticconststructof_device_idsgm3141_led_dt_match[]={
{.compatible="sgmicro,sgm3141"},
{},
};
MODULE_DEVICE_TABLE(of,sgm3141_led_dt_match);
staticstructplatform_driversgm3141_led_driver={
.probe=sgm3141_led_probe,
.remove=sgm3141_led_remove,
.driver={
.name="sgm3141-flash",
.of_match_table=sgm3141_led_dt_match,
},
};


这是一口君的新书,感谢大家支持!
一口Linux
关注,回复【1024】海量Linux资料赠送
新闻来源:一口Linux,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
