前面文章我們簡單給大家介紹了如何移植閃光燈芯片sgm3141,該驅(qū)動依賴了led子系統(tǒng)和v4l2子系統(tǒng)。 V4L2可以參考前面camera系列文章,本文主要講述led子系統(tǒng)。
一、LED子系統(tǒng)框架
Linux內(nèi)核的 led 子系統(tǒng)主要功能:
- 為每個設(shè)備在/sys/class/leds下創(chuàng)建不同的文件節(jié)點,用于操作led
- 抽象出所有的燈基本操作,設(shè)置亮、滅,光強、閃爍等
框架所處的位置,正如上圖所示,由下往上看:
-
Hardware: 硬件設(shè)備,指的是LED,可以是各種設(shè)備上的led燈
-
硬件驅(qū)動層: 是直接操作硬件的實現(xiàn),用于驅(qū)動硬件,實現(xiàn)相應(yīng)的功能,并且將硬件設(shè)備注冊進框架之中。
-
核心層: 將LED進行統(tǒng)一管理,向下提供注冊接口,向上提供統(tǒng)一訪問接口,方便用戶訪問
-
用戶層: 用戶通過指定的文件節(jié)點,能夠直接控制LED的亮滅。
不同的led位于不同的外設(shè)上,有的可能通過gpio控制,也可能由其他的芯片控制, 有的led只需要控制亮滅,有的需要設(shè)置為閃爍,只需要基于架構(gòu)設(shè)置對應(yīng)的回調(diào)函數(shù)即可。
二、LED子系統(tǒng)驅(qū)動文件
了解完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 *【*表示核心文件】
上面即為LED子系統(tǒng)的目錄結(jié)構(gòu),其主要核心文件有:
- led-core.c:核心層實現(xiàn),抽象軟件實現(xiàn)的相關(guān)功能,如閃爍,亮度設(shè)置等等,并管理LED設(shè)備
- led-gpio.c:直接控制硬件設(shè)備,并且將其硬件設(shè)備注冊進入LED驅(qū)動框架
- led-class.c:定義用戶訪問的相關(guān)接口
- led-class-flash.c:燈閃爍相關(guān)功能函數(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 Coreobj-$(CONFIG_NEW_LEDS)+= led-core.oobj-$(CONFIG_LEDS_CLASS)+= led-class.oobj-$(CONFIG_LEDS_CLASS_FLASH)+= led-class-flash.oobj-$(CONFIG_LEDS_TRIGGERS)+= led-triggers.o
我們必須在內(nèi)核的配置中,通過 make menuconfig打開LED的相關(guān)配置,才支持LED相關(guān)功能。
三、查看sysfs文件結(jié)構(gòu)
1. sys/class/leds/
我們在開發(fā)板中輸入ls /sys/class/leds/,可以查看LED子系統(tǒng)生成的文件信息。
rk3568_r:/ # ls /sys/class/ledsblue gpio-flash green mmc0:: red
- blue:板子的RGB燈的藍色
- green:板子的RGB燈的綠色
- red: 板子的RGB燈的紅色
- gpio-flash:camera gpio閃光燈
- mmc0:: :SD卡指示燈
2. red等子目錄
根據(jù)打開配置的不同,生成不同的文件節(jié)點,比如red目錄下信息:
rk3568_r:/sys/class/leds # ls redbrightness max_brightness red_bri_reg subsystem ueventdevice power red_delay trigger
相關(guān)屬性文件有:brightness、max_brightness、trigger等
- max_brightness:表示LED燈的最大亮度值。
- brightness:表示當(dāng)前LED燈的亮度值,它的可取 值范圍為[0~max_brightness],一些LED設(shè)備不支持多級亮度,直接以非0值來 表示LED為點亮狀態(tài),0值表示滅狀態(tài)。
@kernel/include/linux/leds.henum led_brightness { LED_OFF = 0, //全暗 LED_HALF = 127, //一半亮度 LED_FULL = 255, //最大亮度};
- delay_off、delay_on:trigger為timer時,LED亮滅的時間,單位ms
- trigger:則指示了LED燈的觸發(fā)方式,查看該文件的內(nèi)容時,該文件會 列出它的所有可用觸方式,而當(dāng)前使用的觸發(fā)方式會以“[]”符號括起。
常見的觸 發(fā)方式如下表所示:
觸發(fā)方式 說明 none 無觸發(fā)方式 disk-activity 硬盤活動 nand-disk nand flash活動 mtd mtd設(shè)備活動 timer 定時器 heartbeat 系統(tǒng)心跳 1)點亮 LED
echo 255 > /sys/class/leds/red/brightnesscat /sys/class/leds/red/brightnesscat /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)這幾個文件節(jié)點由下面宏表示,
@drivers/leds/led-class.cstatic DEVICE_ATTR_RO(max_brightness);#ifdef CONFIG_LEDS_TRIGGERSstatic 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,};#endifstatic 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 # lsbrightness flash_strobe max_brightness power triggerdevice flash_timeout max_flash_timeout subsystem uevent
創(chuàng)建代碼:
@drivers/leds/led-class-flash.cstatic 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,};
注冊代碼
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); ……}
測試gpio閃光燈
echo 1 > /sys/class/leds/gpio-flash/flash_strobe
“注意,實際操作攝像頭閃光燈,并不是通過sysfs下的文件節(jié)點操作,而是通過v4l2架構(gòu)下發(fā)ioctl的命令來實現(xiàn)的
”
四、驅(qū)動解析
1. 結(jié)構(gòu)體和注冊函數(shù)
下面介紹led相關(guān)的重要的結(jié)構(gòu)體
struct led_classdev { const char *name; enum led_brightness brightness; //光強 enum led_brightness max_brightness; //最大光強 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è)置光強 /* * 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); //獲取光強 /* * 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ù)
注冊struct led_classdev結(jié)構(gòu)圖變量:
#define led_classdev_register(parent, led_cdev) \ of_led_classdev_register(parent, NULL, led_cdev)
對于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閃光燈注冊函數(shù):
int led_classdev_flash_register(struct device *parent, struct led_classdev_flash *fled_cdev)
2. gpio閃光燈sgm3141驅(qū)動詳解
看上圖:
- sgm3141驅(qū)動通過函數(shù)led_classdev_flash_register()->led_classdev_register()向led子系統(tǒng)注冊該設(shè)備
- sgm3141驅(qū)動通過函數(shù)v4l2_async_register_subdev()向v4l2子系統(tǒng)注冊該設(shè)備
- 如果用戶直接通過/sys/class/leds/gpio-flash/flash_strobe文件操作led燈,則會直接調(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
- 如果用戶的app拍照時操作閃光燈,則是通過v4l2子系統(tǒng)調(diào)用下發(fā)ioctl命令 命令序列:
V4L2_CID_FLASH_LED_MODE :設(shè)置led mod為 V4L2_FLASH_LED_MODE_TORCH(2),并點燈V4L2_CID_FLASH_LED_MODE:到達指定超時時間(2.7秒),設(shè)置led mod為 V4L2_FLASH_LED_MODE_NONE 0V4L2_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
- sgm驅(qū)動注冊流程分析 驅(qū)動架構(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, },};