加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 一、LED子系統(tǒng)框架
    • 二、LED子系統(tǒng)驅(qū)動(dòng)文件
    • 三、查看sysfs文件結(jié)構(gòu)
    • 四、驅(qū)動(dòng)解析
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Camera | 10.閃光燈SGM3141概述

2023/05/31
2543
閱讀需 36 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

前面文章我們簡(jiǎn)單給大家介紹了如何移植閃光燈芯片sgm3141,該驅(qū)動(dòng)依賴了led子系統(tǒng)和v4l2子系統(tǒng)。V4L2可以參考前面camera系列文章,本文主要講述led子系統(tǒng)。

一、LED子系統(tǒng)框架

Linux內(nèi)核的 led 子系統(tǒng)主要功能:

    為每個(gè)設(shè)備在/sys/class/leds下創(chuàng)建不同的文件節(jié)點(diǎn),用于操作led抽象出所有的燈基本操作,設(shè)置亮、滅,光強(qiáng)、閃爍等

框架所處的位置,正如上圖所示,由下往上看:

Hardware:硬件設(shè)備,指的是LED,可以是各種設(shè)備上的led燈

硬件驅(qū)動(dòng)層:是直接操作硬件的實(shí)現(xiàn),用于驅(qū)動(dòng)硬件,實(shí)現(xiàn)相應(yīng)的功能,并且將硬件設(shè)備注冊(cè)進(jìn)框架之中。

核心層:將LED進(jìn)行統(tǒng)一管理,向下提供注冊(cè)接口,向上提供統(tǒng)一訪問接口,方便用戶訪問

用戶層:用戶通過指定的文件節(jié)點(diǎn),能夠直接控制LED的亮滅。

不同的led位于不同的外設(shè)上,有的可能通過gpio控制,也可能由其他的芯片控制,
有的led只需要控制亮滅,有的需要設(shè)置為閃爍,只需要基于架構(gòu)設(shè)置對(duì)應(yīng)的回調(diào)函數(shù)即可。

二、LED子系統(tǒng)驅(qū)動(dòng)文件

了解完LED子系統(tǒng)框架之后,我們來分析一下其相關(guān)的目錄結(jié)構(gòu)!

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????*
【*表示核心文件】

上面即為L(zhǎng)ED子系統(tǒng)的目錄結(jié)構(gòu),其主要核心文件有:

    led-core.c:核心層實(shí)現(xiàn),抽象軟件實(shí)現(xiàn)的相關(guān)功能,如閃爍,亮度設(shè)置等等,并管理LED設(shè)備led-gpio.c:直接控制硬件設(shè)備,并且將其硬件設(shè)備注冊(cè)進(jìn)入LED驅(qū)動(dòng)框架led-class.c:定義用戶訪問的相關(guān)接口led-class-flash.c:燈閃爍相關(guān)功能函數(shù)實(shí)現(xiàn)led-triggers.c:LED出發(fā)功能的抽象ledtrig-cpu.c:將LED作為CPU燈ledtrig-heartbeat.c:將LED作為心跳燈

打開了LED子系統(tǒng)目錄下的kernel/drivers/leds/Makefile,我們看到


#?SPDX-License-Identifier:?GPL-2.0

#?LED?Core
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

我們必須在內(nèi)核的配置中,通過 make menuconfig打開LED的相關(guān)配置,才支持LED相關(guān)功能。

三、查看sysfs文件結(jié)構(gòu)

1. sys/class/leds/

我們?cè)?a class="article-link" target="_blank" href="/tag/%E5%BC%80%E5%8F%91%E6%9D%BF/">開發(fā)板中輸入ls /sys/class/leds/,可以查看LED子系統(tǒng)生成的文件信息。

rk3568_r:/?#?ls?/sys/class/leds
blue??gpio-flash??green??mmc0::??red??
    blue:板子的RGB燈的藍(lán)色green:板子的RGB燈的綠色red: 板子的RGB燈的紅色gpio-flash:camera gpio閃光燈mmc0:: :SD卡指示燈

2. red等子目錄

根據(jù)打開配置的不同,生成不同的文件節(jié)點(diǎn),比如red目錄下信息:

rk3568_r:/sys/class/leds?#?ls?red
brightness??max_brightness??red_bri_reg??subsystem??uevent
device??????power???????????red_delay????trigger

相關(guān)屬性文件有:brightness、max_brightness、trigger等

    max_brightness:表示LED燈的最大亮度值。brightness:表示當(dāng)前LED燈的亮度值,它的可取 值范圍為[0~max_brightness],一些LED設(shè)備不支持多級(jí)亮度,直接以非0值來 表示LED為點(diǎn)亮狀態(tài),0值表示滅狀態(tài)。
@kernel/include/linux/leds.h
enum?led_brightness?{
?LED_OFF??=?0,????//全暗
?LED_HALF?=?127,??//一半亮度
?LED_FULL?=?255,??//最大亮度
};
    delay_off、delay_on:trigger為timer時(shí),LED亮滅的時(shí)間,單位mstrigger:則指示了LED燈的觸發(fā)方式,查看該文件的內(nèi)容時(shí),該文件會(huì) 列出它的所有可用觸方式,而當(dāng)前使用的觸發(fā)方式會(huì)以“[]”符號(hào)括起。

常見的觸 發(fā)方式如下表所示:

觸發(fā)方式 說明
none 無觸發(fā)方式
disk-activity 硬盤活動(dòng)
nand-disk nand flash活動(dòng)
mtd mtd設(shè)備活動(dòng)
timer 定時(shí)器
heartbeat 系統(tǒng)心跳
1)點(diǎn)亮 LED
echo?255?>?/sys/class/leds/red/brightness
cat?/sys/class/leds/red/brightness
cat?/sys/class/leds/red/max_brightness
2)關(guān)閉led
echo?0?>?/sys/class/leds/red/delay_on
或
echo?0?>?/sys/class/leds/red/brightness
3)這幾個(gè)文件節(jié)點(diǎn)由下面宏表示,
@drivers/leds/led-class.c
static?DEVICE_ATTR_RO(max_brightness);

#ifdef?CONFIG_LEDS_TRIGGERS
static?DEVICE_ATTR(trigger,?0644,?led_trigger_show,?led_trigger_store);
static?struct?attribute?*led_trigger_attrs[]?=?{
?&dev_attr_trigger.attr,
?NULL,
};
static?const?struct?attribute_group?led_trigger_group?=?{
?.attrs?=?led_trigger_attrs,
};
#endif

static?struct?attribute?*led_class_attrs[]?=?{
?&dev_attr_brightness.attr,
?&dev_attr_max_brightness.attr,
?NULL,
};

static?const?struct?attribute_group?led_group?=?{
?.attrs?=?led_class_attrs,
};

static?const?struct?attribute_group?*led_groups[]?=?{
?&led_group,
#ifdef?CONFIG_LEDS_TRIGGERS
?&led_trigger_group,
#endif
?NULL,
};

創(chuàng)建位置:

int?of_led_classdev_register(struct?device?*parent,?struct?device_node?*np,
???????struct?led_classdev?*led_cdev)
{
?……
?led_cdev->dev?=?device_create_with_groups(leds_class,?parent,?0,
????led_cdev,?led_cdev->groups,?"%s",?name);
????……
}

3. gpio-flash閃光燈目錄

rk3568_r:/sys/class/leds/gpio-flash?#?ls
brightness??flash_strobe???max_brightness?????power??????trigger
device??????flash_timeout??max_flash_timeout??subsystem??uevent

創(chuàng)建代碼:

@drivers/leds/led-class-flash.c
static?struct?attribute?*led_flash_strobe_attrs[]?=?{
?&dev_attr_flash_strobe.attr,
?NULL,
};

static?struct?attribute?*led_flash_timeout_attrs[]?=?{
?&dev_attr_flash_timeout.attr,
?&dev_attr_max_flash_timeout.attr,
?NULL,
};

static?struct?attribute?*led_flash_brightness_attrs[]?=?{
?&dev_attr_flash_brightness.attr,
?&dev_attr_max_flash_brightness.attr,
?NULL,
};

static?struct?attribute?*led_flash_fault_attrs[]?=?{
?&dev_attr_flash_fault.attr,
?NULL,
};

static?const?struct?attribute_group?led_flash_strobe_group?=?{
?.attrs?=?led_flash_strobe_attrs,
};

static?const?struct?attribute_group?led_flash_timeout_group?=?{
?.attrs?=?led_flash_timeout_attrs,
};

static?const?struct?attribute_group?led_flash_brightness_group?=?{
?.attrs?=?led_flash_brightness_attrs,
};

static?const?struct?attribute_group?led_flash_fault_group?=?{
?.attrs?=?led_flash_fault_attrs,
};

注冊(cè)代碼

int?led_classdev_flash_register(struct?device?*parent,
????struct?led_classdev_flash?*fled_cdev)
{
?……
?if?(led_cdev->flags?&?LED_DEV_CAP_FLASH)?{
??……
??/*?Select?the?sysfs?attributes?to?be?created?for?the?device?*/
??led_flash_init_sysfs_groups(fled_cdev);
?}

?/*?Register?led?class?device?*/
?ret?=?led_classdev_register(parent,?led_cdev);
?……
}

測(cè)試gpio閃光燈

echo?1?>?/sys/class/leds/gpio-flash/flash_strobe??

注意,實(shí)際操作攝像頭閃光燈,并不是通過sysfs下的文件節(jié)點(diǎn)操作,而是通過v4l2架構(gòu)下發(fā)ioctl的命令來實(shí)現(xiàn)的

四、驅(qū)動(dòng)解析

1. 結(jié)構(gòu)體和注冊(cè)函數(shù)

下面介紹led相關(guān)的重要的結(jié)構(gòu)體

struct?led_classdev?{
?const?char??*name;
?enum?led_brightness??brightness;???????//光強(qiáng)
?enum?led_brightness??max_brightness;???//最大光強(qiáng)
?int????flags;
?…………

?/*?set_brightness_work?/?blink_timer?flags,?atomic,?private.?*/
?unsigned?long??work_flags;
?????…………?
?/*?Set?LED?brightness?level
??*?Must?not?sleep.?Use?brightness_set_blocking?for?drivers
??*?that?can?sleep?while?setting?brightness.
??*/
?void??(*brightness_set)(struct?led_classdev?*led_cdev,
???????enum?led_brightness?brightness);??//設(shè)置光強(qiáng)
?/*
??*?Set?LED?brightness?level?immediately?-?it?can?block?the?caller?for
??*?the?time?required?for?accessing?a?LED?device?register.
??*/
?int?(*brightness_set_blocking)(struct?led_classdev?*led_cdev,
???????????enum?led_brightness?brightness);
?/*?Get?LED?brightness?level?*/
?enum?led_brightness?(*brightness_get)(struct?led_classdev?*led_cdev);?//獲取光強(qiáng)

?/*
??*?Activate?hardware?accelerated?blink,?delays?are?in?milliseconds
??*?and?if?both?are?zero?then?a?sensible?default?should?be?chosen.
??*?The?call?should?adjust?the?timings?in?that?case?and?if?it?can't
??*?match?the?values?specified?exactly.
??*?Deactivate?blinking?again?when?the?brightness?is?set?to?LED_OFF
??*?via?the?brightness_set()?callback.
??*/
?int??(*blink_set)(struct?led_classdev?*led_cdev,
?????????unsigned?long?*delay_on,
?????????unsigned?long?*delay_off);

?struct?device??*dev;
?const?struct?attribute_group?**groups;

?struct?list_head??node;???/*?LED?Device?list?*/
?const?char??*default_trigger;?/*?Trigger?to?use?*/

?unsigned?long???blink_delay_on,?blink_delay_off;
?struct?timer_list??blink_timer;
?int????blink_brightness;
?int????new_blink_brightness;
?void???(*flash_resume)(struct?led_classdev?*led_cdev);

?struct?work_struct?set_brightness_work;
?int???delayed_set_value;

#ifdef?CONFIG_LEDS_TRIGGERS
?/*?Protects?the?trigger?data?below?*/
?struct?rw_semaphore??trigger_lock;

?struct?led_trigger?*trigger;
?struct?list_head??trig_list;
?void???*trigger_data;
?/*?true?if?activated?-?deactivate?routine?uses?it?to?do?cleanup?*/
?bool???activated;
#endif

#ifdef?CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
?int????brightness_hw_changed;
?struct?kernfs_node?*brightness_hw_changed_kn;
#endif

?/*?Ensures?consistent?access?to?the?LED?Flash?Class?device?*/
?struct?mutex??led_access;
};

該結(jié)構(gòu)體包括led操作的所有信息,和回調(diào)函數(shù)

注冊(cè)struct led_classdev結(jié)構(gòu)圖變量:

#define?led_classdev_register(parent,?led_cdev)????
?of_led_classdev_register(parent,?NULL,?led_cdev)

對(duì)于gpio閃光燈,則需要填充一下結(jié)構(gòu)體:

struct?led_classdev_flash?{
?/*?led?class?device?*/
?struct?led_classdev?led_cdev;

?/*?flash?led?specific?ops?*/
?const?struct?led_flash_ops?*ops;

?/*?flash?brightness?value?in?microamperes?along?with?its?constraints?*/
?struct?led_flash_setting?brightness;

?/*?flash?timeout?value?in?microseconds?along?with?its?constraints?*/
?struct?led_flash_setting?timeout;

?/*?LED?Flash?class?sysfs?groups?*/
?const?struct?attribute_group?*sysfs_groups[LED_FLASH_SYSFS_GROUPS_SIZE];
};

gpio閃光燈注冊(cè)函數(shù):

int?led_classdev_flash_register(struct?device?*parent,
????struct?led_classdev_flash?*fled_cdev)

2. gpio閃光燈sgm3141驅(qū)動(dòng)詳解

看上圖:

    sgm3141驅(qū)動(dòng)通過函數(shù)led_classdev_flash_register()->led_classdev_register()向led子系統(tǒng)注冊(cè)該設(shè)備sgm3141驅(qū)動(dòng)通過函數(shù)v4l2_async_register_subdev()向v4l2子系統(tǒng)注冊(cè)該設(shè)備如果用戶直接通過/sys/class/leds/gpio-flash/flash_strobe文件操作led燈,則會(huì)直接調(diào)用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?
    1. 如果用戶的app拍照時(shí)操作閃光燈,則是通過v4l2子系統(tǒng)調(diào)用下發(fā)ioctl命令

 

    命令序列:
V4L2_CID_FLASH_LED_MODE :設(shè)置led mod為 V4L2_FLASH_LED_MODE_TORCH(2),并點(diǎn)燈
V4L2_CID_FLASH_LED_MODE:到達(dá)指定超時(shí)時(shí)間(2.7秒),設(shè)置led mod為 V4L2_FLASH_LED_MODE_NONE 0
V4L2_CID_FLASH_LED_MODE:在此設(shè)置led mod為V4L2_FLASH_LED_MODE_FLASH(1)
V4L2_CID_FLASH_STROBE_STOP:停止閃光

操作log:

[???90.246203]?sgm3141?V4L2_CID_FLASH_LED_MODE?2
[???90.246251]?sgm3141_set_ctrl(),376
[???90.246262]?sgm3141_set_output(),78?0
[???90.246277]?sgm3141_set_output(),78?1

[???92.902746]?sgm3141?V4L2_CID_FLASH_LED_MODE?0
[???92.902775]?sgm3141_set_ctrl(),376
[???92.902781]?sgm3141_set_output(),78?0

[???93.034903]?sgm3141?V4L2_CID_FLASH_LED_MODE?1
[???93.034929]?sgm3141_set_ctrl(),376
[???93.034934]?sgm3141_set_output(),78?0
[???93.034943]?sgm3141_led_flash_strobe_set(),166?state=1
[???93.034959]?sgm3141_set_output(),78?1

[???93.034977]?sgm3141?V4L2_CID_FLASH_STROBE_STOP?1
[???93.034988]?sgm3141_set_ctrl(),406
[???93.034993]?sgm3141_led_flash_strobe_set(),166?state=0
[???93.035002]?sgm3141_set_output(),78?0
[???93.035058]?sgm3141_timeout_work(),117
    1. sgm驅(qū)動(dòng)注冊(cè)流程分析

 

    驅(qū)動(dòng)架構(gòu)基于platform總線,platform_driver 結(jié)構(gòu)體如下:
static?const?struct?of_device_id?sgm3141_led_dt_match[]?=?{
?{?.compatible?=?"sgmicro,sgm3141"?},
?{},
};
MODULE_DEVICE_TABLE(of,?sgm3141_led_dt_match);

static?struct?platform_driver?sgm3141_led_driver?=?{
?.probe??=?sgm3141_led_probe,
?.remove??=?sgm3141_led_remove,
?.driver??=?{
??.name?=?"sgm3141-flash",
??.of_match_table?=?sgm3141_led_dt_match,
?},
};

 

 

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。