[2/5] libcamera: Add support for camera flash devices
diff mbox series

Message ID 20250828-flash-support-v1-2-4c5dc674a05b@emfend.at
State New
Headers show
Series
  • Support for v4l2 flash devices
Related show

Commit Message

Matthias Fend Aug. 28, 2025, 1:09 p.m. UTC
Add a class to model camera flash devices. Currently, only v4l2 flash
devices are supported. The v4l2 flash devices are implemented similar to
the camera lenses.

Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
---
 include/libcamera/internal/camera_flash.h     |  75 ++++++++
 include/libcamera/internal/camera_sensor.h    |   2 +
 src/libcamera/camera_flash.cpp                | 248 ++++++++++++++++++++++++++
 src/libcamera/meson.build                     |   1 +
 src/libcamera/sensor/camera_sensor_legacy.cpp |  13 ++
 src/libcamera/sensor/camera_sensor_raw.cpp    |  13 ++
 6 files changed, 352 insertions(+)

Comments

Barnabás Pőcze Aug. 28, 2025, 1:44 p.m. UTC | #1
Hi

2025. 08. 28. 15:09 keltezéssel, Matthias Fend írta:
> Add a class to model camera flash devices. Currently, only v4l2 flash
> devices are supported. The v4l2 flash devices are implemented similar to
> the camera lenses.
> 
> Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
> ---
>   include/libcamera/internal/camera_flash.h     |  75 ++++++++
>   include/libcamera/internal/camera_sensor.h    |   2 +
>   src/libcamera/camera_flash.cpp                | 248 ++++++++++++++++++++++++++
>   src/libcamera/meson.build                     |   1 +
>   src/libcamera/sensor/camera_sensor_legacy.cpp |  13 ++
>   src/libcamera/sensor/camera_sensor_raw.cpp    |  13 ++
>   6 files changed, 352 insertions(+)
> 
> diff --git a/include/libcamera/internal/camera_flash.h b/include/libcamera/internal/camera_flash.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..e41afef2ab84852a340a12a012e3994f00cac27a
> --- /dev/null
> +++ b/include/libcamera/internal/camera_flash.h
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2025, matthias.fend@emfend.at
> + *
> + * Camera flash support
> + */
> +#pragma once
> +
> +#include <memory>
> +#include <stdint.h>
> +#include <string>
> +
> +#include <libcamera/base/class.h>
> +#include <libcamera/base/log.h>
> +
> +#include <libcamera/controls.h>
> +
> +namespace libcamera {
> +
> +class MediaEntity;
> +class V4L2Subdevice;
> +
> +class CameraFlash : protected Loggable
> +{
> +public:
> +	enum Mode {
> +		None,
> +		Flash,
> +		Torch,
> +	};
> +
> +	enum StrobeSource {
> +		Software,
> +		External,
> +	};

I think instead of defining these again, you should use the generated enums from
`control_ids.h` since the return values of `getMode()` and `getStrobeSource()` are
directly set as the value for `controls::draft::Flash{Mode,StrobeSource}`. Or at the
very least some `static_assert()`s would be good. But I think using the generated
enumerators would be preferable.


> +
> +	explicit CameraFlash(const MediaEntity *entity);
> +	~CameraFlash();
> +	int init();
> +	Mode getMode() const;
> +	int setMode(Mode mode);
> +	const ControlInfo &getFlashIntensityInfo() const;
> +	int32_t getFlashIntensity() const;
> +	int setFlashIntensity(int32_t intensity);
> +	const ControlInfo &getFlashTimeoutInfo() const;
> +	int32_t getFlashTimeout() const;
> +	int setFlashTimeout(int32_t timeout_us);
> +	StrobeSource getStrobeSource() const;
> +	int setStrobeSource(StrobeSource source);
> +	int startStrobe();
> +	int stopStrobe();
> +	const ControlInfo &getTorchIntensityInfo() const;
> +	int32_t getTorchIntensity() const;
> +	int setTorchIntensity(int32_t intensity);
> +
> +	const std::string &model() const;
> +	const ControlInfoMap &controls() const;
> +
> +protected:
> +	std::string logPrefix() const override;
> +
> +private:
> +	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraFlash)
> +
> +	int32_t getSubdevControl(uint32_t id) const;
> +	int setSubdevControl(uint32_t id, int32_t value);
> +	int validateDriver();
> +
> +	const MediaEntity *entity_;
> +	std::unique_ptr<V4L2Subdevice> subdev_;
> +	std::string model_;
> +	const ControlInfoMap *controlInfoMap_ = nullptr;

What is the motivation for caching this value?


> +};
> +
> +} /* namespace libcamera */
> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> index f6ef4df170d43500bc652762e9a575010a6c1cbd..faeecf244c8d42a43eca52ae04813e0c2f80e516 100644
> --- a/include/libcamera/internal/camera_sensor.h
> +++ b/include/libcamera/internal/camera_sensor.h
> @@ -28,6 +28,7 @@
>   
>   namespace libcamera {
>   
> +class CameraFlash;
>   class CameraLens;
>   class MediaEntity;
>   class SensorConfiguration;
> @@ -48,6 +49,7 @@ public:
>   	virtual V4L2Subdevice *device() = 0;
>   
>   	virtual CameraLens *focusLens() = 0;
> +	virtual CameraFlash *flash() = 0;
>   
>   	virtual const std::vector<unsigned int> &mbusCodes() const = 0;
>   	virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
> diff --git a/src/libcamera/camera_flash.cpp b/src/libcamera/camera_flash.cpp
> new file mode 100644
> index 0000000000000000000000000000000000000000..4702c590c91e0bcb20d173e7ce03608e1ae6ecfd
> --- /dev/null
> +++ b/src/libcamera/camera_flash.cpp
> @@ -0,0 +1,248 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2025, matthias.fend@emfend.at
> + *
> + * Camera flash support
> + */
> +
> +#include "libcamera/internal/camera_flash.h"
> +
> +#include <libcamera/base/utils.h>
> +
> +#include "libcamera/internal/v4l2_subdevice.h"
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(CameraFlash)
> +
> +CameraFlash::CameraFlash(const MediaEntity *entity)
> +	: entity_(entity)
> +{
> +}
> +
> +CameraFlash::~CameraFlash() = default;
> +
> +int CameraFlash::init()
> +{
> +	if (entity_->function() != MEDIA_ENT_F_FLASH) {
> +		LOG(CameraFlash, Error)
> +			<< "Invalid flash function "
> +			<< utils::hex(entity_->function());
> +		return -EINVAL;
> +	}
> +
> +	subdev_ = std::make_unique<V4L2Subdevice>(entity_);
> +	int ret = subdev_->open();
> +	if (ret < 0)
> +		return ret;
> +
> +	controlInfoMap_ = &subdev_->controls();
> +
> +	ret = validateDriver();
> +	if (ret)
> +		return ret;
> +
> +	model_ = subdev_->model();
> +
> +	return 0;
> +}
> +
> +CameraFlash::Mode CameraFlash::getMode() const
> +{
> +	Mode m;
> +
> +	switch (getSubdevControl(V4L2_CID_FLASH_LED_MODE)) {
> +	case V4L2_FLASH_LED_MODE_FLASH:
> +		m = Flash;
> +		break;
> +	case V4L2_FLASH_LED_MODE_TORCH:
> +		m = Torch;
> +		break;
> +	case V4L2_FLASH_LED_MODE_NONE:
> +	default:
> +		m = None;
> +		break;
> +	}
> +
> +	return m;
> +}
> +
> +int CameraFlash::setMode(Mode mode)
> +{
> +	int32_t m;
> +
> +	switch (mode) {
> +	case Flash:
> +		m = V4L2_FLASH_LED_MODE_FLASH;
> +		break;
> +	case Torch:
> +		m = V4L2_FLASH_LED_MODE_TORCH;
> +		break;
> +	case None:
> +		m = V4L2_FLASH_LED_MODE_NONE;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return setSubdevControl(V4L2_CID_FLASH_LED_MODE, m);
> +}
> +
> +const ControlInfo &CameraFlash::getFlashIntensityInfo() const
> +{
> +	return controlInfoMap_->find(V4L2_CID_FLASH_INTENSITY)->second;
> +}
> +
> +int32_t CameraFlash::getFlashIntensity() const
> +{
> +	return getSubdevControl(V4L2_CID_FLASH_INTENSITY);
> +}
> +
> +int CameraFlash::setFlashIntensity(int32_t intensity)
> +{
> +	return setSubdevControl(V4L2_CID_FLASH_INTENSITY, intensity);
> +}
> +
> +const ControlInfo &CameraFlash::getFlashTimeoutInfo() const
> +{
> +	return controlInfoMap_->find(V4L2_CID_FLASH_TIMEOUT)->second;
> +}
> +
> +int32_t CameraFlash::getFlashTimeout() const
> +{
> +	return getSubdevControl(V4L2_CID_FLASH_TIMEOUT);
> +}
> +
> +int CameraFlash::setFlashTimeout(int32_t timeout)
> +{
> +	return setSubdevControl(V4L2_CID_FLASH_TIMEOUT, timeout);
> +}
> +
> +CameraFlash::StrobeSource CameraFlash::getStrobeSource() const
> +{
> +	StrobeSource s;
> +
> +	switch (getSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE)) {
> +	case V4L2_FLASH_STROBE_SOURCE_EXTERNAL:
> +		s = External;
> +		break;
> +	case V4L2_FLASH_STROBE_SOURCE_SOFTWARE:
> +	default:
> +		s = Software;
> +		break;
> +	}
> +
> +	return s;
> +}
> +
> +int CameraFlash::setStrobeSource(StrobeSource source)
> +{
> +	int32_t s;
> +
> +	switch (source) {
> +	case External:
> +		s = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> +		break;
> +	case Software:
> +		s = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return setSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE, s);
> +}
> +
> +int CameraFlash::startStrobe()
> +{
> +	return setSubdevControl(V4L2_CID_FLASH_STROBE, 1);
> +}
> +
> +int CameraFlash::stopStrobe()
> +{
> +	return setSubdevControl(V4L2_CID_FLASH_STROBE_STOP, 1);
> +}
> +
> +const ControlInfo &CameraFlash::getTorchIntensityInfo() const
> +{
> +	return controlInfoMap_->find(V4L2_CID_FLASH_TORCH_INTENSITY)->second;
> +}
> +
> +int32_t CameraFlash::getTorchIntensity() const
> +{
> +	return getSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY);
> +}
> +
> +int CameraFlash::setTorchIntensity(int32_t intensity)
> +{
> +	return setSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY, intensity);
> +}
> +
> +const std::string &CameraFlash::model() const
> +{
> +	return model_;
> +}
> +
> +const ControlInfoMap &CameraFlash::controls() const
> +{
> +	return subdev_->controls();

Why not `controlInfoMap_` here?


> +}
> +
> +std::string CameraFlash::logPrefix() const
> +{
> +	return "'" + entity_->name() + "'";
> +}
> +
> +int32_t CameraFlash::getSubdevControl(uint32_t id) const
> +{
> +	ControlList controlList = subdev_->getControls(std::vector<uint32_t>{ id });

   getControls(std::array{ id })

should work.


> +
> +	return controlList.get(id).get<int32_t>();

I think some level of error checking would be useful when
querying the device.


Regards,
Barnabás Pőcze


> +}
> +
> +int CameraFlash::setSubdevControl(uint32_t id, int32_t value)
> +{
> +	ControlList flashCtrls(subdev_->controls());
> +
> +	flashCtrls.set(id, value);
> +
> +	if (subdev_->setControls(&flashCtrls))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +int CameraFlash::validateDriver()
> +{
> +	int ret = 0;
> +	static constexpr uint32_t mandatoryControls[] = {
> +		V4L2_CID_FLASH_LED_MODE,
> +		V4L2_CID_FLASH_STROBE_SOURCE,
> +		V4L2_CID_FLASH_STROBE,
> +		V4L2_CID_FLASH_TIMEOUT,
> +		V4L2_CID_FLASH_INTENSITY,
> +		V4L2_CID_FLASH_TORCH_INTENSITY,
> +	};
> +
> +	for (uint32_t ctrl : mandatoryControls) {
> +		if (!controlInfoMap_->count(ctrl)) {
> +			LOG(CameraFlash, Error)
> +				<< "Mandatory V4L2 control " << utils::hex(ctrl)
> +				<< " not available";
> +			ret = -EINVAL;
> +		}
> +	}
> +
> +	if (ret) {
> +		LOG(CameraFlash, Error)
> +			<< "The flash kernel driver needs to be fixed";
> +		LOG(CameraFlash, Error)
> +			<< "See Documentation/flash_driver_requirements.rst in"
> +			<< " the libcamera sources for more information";
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index b3ca27f217da4ba3a896ef7cbfb5502fa82a4907..0f125661a51e2431c1febc353cef30a1219f9ce7 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -20,6 +20,7 @@ libcamera_internal_sources = files([
>       'bayer_format.cpp',
>       'byte_stream_buffer.cpp',
>       'camera_controls.cpp',
> +    'camera_flash.cpp',
>       'camera_lens.cpp',
>       'clock_recovery.cpp',
>       'control_serializer.cpp',
> diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp
> index f9e685a9acc499fc91d51ed1d66780a0ad2d2a8f..632b66ea0aa15fcd654e7f0efb50c24cb9b973bf 100644
> --- a/src/libcamera/sensor/camera_sensor_legacy.cpp
> +++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
> @@ -31,6 +31,7 @@
>   #include <libcamera/ipa/core_ipa_interface.h>
>   
>   #include "libcamera/internal/bayer_format.h"
> +#include "libcamera/internal/camera_flash.h"
>   #include "libcamera/internal/camera_lens.h"
>   #include "libcamera/internal/camera_sensor.h"
>   #include "libcamera/internal/camera_sensor_properties.h"
> @@ -68,6 +69,7 @@ public:
>   	V4L2Subdevice *device() override { return subdev_.get(); }
>   
>   	CameraLens *focusLens() override { return focusLens_.get(); }
> +	CameraFlash *flash() override { return flash_.get(); }
>   
>   	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
>   	std::vector<Size> sizes(unsigned int mbusCode) const override;
> @@ -139,6 +141,7 @@ private:
>   	ControlList properties_;
>   
>   	std::unique_ptr<CameraLens> focusLens_;
> +	std::unique_ptr<CameraFlash> flash_;
>   };
>   
>   /**
> @@ -665,6 +668,16 @@ int CameraSensorLegacy::discoverAncillaryDevices()
>   			}
>   			break;
>   
> +		case MEDIA_ENT_F_FLASH:
> +			flash_ = std::make_unique<CameraFlash>(ancillary);
> +			ret = flash_->init();
> +			if (ret) {
> +				LOG(CameraSensor, Error)
> +					<< "Flash initialisation failed, flash disabled";
> +				flash_.reset();
> +			}
> +			break;
> +
>   		default:
>   			LOG(CameraSensor, Warning)
>   				<< "Unsupported ancillary entity function "
> diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp
> index 8ea4423698cd8c1eaae43eb5ba8b5d524b94d515..9d533d814b9df453aa4009a87818c1558bcbd665 100644
> --- a/src/libcamera/sensor/camera_sensor_raw.cpp
> +++ b/src/libcamera/sensor/camera_sensor_raw.cpp
> @@ -32,6 +32,7 @@
>   #include <libcamera/ipa/core_ipa_interface.h>
>   
>   #include "libcamera/internal/bayer_format.h"
> +#include "libcamera/internal/camera_flash.h"
>   #include "libcamera/internal/camera_lens.h"
>   #include "libcamera/internal/camera_sensor.h"
>   #include "libcamera/internal/camera_sensor_properties.h"
> @@ -69,6 +70,7 @@ public:
>   	V4L2Subdevice *device() override { return subdev_.get(); }
>   
>   	CameraLens *focusLens() override { return focusLens_.get(); }
> +	CameraFlash *flash() override { return flash_.get(); }
>   
>   	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
>   	std::vector<Size> sizes(unsigned int mbusCode) const override;
> @@ -150,6 +152,7 @@ private:
>   	ControlList properties_;
>   
>   	std::unique_ptr<CameraLens> focusLens_;
> +	std::unique_ptr<CameraFlash> flash_;
>   };
>   
>   /**
> @@ -513,6 +516,16 @@ std::optional<int> CameraSensorRaw::init()
>   			}
>   			break;
>   
> +		case MEDIA_ENT_F_FLASH:
> +			flash_ = std::make_unique<CameraFlash>(ancillary);
> +			ret = flash_->init();
> +			if (ret) {
> +				LOG(CameraSensor, Error)
> +					<< "Flash initialisation failed, flash disabled";
> +				flash_.reset();
> +			}
> +			break;
> +
>   		default:
>   			LOG(CameraSensor, Warning)
>   				<< "Unsupported ancillary entity function "
>
Matthias Fend Aug. 28, 2025, 2:30 p.m. UTC | #2
Hi Barnabás,

thanks a lot for your feedback!

Am 28.08.2025 um 15:44 schrieb Barnabás Pőcze:
> Hi
> 
> 2025. 08. 28. 15:09 keltezéssel, Matthias Fend írta:
>> Add a class to model camera flash devices. Currently, only v4l2 flash
>> devices are supported. The v4l2 flash devices are implemented similar to
>> the camera lenses.
>>
>> Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
>> ---
>>   include/libcamera/internal/camera_flash.h     |  75 ++++++++
>>   include/libcamera/internal/camera_sensor.h    |   2 +
>>   src/libcamera/camera_flash.cpp                | 248 ++++++++++++++++ 
>> ++++++++++
>>   src/libcamera/meson.build                     |   1 +
>>   src/libcamera/sensor/camera_sensor_legacy.cpp |  13 ++
>>   src/libcamera/sensor/camera_sensor_raw.cpp    |  13 ++
>>   6 files changed, 352 insertions(+)
>>
>> diff --git a/include/libcamera/internal/camera_flash.h b/include/ 
>> libcamera/internal/camera_flash.h
>> new file mode 100644
>> index 
>> 0000000000000000000000000000000000000000..e41afef2ab84852a340a12a012e3994f00cac27a
>> --- /dev/null
>> +++ b/include/libcamera/internal/camera_flash.h
>> @@ -0,0 +1,75 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2025, matthias.fend@emfend.at
>> + *
>> + * Camera flash support
>> + */
>> +#pragma once
>> +
>> +#include <memory>
>> +#include <stdint.h>
>> +#include <string>
>> +
>> +#include <libcamera/base/class.h>
>> +#include <libcamera/base/log.h>
>> +
>> +#include <libcamera/controls.h>
>> +
>> +namespace libcamera {
>> +
>> +class MediaEntity;
>> +class V4L2Subdevice;
>> +
>> +class CameraFlash : protected Loggable
>> +{
>> +public:
>> +    enum Mode {
>> +        None,
>> +        Flash,
>> +        Torch,
>> +    };
>> +
>> +    enum StrobeSource {
>> +        Software,
>> +        External,
>> +    };
> 
> I think instead of defining these again, you should use the generated 
> enums from
> `control_ids.h` since the return values of `getMode()` and 
> `getStrobeSource()` are
> directly set as the value for `controls::draft::Flash{Mode,StrobeSource} 
> `. Or at the
> very least some `static_assert()`s would be good. But I think using the 
> generated
> enumerators would be preferable.

I wanted to have a dedicated internal API here and not mix it with the 
external control API.
But you're right, for the controls in the metadata, the return values 
​​of getMode() and getStrobeSource() should also be mapped (as is 
already done for the incoming controls).

> 
> 
>> +
>> +    explicit CameraFlash(const MediaEntity *entity);
>> +    ~CameraFlash();
>> +    int init();
>> +    Mode getMode() const;
>> +    int setMode(Mode mode);
>> +    const ControlInfo &getFlashIntensityInfo() const;
>> +    int32_t getFlashIntensity() const;
>> +    int setFlashIntensity(int32_t intensity);
>> +    const ControlInfo &getFlashTimeoutInfo() const;
>> +    int32_t getFlashTimeout() const;
>> +    int setFlashTimeout(int32_t timeout_us);
>> +    StrobeSource getStrobeSource() const;
>> +    int setStrobeSource(StrobeSource source);
>> +    int startStrobe();
>> +    int stopStrobe();
>> +    const ControlInfo &getTorchIntensityInfo() const;
>> +    int32_t getTorchIntensity() const;
>> +    int setTorchIntensity(int32_t intensity);
>> +
>> +    const std::string &model() const;
>> +    const ControlInfoMap &controls() const;
>> +
>> +protected:
>> +    std::string logPrefix() const override;
>> +
>> +private:
>> +    LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraFlash)
>> +
>> +    int32_t getSubdevControl(uint32_t id) const;
>> +    int setSubdevControl(uint32_t id, int32_t value);
>> +    int validateDriver();
>> +
>> +    const MediaEntity *entity_;
>> +    std::unique_ptr<V4L2Subdevice> subdev_;
>> +    std::string model_;
>> +    const ControlInfoMap *controlInfoMap_ = nullptr;
> 
> What is the motivation for caching this value?

Just to make it a little easier to read and to avoid constructs like this:
subdev_->controls().find(V4L2_CID_FLASH_TORCH_INTENSITY)->second;

> 
> 
>> +};
>> +
>> +} /* namespace libcamera */
>> diff --git a/include/libcamera/internal/camera_sensor.h b/include/ 
>> libcamera/internal/camera_sensor.h
>> index 
>> f6ef4df170d43500bc652762e9a575010a6c1cbd..faeecf244c8d42a43eca52ae04813e0c2f80e516 100644
>> --- a/include/libcamera/internal/camera_sensor.h
>> +++ b/include/libcamera/internal/camera_sensor.h
>> @@ -28,6 +28,7 @@
>>   namespace libcamera {
>> +class CameraFlash;
>>   class CameraLens;
>>   class MediaEntity;
>>   class SensorConfiguration;
>> @@ -48,6 +49,7 @@ public:
>>       virtual V4L2Subdevice *device() = 0;
>>       virtual CameraLens *focusLens() = 0;
>> +    virtual CameraFlash *flash() = 0;
>>       virtual const std::vector<unsigned int> &mbusCodes() const = 0;
>>       virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
>> diff --git a/src/libcamera/camera_flash.cpp b/src/libcamera/ 
>> camera_flash.cpp
>> new file mode 100644
>> index 
>> 0000000000000000000000000000000000000000..4702c590c91e0bcb20d173e7ce03608e1ae6ecfd
>> --- /dev/null
>> +++ b/src/libcamera/camera_flash.cpp
>> @@ -0,0 +1,248 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2025, matthias.fend@emfend.at
>> + *
>> + * Camera flash support
>> + */
>> +
>> +#include "libcamera/internal/camera_flash.h"
>> +
>> +#include <libcamera/base/utils.h>
>> +
>> +#include "libcamera/internal/v4l2_subdevice.h"
>> +
>> +namespace libcamera {
>> +
>> +LOG_DEFINE_CATEGORY(CameraFlash)
>> +
>> +CameraFlash::CameraFlash(const MediaEntity *entity)
>> +    : entity_(entity)
>> +{
>> +}
>> +
>> +CameraFlash::~CameraFlash() = default;
>> +
>> +int CameraFlash::init()
>> +{
>> +    if (entity_->function() != MEDIA_ENT_F_FLASH) {
>> +        LOG(CameraFlash, Error)
>> +            << "Invalid flash function "
>> +            << utils::hex(entity_->function());
>> +        return -EINVAL;
>> +    }
>> +
>> +    subdev_ = std::make_unique<V4L2Subdevice>(entity_);
>> +    int ret = subdev_->open();
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    controlInfoMap_ = &subdev_->controls();
>> +
>> +    ret = validateDriver();
>> +    if (ret)
>> +        return ret;
>> +
>> +    model_ = subdev_->model();
>> +
>> +    return 0;
>> +}
>> +
>> +CameraFlash::Mode CameraFlash::getMode() const
>> +{
>> +    Mode m;
>> +
>> +    switch (getSubdevControl(V4L2_CID_FLASH_LED_MODE)) {
>> +    case V4L2_FLASH_LED_MODE_FLASH:
>> +        m = Flash;
>> +        break;
>> +    case V4L2_FLASH_LED_MODE_TORCH:
>> +        m = Torch;
>> +        break;
>> +    case V4L2_FLASH_LED_MODE_NONE:
>> +    default:
>> +        m = None;
>> +        break;
>> +    }
>> +
>> +    return m;
>> +}
>> +
>> +int CameraFlash::setMode(Mode mode)
>> +{
>> +    int32_t m;
>> +
>> +    switch (mode) {
>> +    case Flash:
>> +        m = V4L2_FLASH_LED_MODE_FLASH;
>> +        break;
>> +    case Torch:
>> +        m = V4L2_FLASH_LED_MODE_TORCH;
>> +        break;
>> +    case None:
>> +        m = V4L2_FLASH_LED_MODE_NONE;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +
>> +    return setSubdevControl(V4L2_CID_FLASH_LED_MODE, m);
>> +}
>> +
>> +const ControlInfo &CameraFlash::getFlashIntensityInfo() const
>> +{
>> +    return controlInfoMap_->find(V4L2_CID_FLASH_INTENSITY)->second;
>> +}
>> +
>> +int32_t CameraFlash::getFlashIntensity() const
>> +{
>> +    return getSubdevControl(V4L2_CID_FLASH_INTENSITY);
>> +}
>> +
>> +int CameraFlash::setFlashIntensity(int32_t intensity)
>> +{
>> +    return setSubdevControl(V4L2_CID_FLASH_INTENSITY, intensity);
>> +}
>> +
>> +const ControlInfo &CameraFlash::getFlashTimeoutInfo() const
>> +{
>> +    return controlInfoMap_->find(V4L2_CID_FLASH_TIMEOUT)->second;
>> +}
>> +
>> +int32_t CameraFlash::getFlashTimeout() const
>> +{
>> +    return getSubdevControl(V4L2_CID_FLASH_TIMEOUT);
>> +}
>> +
>> +int CameraFlash::setFlashTimeout(int32_t timeout)
>> +{
>> +    return setSubdevControl(V4L2_CID_FLASH_TIMEOUT, timeout);
>> +}
>> +
>> +CameraFlash::StrobeSource CameraFlash::getStrobeSource() const
>> +{
>> +    StrobeSource s;
>> +
>> +    switch (getSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE)) {
>> +    case V4L2_FLASH_STROBE_SOURCE_EXTERNAL:
>> +        s = External;
>> +        break;
>> +    case V4L2_FLASH_STROBE_SOURCE_SOFTWARE:
>> +    default:
>> +        s = Software;
>> +        break;
>> +    }
>> +
>> +    return s;
>> +}
>> +
>> +int CameraFlash::setStrobeSource(StrobeSource source)
>> +{
>> +    int32_t s;
>> +
>> +    switch (source) {
>> +    case External:
>> +        s = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>> +        break;
>> +    case Software:
>> +        s = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +
>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE, s);
>> +}
>> +
>> +int CameraFlash::startStrobe()
>> +{
>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE, 1);
>> +}
>> +
>> +int CameraFlash::stopStrobe()
>> +{
>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE_STOP, 1);
>> +}
>> +
>> +const ControlInfo &CameraFlash::getTorchIntensityInfo() const
>> +{
>> +    return controlInfoMap_->find(V4L2_CID_FLASH_TORCH_INTENSITY)- 
>> >second;
>> +}
>> +
>> +int32_t CameraFlash::getTorchIntensity() const
>> +{
>> +    return getSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY);
>> +}
>> +
>> +int CameraFlash::setTorchIntensity(int32_t intensity)
>> +{
>> +    return setSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY, intensity);
>> +}
>> +
>> +const std::string &CameraFlash::model() const
>> +{
>> +    return model_;
>> +}
>> +
>> +const ControlInfoMap &CameraFlash::controls() const
>> +{
>> +    return subdev_->controls();
> 
> Why not `controlInfoMap_` here?

True, if the variable already exists, it should probably always be used.

> 
> 
>> +}
>> +
>> +std::string CameraFlash::logPrefix() const
>> +{
>> +    return "'" + entity_->name() + "'";
>> +}
>> +
>> +int32_t CameraFlash::getSubdevControl(uint32_t id) const
>> +{
>> +    ControlList controlList = subdev_- 
>> >getControls(std::vector<uint32_t>{ id });
> 
>    getControls(std::array{ id })
> 
> should work.

ACK.

> 
> 
>> +
>> +    return controlList.get(id).get<int32_t>();
> 
> I think some level of error checking would be useful when
> querying the device.

Okay. Do you have anything specific in mind that could go wrong here 
that should be checked?

Thanks,
  ~Matthias

> 
> 
> Regards,
> Barnabás Pőcze
> 
> 
>> +}
>> +
>> +int CameraFlash::setSubdevControl(uint32_t id, int32_t value)
>> +{
>> +    ControlList flashCtrls(subdev_->controls());
>> +
>> +    flashCtrls.set(id, value);
>> +
>> +    if (subdev_->setControls(&flashCtrls))
>> +        return -EINVAL;
>> +
>> +    return 0;
>> +}
>> +
>> +int CameraFlash::validateDriver()
>> +{
>> +    int ret = 0;
>> +    static constexpr uint32_t mandatoryControls[] = {
>> +        V4L2_CID_FLASH_LED_MODE,
>> +        V4L2_CID_FLASH_STROBE_SOURCE,
>> +        V4L2_CID_FLASH_STROBE,
>> +        V4L2_CID_FLASH_TIMEOUT,
>> +        V4L2_CID_FLASH_INTENSITY,
>> +        V4L2_CID_FLASH_TORCH_INTENSITY,
>> +    };
>> +
>> +    for (uint32_t ctrl : mandatoryControls) {
>> +        if (!controlInfoMap_->count(ctrl)) {
>> +            LOG(CameraFlash, Error)
>> +                << "Mandatory V4L2 control " << utils::hex(ctrl)
>> +                << " not available";
>> +            ret = -EINVAL;
>> +        }
>> +    }
>> +
>> +    if (ret) {
>> +        LOG(CameraFlash, Error)
>> +            << "The flash kernel driver needs to be fixed";
>> +        LOG(CameraFlash, Error)
>> +            << "See Documentation/flash_driver_requirements.rst in"
>> +            << " the libcamera sources for more information";
>> +        return ret;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +} /* namespace libcamera */
>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
>> index 
>> b3ca27f217da4ba3a896ef7cbfb5502fa82a4907..0f125661a51e2431c1febc353cef30a1219f9ce7 100644
>> --- a/src/libcamera/meson.build
>> +++ b/src/libcamera/meson.build
>> @@ -20,6 +20,7 @@ libcamera_internal_sources = files([
>>       'bayer_format.cpp',
>>       'byte_stream_buffer.cpp',
>>       'camera_controls.cpp',
>> +    'camera_flash.cpp',
>>       'camera_lens.cpp',
>>       'clock_recovery.cpp',
>>       'control_serializer.cpp',
>> diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/ 
>> libcamera/sensor/camera_sensor_legacy.cpp
>> index 
>> f9e685a9acc499fc91d51ed1d66780a0ad2d2a8f..632b66ea0aa15fcd654e7f0efb50c24cb9b973bf 100644
>> --- a/src/libcamera/sensor/camera_sensor_legacy.cpp
>> +++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
>> @@ -31,6 +31,7 @@
>>   #include <libcamera/ipa/core_ipa_interface.h>
>>   #include "libcamera/internal/bayer_format.h"
>> +#include "libcamera/internal/camera_flash.h"
>>   #include "libcamera/internal/camera_lens.h"
>>   #include "libcamera/internal/camera_sensor.h"
>>   #include "libcamera/internal/camera_sensor_properties.h"
>> @@ -68,6 +69,7 @@ public:
>>       V4L2Subdevice *device() override { return subdev_.get(); }
>>       CameraLens *focusLens() override { return focusLens_.get(); }
>> +    CameraFlash *flash() override { return flash_.get(); }
>>       const std::vector<unsigned int> &mbusCodes() const override 
>> { return mbusCodes_; }
>>       std::vector<Size> sizes(unsigned int mbusCode) const override;
>> @@ -139,6 +141,7 @@ private:
>>       ControlList properties_;
>>       std::unique_ptr<CameraLens> focusLens_;
>> +    std::unique_ptr<CameraFlash> flash_;
>>   };
>>   /**
>> @@ -665,6 +668,16 @@ int CameraSensorLegacy::discoverAncillaryDevices()
>>               }
>>               break;
>> +        case MEDIA_ENT_F_FLASH:
>> +            flash_ = std::make_unique<CameraFlash>(ancillary);
>> +            ret = flash_->init();
>> +            if (ret) {
>> +                LOG(CameraSensor, Error)
>> +                    << "Flash initialisation failed, flash disabled";
>> +                flash_.reset();
>> +            }
>> +            break;
>> +
>>           default:
>>               LOG(CameraSensor, Warning)
>>                   << "Unsupported ancillary entity function "
>> diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/ 
>> libcamera/sensor/camera_sensor_raw.cpp
>> index 
>> 8ea4423698cd8c1eaae43eb5ba8b5d524b94d515..9d533d814b9df453aa4009a87818c1558bcbd665 100644
>> --- a/src/libcamera/sensor/camera_sensor_raw.cpp
>> +++ b/src/libcamera/sensor/camera_sensor_raw.cpp
>> @@ -32,6 +32,7 @@
>>   #include <libcamera/ipa/core_ipa_interface.h>
>>   #include "libcamera/internal/bayer_format.h"
>> +#include "libcamera/internal/camera_flash.h"
>>   #include "libcamera/internal/camera_lens.h"
>>   #include "libcamera/internal/camera_sensor.h"
>>   #include "libcamera/internal/camera_sensor_properties.h"
>> @@ -69,6 +70,7 @@ public:
>>       V4L2Subdevice *device() override { return subdev_.get(); }
>>       CameraLens *focusLens() override { return focusLens_.get(); }
>> +    CameraFlash *flash() override { return flash_.get(); }
>>       const std::vector<unsigned int> &mbusCodes() const override 
>> { return mbusCodes_; }
>>       std::vector<Size> sizes(unsigned int mbusCode) const override;
>> @@ -150,6 +152,7 @@ private:
>>       ControlList properties_;
>>       std::unique_ptr<CameraLens> focusLens_;
>> +    std::unique_ptr<CameraFlash> flash_;
>>   };
>>   /**
>> @@ -513,6 +516,16 @@ std::optional<int> CameraSensorRaw::init()
>>               }
>>               break;
>> +        case MEDIA_ENT_F_FLASH:
>> +            flash_ = std::make_unique<CameraFlash>(ancillary);
>> +            ret = flash_->init();
>> +            if (ret) {
>> +                LOG(CameraSensor, Error)
>> +                    << "Flash initialisation failed, flash disabled";
>> +                flash_.reset();
>> +            }
>> +            break;
>> +
>>           default:
>>               LOG(CameraSensor, Warning)
>>                   << "Unsupported ancillary entity function "
>>
>
Barnabás Pőcze Aug. 28, 2025, 2:51 p.m. UTC | #3
2025. 08. 28. 16:30 keltezéssel, Matthias Fend írta:
> Hi Barnabás,
> 
> thanks a lot for your feedback!
> 
> Am 28.08.2025 um 15:44 schrieb Barnabás Pőcze:
>> Hi
>>
>> 2025. 08. 28. 15:09 keltezéssel, Matthias Fend írta:
>>> Add a class to model camera flash devices. Currently, only v4l2 flash
>>> devices are supported. The v4l2 flash devices are implemented similar to
>>> the camera lenses.
>>>
>>> Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
>>> ---
>>>   include/libcamera/internal/camera_flash.h     |  75 ++++++++
>>>   include/libcamera/internal/camera_sensor.h    |   2 +
>>>   src/libcamera/camera_flash.cpp                | 248 ++++++++++++++++ ++++++++++
>>>   src/libcamera/meson.build                     |   1 +
>>>   src/libcamera/sensor/camera_sensor_legacy.cpp |  13 ++
>>>   src/libcamera/sensor/camera_sensor_raw.cpp    |  13 ++
>>>   6 files changed, 352 insertions(+)
>>>
>>> diff --git a/include/libcamera/internal/camera_flash.h b/include/ libcamera/internal/camera_flash.h
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..e41afef2ab84852a340a12a012e3994f00cac27a
>>> --- /dev/null
>>> +++ b/include/libcamera/internal/camera_flash.h
>>> @@ -0,0 +1,75 @@
>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>>> +/*
>>> + * Copyright (C) 2025, matthias.fend@emfend.at
>>> + *
>>> + * Camera flash support
>>> + */
>>> +#pragma once
>>> +
>>> +#include <memory>
>>> +#include <stdint.h>
>>> +#include <string>
>>> +
>>> +#include <libcamera/base/class.h>
>>> +#include <libcamera/base/log.h>
>>> +
>>> +#include <libcamera/controls.h>
>>> +
>>> +namespace libcamera {
>>> +
>>> +class MediaEntity;
>>> +class V4L2Subdevice;
>>> +
>>> +class CameraFlash : protected Loggable
>>> +{
>>> +public:
>>> +    enum Mode {
>>> +        None,
>>> +        Flash,
>>> +        Torch,
>>> +    };
>>> +
>>> +    enum StrobeSource {
>>> +        Software,
>>> +        External,
>>> +    };
>>
>> I think instead of defining these again, you should use the generated enums from
>> `control_ids.h` since the return values of `getMode()` and `getStrobeSource()` are
>> directly set as the value for `controls::draft::Flash{Mode,StrobeSource} `. Or at the
>> very least some `static_assert()`s would be good. But I think using the generated
>> enumerators would be preferable.
> 
> I wanted to have a dedicated internal API here and not mix it with the external control API.
> But you're right, for the controls in the metadata, the return values ​​of getMode() and getStrobeSource() should also be mapped (as is already done for the incoming controls).
> 

The `CameraSensor*` types also reuse `controls::draft::TestPatternModeEnum`, I don't see any
issues with it if you just end up duplicating the values. In any case, if you want the separate
enumerators, please try to make them `enum class`.


>>
>>
>>> +
>>> +    explicit CameraFlash(const MediaEntity *entity);
>>> +    ~CameraFlash();
>>> +    int init();
>>> +    Mode getMode() const;
>>> +    int setMode(Mode mode);
>>> +    const ControlInfo &getFlashIntensityInfo() const;
>>> +    int32_t getFlashIntensity() const;
>>> +    int setFlashIntensity(int32_t intensity);
>>> +    const ControlInfo &getFlashTimeoutInfo() const;
>>> +    int32_t getFlashTimeout() const;
>>> +    int setFlashTimeout(int32_t timeout_us);
>>> +    StrobeSource getStrobeSource() const;
>>> +    int setStrobeSource(StrobeSource source);
>>> +    int startStrobe();
>>> +    int stopStrobe();
>>> +    const ControlInfo &getTorchIntensityInfo() const;
>>> +    int32_t getTorchIntensity() const;
>>> +    int setTorchIntensity(int32_t intensity);
>>> +
>>> +    const std::string &model() const;
>>> +    const ControlInfoMap &controls() const;

Are these two used? In any case, I'd make the inline, they are quite trivial.


>>> +
>>> +protected:
>>> +    std::string logPrefix() const override;
>>> +
>>> +private:
>>> +    LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraFlash)
>>> +
>>> +    int32_t getSubdevControl(uint32_t id) const;
>>> +    int setSubdevControl(uint32_t id, int32_t value);
>>> +    int validateDriver();
>>> +
>>> +    const MediaEntity *entity_;
>>> +    std::unique_ptr<V4L2Subdevice> subdev_;
>>> +    std::string model_;
>>> +    const ControlInfoMap *controlInfoMap_ = nullptr;
>>
>> What is the motivation for caching this value?
> 
> Just to make it a little easier to read and to avoid constructs like this:
> subdev_->controls().find(V4L2_CID_FLASH_TORCH_INTENSITY)->second;

ACK. In any case, I think what you wrote or even just `controls().find(...)` is fine.


> 
>>
>>
>>> +};
>>> +
>>> +} /* namespace libcamera */
>>> diff --git a/include/libcamera/internal/camera_sensor.h b/include/ libcamera/internal/camera_sensor.h
>>> index f6ef4df170d43500bc652762e9a575010a6c1cbd..faeecf244c8d42a43eca52ae04813e0c2f80e516 100644
>>> --- a/include/libcamera/internal/camera_sensor.h
>>> +++ b/include/libcamera/internal/camera_sensor.h
>>> @@ -28,6 +28,7 @@
>>>   namespace libcamera {
>>> +class CameraFlash;
>>>   class CameraLens;
>>>   class MediaEntity;
>>>   class SensorConfiguration;
>>> @@ -48,6 +49,7 @@ public:
>>>       virtual V4L2Subdevice *device() = 0;
>>>       virtual CameraLens *focusLens() = 0;
>>> +    virtual CameraFlash *flash() = 0;
>>>       virtual const std::vector<unsigned int> &mbusCodes() const = 0;
>>>       virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
>>> diff --git a/src/libcamera/camera_flash.cpp b/src/libcamera/ camera_flash.cpp
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..4702c590c91e0bcb20d173e7ce03608e1ae6ecfd
>>> --- /dev/null
>>> +++ b/src/libcamera/camera_flash.cpp
>>> @@ -0,0 +1,248 @@
>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>>> +/*
>>> + * Copyright (C) 2025, matthias.fend@emfend.at
>>> + *
>>> + * Camera flash support
>>> + */
>>> +
>>> +#include "libcamera/internal/camera_flash.h"
>>> +
>>> +#include <libcamera/base/utils.h>
>>> +
>>> +#include "libcamera/internal/v4l2_subdevice.h"
>>> +
>>> +namespace libcamera {
>>> +
>>> +LOG_DEFINE_CATEGORY(CameraFlash)
>>> +
>>> +CameraFlash::CameraFlash(const MediaEntity *entity)
>>> +    : entity_(entity)
>>> +{
>>> +}
>>> +
>>> +CameraFlash::~CameraFlash() = default;
>>> +
>>> +int CameraFlash::init()
>>> +{
>>> +    if (entity_->function() != MEDIA_ENT_F_FLASH) {
>>> +        LOG(CameraFlash, Error)
>>> +            << "Invalid flash function "
>>> +            << utils::hex(entity_->function());
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    subdev_ = std::make_unique<V4L2Subdevice>(entity_);
>>> +    int ret = subdev_->open();
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    controlInfoMap_ = &subdev_->controls();
>>> +
>>> +    ret = validateDriver();
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    model_ = subdev_->model();
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +CameraFlash::Mode CameraFlash::getMode() const
>>> +{
>>> +    Mode m;
>>> +
>>> +    switch (getSubdevControl(V4L2_CID_FLASH_LED_MODE)) {
>>> +    case V4L2_FLASH_LED_MODE_FLASH:
>>> +        m = Flash;
>>> +        break;
>>> +    case V4L2_FLASH_LED_MODE_TORCH:
>>> +        m = Torch;
>>> +        break;
>>> +    case V4L2_FLASH_LED_MODE_NONE:
>>> +    default:
>>> +        m = None;
>>> +        break;
>>> +    }
>>> +
>>> +    return m;
>>> +}
>>> +
>>> +int CameraFlash::setMode(Mode mode)
>>> +{
>>> +    int32_t m;
>>> +
>>> +    switch (mode) {
>>> +    case Flash:
>>> +        m = V4L2_FLASH_LED_MODE_FLASH;
>>> +        break;
>>> +    case Torch:
>>> +        m = V4L2_FLASH_LED_MODE_TORCH;
>>> +        break;
>>> +    case None:
>>> +        m = V4L2_FLASH_LED_MODE_NONE;
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    return setSubdevControl(V4L2_CID_FLASH_LED_MODE, m);
>>> +}
>>> +
>>> +const ControlInfo &CameraFlash::getFlashIntensityInfo() const
>>> +{
>>> +    return controlInfoMap_->find(V4L2_CID_FLASH_INTENSITY)->second;
>>> +}
>>> +
>>> +int32_t CameraFlash::getFlashIntensity() const
>>> +{
>>> +    return getSubdevControl(V4L2_CID_FLASH_INTENSITY);
>>> +}
>>> +
>>> +int CameraFlash::setFlashIntensity(int32_t intensity)
>>> +{
>>> +    return setSubdevControl(V4L2_CID_FLASH_INTENSITY, intensity);
>>> +}
>>> +
>>> +const ControlInfo &CameraFlash::getFlashTimeoutInfo() const
>>> +{
>>> +    return controlInfoMap_->find(V4L2_CID_FLASH_TIMEOUT)->second;
>>> +}
>>> +
>>> +int32_t CameraFlash::getFlashTimeout() const
>>> +{
>>> +    return getSubdevControl(V4L2_CID_FLASH_TIMEOUT);
>>> +}
>>> +
>>> +int CameraFlash::setFlashTimeout(int32_t timeout)
>>> +{
>>> +    return setSubdevControl(V4L2_CID_FLASH_TIMEOUT, timeout);
>>> +}
>>> +
>>> +CameraFlash::StrobeSource CameraFlash::getStrobeSource() const
>>> +{
>>> +    StrobeSource s;
>>> +
>>> +    switch (getSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE)) {
>>> +    case V4L2_FLASH_STROBE_SOURCE_EXTERNAL:
>>> +        s = External;
>>> +        break;
>>> +    case V4L2_FLASH_STROBE_SOURCE_SOFTWARE:
>>> +    default:
>>> +        s = Software;
>>> +        break;
>>> +    }
>>> +
>>> +    return s;
>>> +}
>>> +
>>> +int CameraFlash::setStrobeSource(StrobeSource source)
>>> +{
>>> +    int32_t s;
>>> +
>>> +    switch (source) {
>>> +    case External:
>>> +        s = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>>> +        break;
>>> +    case Software:
>>> +        s = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE, s);
>>> +}
>>> +
>>> +int CameraFlash::startStrobe()
>>> +{
>>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE, 1);
>>> +}
>>> +
>>> +int CameraFlash::stopStrobe()
>>> +{
>>> +    return setSubdevControl(V4L2_CID_FLASH_STROBE_STOP, 1);
>>> +}
>>> +
>>> +const ControlInfo &CameraFlash::getTorchIntensityInfo() const
>>> +{
>>> +    return controlInfoMap_->find(V4L2_CID_FLASH_TORCH_INTENSITY)- >second;
>>> +}
>>> +
>>> +int32_t CameraFlash::getTorchIntensity() const
>>> +{
>>> +    return getSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY);
>>> +}
>>> +
>>> +int CameraFlash::setTorchIntensity(int32_t intensity)
>>> +{
>>> +    return setSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY, intensity);
>>> +}
>>> +
>>> +const std::string &CameraFlash::model() const
>>> +{
>>> +    return model_;
>>> +}
>>> +
>>> +const ControlInfoMap &CameraFlash::controls() const
>>> +{
>>> +    return subdev_->controls();
>>
>> Why not `controlInfoMap_` here?
> 
> True, if the variable already exists, it should probably always be used.
> 
>>
>>
>>> +}
>>> +
>>> +std::string CameraFlash::logPrefix() const
>>> +{
>>> +    return "'" + entity_->name() + "'";
>>> +}
>>> +
>>> +int32_t CameraFlash::getSubdevControl(uint32_t id) const
>>> +{
>>> +    ControlList controlList = subdev_- >getControls(std::vector<uint32_t>{ id });
>>
>>    getControls(std::array{ id })
>>
>> should work.
> 
> ACK.
> 
>>
>>
>>> +
>>> +    return controlList.get(id).get<int32_t>();
>>
>> I think some level of error checking would be useful when
>> querying the device.
> 
> Okay. Do you have anything specific in mind that could go wrong here that should be checked?

Something like `controlList.empty()` or `controlList.contains(id)` at least.
Possibly making it return `std::optional`.


Regards,
Barnabás Pőcze

> 
> Thanks,
>   ~Matthias
> 
>>
>>
>> Regards,
>> Barnabás Pőcze
>>
>>
>>> +}
>>> +
>>> +int CameraFlash::setSubdevControl(uint32_t id, int32_t value)
>>> +{
>>> +    ControlList flashCtrls(subdev_->controls());
>>> +
>>> +    flashCtrls.set(id, value);
>>> +
>>> +    if (subdev_->setControls(&flashCtrls))
>>> +        return -EINVAL;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +int CameraFlash::validateDriver()
>>> +{
>>> +    int ret = 0;
>>> +    static constexpr uint32_t mandatoryControls[] = {
>>> +        V4L2_CID_FLASH_LED_MODE,
>>> +        V4L2_CID_FLASH_STROBE_SOURCE,
>>> +        V4L2_CID_FLASH_STROBE,
>>> +        V4L2_CID_FLASH_TIMEOUT,
>>> +        V4L2_CID_FLASH_INTENSITY,
>>> +        V4L2_CID_FLASH_TORCH_INTENSITY,
>>> +    };
>>> +
>>> +    for (uint32_t ctrl : mandatoryControls) {
>>> +        if (!controlInfoMap_->count(ctrl)) {
>>> +            LOG(CameraFlash, Error)
>>> +                << "Mandatory V4L2 control " << utils::hex(ctrl)
>>> +                << " not available";
>>> +            ret = -EINVAL;
>>> +        }
>>> +    }
>>> +
>>> +    if (ret) {
>>> +        LOG(CameraFlash, Error)
>>> +            << "The flash kernel driver needs to be fixed";
>>> +        LOG(CameraFlash, Error)
>>> +            << "See Documentation/flash_driver_requirements.rst in"
>>> +            << " the libcamera sources for more information";
>>> +        return ret;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +} /* namespace libcamera */
>>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
>>> index b3ca27f217da4ba3a896ef7cbfb5502fa82a4907..0f125661a51e2431c1febc353cef30a1219f9ce7 100644
>>> --- a/src/libcamera/meson.build
>>> +++ b/src/libcamera/meson.build
>>> @@ -20,6 +20,7 @@ libcamera_internal_sources = files([
>>>       'bayer_format.cpp',
>>>       'byte_stream_buffer.cpp',
>>>       'camera_controls.cpp',
>>> +    'camera_flash.cpp',
>>>       'camera_lens.cpp',
>>>       'clock_recovery.cpp',
>>>       'control_serializer.cpp',
>>> diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/ libcamera/sensor/camera_sensor_legacy.cpp
>>> index f9e685a9acc499fc91d51ed1d66780a0ad2d2a8f..632b66ea0aa15fcd654e7f0efb50c24cb9b973bf 100644
>>> --- a/src/libcamera/sensor/camera_sensor_legacy.cpp
>>> +++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
>>> @@ -31,6 +31,7 @@
>>>   #include <libcamera/ipa/core_ipa_interface.h>
>>>   #include "libcamera/internal/bayer_format.h"
>>> +#include "libcamera/internal/camera_flash.h"
>>>   #include "libcamera/internal/camera_lens.h"
>>>   #include "libcamera/internal/camera_sensor.h"
>>>   #include "libcamera/internal/camera_sensor_properties.h"
>>> @@ -68,6 +69,7 @@ public:
>>>       V4L2Subdevice *device() override { return subdev_.get(); }
>>>       CameraLens *focusLens() override { return focusLens_.get(); }
>>> +    CameraFlash *flash() override { return flash_.get(); }
>>>       const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
>>>       std::vector<Size> sizes(unsigned int mbusCode) const override;
>>> @@ -139,6 +141,7 @@ private:
>>>       ControlList properties_;
>>>       std::unique_ptr<CameraLens> focusLens_;
>>> +    std::unique_ptr<CameraFlash> flash_;
>>>   };
>>>   /**
>>> @@ -665,6 +668,16 @@ int CameraSensorLegacy::discoverAncillaryDevices()
>>>               }
>>>               break;
>>> +        case MEDIA_ENT_F_FLASH:
>>> +            flash_ = std::make_unique<CameraFlash>(ancillary);
>>> +            ret = flash_->init();
>>> +            if (ret) {
>>> +                LOG(CameraSensor, Error)
>>> +                    << "Flash initialisation failed, flash disabled";
>>> +                flash_.reset();
>>> +            }
>>> +            break;
>>> +
>>>           default:
>>>               LOG(CameraSensor, Warning)
>>>                   << "Unsupported ancillary entity function "
>>> diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/ libcamera/sensor/camera_sensor_raw.cpp
>>> index 8ea4423698cd8c1eaae43eb5ba8b5d524b94d515..9d533d814b9df453aa4009a87818c1558bcbd665 100644
>>> --- a/src/libcamera/sensor/camera_sensor_raw.cpp
>>> +++ b/src/libcamera/sensor/camera_sensor_raw.cpp
>>> @@ -32,6 +32,7 @@
>>>   #include <libcamera/ipa/core_ipa_interface.h>
>>>   #include "libcamera/internal/bayer_format.h"
>>> +#include "libcamera/internal/camera_flash.h"
>>>   #include "libcamera/internal/camera_lens.h"
>>>   #include "libcamera/internal/camera_sensor.h"
>>>   #include "libcamera/internal/camera_sensor_properties.h"
>>> @@ -69,6 +70,7 @@ public:
>>>       V4L2Subdevice *device() override { return subdev_.get(); }
>>>       CameraLens *focusLens() override { return focusLens_.get(); }
>>> +    CameraFlash *flash() override { return flash_.get(); }
>>>       const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
>>>       std::vector<Size> sizes(unsigned int mbusCode) const override;
>>> @@ -150,6 +152,7 @@ private:
>>>       ControlList properties_;
>>>       std::unique_ptr<CameraLens> focusLens_;
>>> +    std::unique_ptr<CameraFlash> flash_;
>>>   };
>>>   /**
>>> @@ -513,6 +516,16 @@ std::optional<int> CameraSensorRaw::init()
>>>               }
>>>               break;
>>> +        case MEDIA_ENT_F_FLASH:
>>> +            flash_ = std::make_unique<CameraFlash>(ancillary);
>>> +            ret = flash_->init();
>>> +            if (ret) {
>>> +                LOG(CameraSensor, Error)
>>> +                    << "Flash initialisation failed, flash disabled";
>>> +                flash_.reset();
>>> +            }
>>> +            break;
>>> +
>>>           default:
>>>               LOG(CameraSensor, Warning)
>>>                   << "Unsupported ancillary entity function "
>>>
>>
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/camera_flash.h b/include/libcamera/internal/camera_flash.h
new file mode 100644
index 0000000000000000000000000000000000000000..e41afef2ab84852a340a12a012e3994f00cac27a
--- /dev/null
+++ b/include/libcamera/internal/camera_flash.h
@@ -0,0 +1,75 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, matthias.fend@emfend.at
+ *
+ * Camera flash support
+ */
+#pragma once
+
+#include <memory>
+#include <stdint.h>
+#include <string>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/controls.h>
+
+namespace libcamera {
+
+class MediaEntity;
+class V4L2Subdevice;
+
+class CameraFlash : protected Loggable
+{
+public:
+	enum Mode {
+		None,
+		Flash,
+		Torch,
+	};
+
+	enum StrobeSource {
+		Software,
+		External,
+	};
+
+	explicit CameraFlash(const MediaEntity *entity);
+	~CameraFlash();
+	int init();
+	Mode getMode() const;
+	int setMode(Mode mode);
+	const ControlInfo &getFlashIntensityInfo() const;
+	int32_t getFlashIntensity() const;
+	int setFlashIntensity(int32_t intensity);
+	const ControlInfo &getFlashTimeoutInfo() const;
+	int32_t getFlashTimeout() const;
+	int setFlashTimeout(int32_t timeout_us);
+	StrobeSource getStrobeSource() const;
+	int setStrobeSource(StrobeSource source);
+	int startStrobe();
+	int stopStrobe();
+	const ControlInfo &getTorchIntensityInfo() const;
+	int32_t getTorchIntensity() const;
+	int setTorchIntensity(int32_t intensity);
+
+	const std::string &model() const;
+	const ControlInfoMap &controls() const;
+
+protected:
+	std::string logPrefix() const override;
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraFlash)
+
+	int32_t getSubdevControl(uint32_t id) const;
+	int setSubdevControl(uint32_t id, int32_t value);
+	int validateDriver();
+
+	const MediaEntity *entity_;
+	std::unique_ptr<V4L2Subdevice> subdev_;
+	std::string model_;
+	const ControlInfoMap *controlInfoMap_ = nullptr;
+};
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
index f6ef4df170d43500bc652762e9a575010a6c1cbd..faeecf244c8d42a43eca52ae04813e0c2f80e516 100644
--- a/include/libcamera/internal/camera_sensor.h
+++ b/include/libcamera/internal/camera_sensor.h
@@ -28,6 +28,7 @@ 
 
 namespace libcamera {
 
+class CameraFlash;
 class CameraLens;
 class MediaEntity;
 class SensorConfiguration;
@@ -48,6 +49,7 @@  public:
 	virtual V4L2Subdevice *device() = 0;
 
 	virtual CameraLens *focusLens() = 0;
+	virtual CameraFlash *flash() = 0;
 
 	virtual const std::vector<unsigned int> &mbusCodes() const = 0;
 	virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
diff --git a/src/libcamera/camera_flash.cpp b/src/libcamera/camera_flash.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4702c590c91e0bcb20d173e7ce03608e1ae6ecfd
--- /dev/null
+++ b/src/libcamera/camera_flash.cpp
@@ -0,0 +1,248 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, matthias.fend@emfend.at
+ *
+ * Camera flash support
+ */
+
+#include "libcamera/internal/camera_flash.h"
+
+#include <libcamera/base/utils.h>
+
+#include "libcamera/internal/v4l2_subdevice.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(CameraFlash)
+
+CameraFlash::CameraFlash(const MediaEntity *entity)
+	: entity_(entity)
+{
+}
+
+CameraFlash::~CameraFlash() = default;
+
+int CameraFlash::init()
+{
+	if (entity_->function() != MEDIA_ENT_F_FLASH) {
+		LOG(CameraFlash, Error)
+			<< "Invalid flash function "
+			<< utils::hex(entity_->function());
+		return -EINVAL;
+	}
+
+	subdev_ = std::make_unique<V4L2Subdevice>(entity_);
+	int ret = subdev_->open();
+	if (ret < 0)
+		return ret;
+
+	controlInfoMap_ = &subdev_->controls();
+
+	ret = validateDriver();
+	if (ret)
+		return ret;
+
+	model_ = subdev_->model();
+
+	return 0;
+}
+
+CameraFlash::Mode CameraFlash::getMode() const
+{
+	Mode m;
+
+	switch (getSubdevControl(V4L2_CID_FLASH_LED_MODE)) {
+	case V4L2_FLASH_LED_MODE_FLASH:
+		m = Flash;
+		break;
+	case V4L2_FLASH_LED_MODE_TORCH:
+		m = Torch;
+		break;
+	case V4L2_FLASH_LED_MODE_NONE:
+	default:
+		m = None;
+		break;
+	}
+
+	return m;
+}
+
+int CameraFlash::setMode(Mode mode)
+{
+	int32_t m;
+
+	switch (mode) {
+	case Flash:
+		m = V4L2_FLASH_LED_MODE_FLASH;
+		break;
+	case Torch:
+		m = V4L2_FLASH_LED_MODE_TORCH;
+		break;
+	case None:
+		m = V4L2_FLASH_LED_MODE_NONE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return setSubdevControl(V4L2_CID_FLASH_LED_MODE, m);
+}
+
+const ControlInfo &CameraFlash::getFlashIntensityInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_INTENSITY)->second;
+}
+
+int32_t CameraFlash::getFlashIntensity() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_INTENSITY);
+}
+
+int CameraFlash::setFlashIntensity(int32_t intensity)
+{
+	return setSubdevControl(V4L2_CID_FLASH_INTENSITY, intensity);
+}
+
+const ControlInfo &CameraFlash::getFlashTimeoutInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_TIMEOUT)->second;
+}
+
+int32_t CameraFlash::getFlashTimeout() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_TIMEOUT);
+}
+
+int CameraFlash::setFlashTimeout(int32_t timeout)
+{
+	return setSubdevControl(V4L2_CID_FLASH_TIMEOUT, timeout);
+}
+
+CameraFlash::StrobeSource CameraFlash::getStrobeSource() const
+{
+	StrobeSource s;
+
+	switch (getSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE)) {
+	case V4L2_FLASH_STROBE_SOURCE_EXTERNAL:
+		s = External;
+		break;
+	case V4L2_FLASH_STROBE_SOURCE_SOFTWARE:
+	default:
+		s = Software;
+		break;
+	}
+
+	return s;
+}
+
+int CameraFlash::setStrobeSource(StrobeSource source)
+{
+	int32_t s;
+
+	switch (source) {
+	case External:
+		s = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+		break;
+	case Software:
+		s = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return setSubdevControl(V4L2_CID_FLASH_STROBE_SOURCE, s);
+}
+
+int CameraFlash::startStrobe()
+{
+	return setSubdevControl(V4L2_CID_FLASH_STROBE, 1);
+}
+
+int CameraFlash::stopStrobe()
+{
+	return setSubdevControl(V4L2_CID_FLASH_STROBE_STOP, 1);
+}
+
+const ControlInfo &CameraFlash::getTorchIntensityInfo() const
+{
+	return controlInfoMap_->find(V4L2_CID_FLASH_TORCH_INTENSITY)->second;
+}
+
+int32_t CameraFlash::getTorchIntensity() const
+{
+	return getSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY);
+}
+
+int CameraFlash::setTorchIntensity(int32_t intensity)
+{
+	return setSubdevControl(V4L2_CID_FLASH_TORCH_INTENSITY, intensity);
+}
+
+const std::string &CameraFlash::model() const
+{
+	return model_;
+}
+
+const ControlInfoMap &CameraFlash::controls() const
+{
+	return subdev_->controls();
+}
+
+std::string CameraFlash::logPrefix() const
+{
+	return "'" + entity_->name() + "'";
+}
+
+int32_t CameraFlash::getSubdevControl(uint32_t id) const
+{
+	ControlList controlList = subdev_->getControls(std::vector<uint32_t>{ id });
+
+	return controlList.get(id).get<int32_t>();
+}
+
+int CameraFlash::setSubdevControl(uint32_t id, int32_t value)
+{
+	ControlList flashCtrls(subdev_->controls());
+
+	flashCtrls.set(id, value);
+
+	if (subdev_->setControls(&flashCtrls))
+		return -EINVAL;
+
+	return 0;
+}
+
+int CameraFlash::validateDriver()
+{
+	int ret = 0;
+	static constexpr uint32_t mandatoryControls[] = {
+		V4L2_CID_FLASH_LED_MODE,
+		V4L2_CID_FLASH_STROBE_SOURCE,
+		V4L2_CID_FLASH_STROBE,
+		V4L2_CID_FLASH_TIMEOUT,
+		V4L2_CID_FLASH_INTENSITY,
+		V4L2_CID_FLASH_TORCH_INTENSITY,
+	};
+
+	for (uint32_t ctrl : mandatoryControls) {
+		if (!controlInfoMap_->count(ctrl)) {
+			LOG(CameraFlash, Error)
+				<< "Mandatory V4L2 control " << utils::hex(ctrl)
+				<< " not available";
+			ret = -EINVAL;
+		}
+	}
+
+	if (ret) {
+		LOG(CameraFlash, Error)
+			<< "The flash kernel driver needs to be fixed";
+		LOG(CameraFlash, Error)
+			<< "See Documentation/flash_driver_requirements.rst in"
+			<< " the libcamera sources for more information";
+		return ret;
+	}
+
+	return ret;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index b3ca27f217da4ba3a896ef7cbfb5502fa82a4907..0f125661a51e2431c1febc353cef30a1219f9ce7 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -20,6 +20,7 @@  libcamera_internal_sources = files([
     'bayer_format.cpp',
     'byte_stream_buffer.cpp',
     'camera_controls.cpp',
+    'camera_flash.cpp',
     'camera_lens.cpp',
     'clock_recovery.cpp',
     'control_serializer.cpp',
diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp
index f9e685a9acc499fc91d51ed1d66780a0ad2d2a8f..632b66ea0aa15fcd654e7f0efb50c24cb9b973bf 100644
--- a/src/libcamera/sensor/camera_sensor_legacy.cpp
+++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
@@ -31,6 +31,7 @@ 
 #include <libcamera/ipa/core_ipa_interface.h>
 
 #include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_flash.h"
 #include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/camera_sensor_properties.h"
@@ -68,6 +69,7 @@  public:
 	V4L2Subdevice *device() override { return subdev_.get(); }
 
 	CameraLens *focusLens() override { return focusLens_.get(); }
+	CameraFlash *flash() override { return flash_.get(); }
 
 	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
 	std::vector<Size> sizes(unsigned int mbusCode) const override;
@@ -139,6 +141,7 @@  private:
 	ControlList properties_;
 
 	std::unique_ptr<CameraLens> focusLens_;
+	std::unique_ptr<CameraFlash> flash_;
 };
 
 /**
@@ -665,6 +668,16 @@  int CameraSensorLegacy::discoverAncillaryDevices()
 			}
 			break;
 
+		case MEDIA_ENT_F_FLASH:
+			flash_ = std::make_unique<CameraFlash>(ancillary);
+			ret = flash_->init();
+			if (ret) {
+				LOG(CameraSensor, Error)
+					<< "Flash initialisation failed, flash disabled";
+				flash_.reset();
+			}
+			break;
+
 		default:
 			LOG(CameraSensor, Warning)
 				<< "Unsupported ancillary entity function "
diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp
index 8ea4423698cd8c1eaae43eb5ba8b5d524b94d515..9d533d814b9df453aa4009a87818c1558bcbd665 100644
--- a/src/libcamera/sensor/camera_sensor_raw.cpp
+++ b/src/libcamera/sensor/camera_sensor_raw.cpp
@@ -32,6 +32,7 @@ 
 #include <libcamera/ipa/core_ipa_interface.h>
 
 #include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_flash.h"
 #include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/camera_sensor_properties.h"
@@ -69,6 +70,7 @@  public:
 	V4L2Subdevice *device() override { return subdev_.get(); }
 
 	CameraLens *focusLens() override { return focusLens_.get(); }
+	CameraFlash *flash() override { return flash_.get(); }
 
 	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
 	std::vector<Size> sizes(unsigned int mbusCode) const override;
@@ -150,6 +152,7 @@  private:
 	ControlList properties_;
 
 	std::unique_ptr<CameraLens> focusLens_;
+	std::unique_ptr<CameraFlash> flash_;
 };
 
 /**
@@ -513,6 +516,16 @@  std::optional<int> CameraSensorRaw::init()
 			}
 			break;
 
+		case MEDIA_ENT_F_FLASH:
+			flash_ = std::make_unique<CameraFlash>(ancillary);
+			ret = flash_->init();
+			if (ret) {
+				LOG(CameraSensor, Error)
+					<< "Flash initialisation failed, flash disabled";
+				flash_.reset();
+			}
+			break;
+
 		default:
 			LOG(CameraSensor, Warning)
 				<< "Unsupported ancillary entity function "