| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| /* |
| * Copyright (C) 2019, Google Inc. |
| * |
| * v4l2_subdevice.cpp - V4L2 Subdevice |
| */ |
| |
| #include "libcamera/internal/v4l2_subdevice.h" |
| |
| #include <fcntl.h> |
| #include <iomanip> |
| #include <regex> |
| #include <sstream> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| |
| #include <linux/media-bus-format.h> |
| #include <linux/v4l2-subdev.h> |
| |
| #include <libcamera/geometry.h> |
| |
| #include <libcamera/base/log.h> |
| #include <libcamera/base/utils.h> |
| |
| #include "libcamera/internal/media_device.h" |
| #include "libcamera/internal/media_object.h" |
| |
| /** |
| * \file v4l2_subdevice.h |
| * \brief V4L2 Subdevice API |
| */ |
| |
| namespace libcamera { |
| |
| LOG_DECLARE_CATEGORY(V4L2) |
| |
| namespace { |
| |
| /* |
| * \struct V4L2SubdeviceFormatInfo |
| * \brief Information about media bus formats |
| * \param bitsPerPixel Bits per pixel |
| * \param name Name of MBUS format |
| */ |
| struct V4L2SubdeviceFormatInfo { |
| unsigned int bitsPerPixel; |
| const char *name; |
| }; |
| |
| /* |
| * \var formatInfoMap |
| * \brief A map that associates V4L2SubdeviceFormatInfo struct to V4L2 media |
| * bus codes |
| */ |
| const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = { |
| { V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, { 16, "RGB444_2X8_PADHI_BE" } }, |
| { V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, { 16, "RGB444_2X8_PADHI_LE" } }, |
| { V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, { 16, "RGB555_2X8_PADHI_BE" } }, |
| { V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, { 16, "RGB555_2X8_PADHI_LE" } }, |
| { V4L2_MBUS_FMT_BGR565_2X8_BE, { 16, "BGR565_2X8_BE" } }, |
| { V4L2_MBUS_FMT_BGR565_2X8_LE, { 16, "BGR565_2X8_LE" } }, |
| { V4L2_MBUS_FMT_RGB565_2X8_BE, { 16, "RGB565_2X8_BE" } }, |
| { V4L2_MBUS_FMT_RGB565_2X8_LE, { 16, "RGB565_2X8_LE" } }, |
| { V4L2_MBUS_FMT_RGB666_1X18, { 18, "RGB666_1X18" } }, |
| { V4L2_MBUS_FMT_RGB888_1X24, { 24, "RGB888_1X24" } }, |
| { V4L2_MBUS_FMT_RGB888_2X12_BE, { 24, "RGB888_2X12_BE" } }, |
| { V4L2_MBUS_FMT_RGB888_2X12_LE, { 24, "RGB888_2X12_LE" } }, |
| { V4L2_MBUS_FMT_ARGB8888_1X32, { 32, "ARGB8888_1X32" } }, |
| { V4L2_MBUS_FMT_Y8_1X8, { 8, "Y8_1X8" } }, |
| { V4L2_MBUS_FMT_UV8_1X8, { 8, "UV8_1X8" } }, |
| { V4L2_MBUS_FMT_UYVY8_1_5X8, { 12, "UYVY8_1_5X8" } }, |
| { V4L2_MBUS_FMT_VYUY8_1_5X8, { 12, "VYUY8_1_5X8" } }, |
| { V4L2_MBUS_FMT_YUYV8_1_5X8, { 12, "YUYV8_1_5X8" } }, |
| { V4L2_MBUS_FMT_YVYU8_1_5X8, { 12, "YVYU8_1_5X8" } }, |
| { V4L2_MBUS_FMT_UYVY8_2X8, { 16, "UYVY8_2X8" } }, |
| { V4L2_MBUS_FMT_VYUY8_2X8, { 16, "VYUY8_2X8" } }, |
| { V4L2_MBUS_FMT_YUYV8_2X8, { 16, "YUYV8_2X8" } }, |
| { V4L2_MBUS_FMT_YVYU8_2X8, { 16, "YVYU8_2X8" } }, |
| { V4L2_MBUS_FMT_Y10_1X10, { 10, "Y10_1X10" } }, |
| { V4L2_MBUS_FMT_UYVY10_2X10, { 20, "UYVY10_2X10" } }, |
| { V4L2_MBUS_FMT_VYUY10_2X10, { 20, "VYUY10_2X10" } }, |
| { V4L2_MBUS_FMT_YUYV10_2X10, { 20, "YUYV10_2X10" } }, |
| { V4L2_MBUS_FMT_YVYU10_2X10, { 20, "YVYU10_2X10" } }, |
| { V4L2_MBUS_FMT_Y12_1X12, { 12, "Y12_1X12" } }, |
| { V4L2_MBUS_FMT_UYVY8_1X16, { 16, "UYVY8_1X16" } }, |
| { V4L2_MBUS_FMT_VYUY8_1X16, { 16, "VYUY8_1X16" } }, |
| { V4L2_MBUS_FMT_YUYV8_1X16, { 16, "YUYV8_1X16" } }, |
| { V4L2_MBUS_FMT_YVYU8_1X16, { 16, "YVYU8_1X16" } }, |
| { V4L2_MBUS_FMT_YDYUYDYV8_1X16, { 16, "YDYUYDYV8_1X16" } }, |
| { V4L2_MBUS_FMT_UYVY10_1X20, { 20, "UYVY10_1X20" } }, |
| { V4L2_MBUS_FMT_VYUY10_1X20, { 20, "VYUY10_1X20" } }, |
| { V4L2_MBUS_FMT_YUYV10_1X20, { 20, "YUYV10_1X20" } }, |
| { V4L2_MBUS_FMT_YVYU10_1X20, { 20, "YVYU10_1X20" } }, |
| { V4L2_MBUS_FMT_YUV10_1X30, { 30, "YUV10_1X30" } }, |
| { V4L2_MBUS_FMT_AYUV8_1X32, { 32, "AYUV8_1X32" } }, |
| { V4L2_MBUS_FMT_UYVY12_2X12, { 24, "UYVY12_2X12" } }, |
| { V4L2_MBUS_FMT_VYUY12_2X12, { 24, "VYUY12_2X12" } }, |
| { V4L2_MBUS_FMT_YUYV12_2X12, { 24, "YUYV12_2X12" } }, |
| { V4L2_MBUS_FMT_YVYU12_2X12, { 24, "YVYU12_2X12" } }, |
| { V4L2_MBUS_FMT_UYVY12_1X24, { 24, "UYVY12_1X24" } }, |
| { V4L2_MBUS_FMT_VYUY12_1X24, { 24, "VYUY12_1X24" } }, |
| { V4L2_MBUS_FMT_YUYV12_1X24, { 24, "YUYV12_1X24" } }, |
| { V4L2_MBUS_FMT_YVYU12_1X24, { 24, "YVYU12_1X24" } }, |
| { V4L2_MBUS_FMT_SBGGR8_1X8, { 8, "SBGGR8_1X8" } }, |
| { V4L2_MBUS_FMT_SGBRG8_1X8, { 8, "SGBRG8_1X8" } }, |
| { V4L2_MBUS_FMT_SGRBG8_1X8, { 8, "SGRBG8_1X8" } }, |
| { V4L2_MBUS_FMT_SRGGB8_1X8, { 8, "SRGGB8_1X8" } }, |
| { V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8, { 8, "SBGGR10_ALAW8_1X8" } }, |
| { V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8, { 8, "SGBRG10_ALAW8_1X8" } }, |
| { V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, { 8, "SGRBG10_ALAW8_1X8" } }, |
| { V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8, { 8, "SRGGB10_ALAW8_1X8" } }, |
| { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, { 8, "SBGGR10_DPCM8_1X8" } }, |
| { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, { 8, "SGBRG10_DPCM8_1X8" } }, |
| { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, { 8, "SGRBG10_DPCM8_1X8" } }, |
| { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, { 8, "SRGGB10_DPCM8_1X8" } }, |
| { V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, { 16, "SBGGR10_2X8_PADHI_BE" } }, |
| { V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, { 16, "SBGGR10_2X8_PADHI_LE" } }, |
| { V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, { 16, "SBGGR10_2X8_PADLO_BE" } }, |
| { V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, { 16, "SBGGR10_2X8_PADLO_LE" } }, |
| { V4L2_MBUS_FMT_SBGGR10_1X10, { 10, "SBGGR10_1X10" } }, |
| { V4L2_MBUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10" } }, |
| { V4L2_MBUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10" } }, |
| { V4L2_MBUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10" } }, |
| { V4L2_MBUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12" } }, |
| { V4L2_MBUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12" } }, |
| { V4L2_MBUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12" } }, |
| { V4L2_MBUS_FMT_SRGGB12_1X12, { 12, "SRGGB12_1X12" } }, |
| { V4L2_MBUS_FMT_AHSV8888_1X32, { 32, "AHSV8888_1X32" } }, |
| }; |
| |
| } /* namespace */ |
| |
| /** |
| * \struct V4L2SubdeviceFormat |
| * \brief The V4L2 sub-device image format and sizes |
| * |
| * This structure describes the format of images when transported between |
| * separate components connected through a physical bus, such as image sensor |
| * and image receiver or between components part of the same System-on-Chip that |
| * realize an image transformation pipeline. |
| * |
| * The format of images when transported on physical interconnections is known |
| * as the "media bus format", and it is identified by a resolution and a pixel |
| * format identification code, known as the "media bus code", not to be confused |
| * with the fourcc code that identify the format of images when stored in memory |
| * (see V4L2VideoDevice::V4L2DeviceFormat). |
| * |
| * Media Bus formats supported by the V4L2 APIs are described in Section |
| * 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media |
| * Infrastructure userspace API", part of the Linux kernel documentation. |
| * |
| * Image media bus formats are properties of the subdev pads. When images are |
| * transported between two media pads identified by a 0-indexed number, the |
| * image bus format configured on the two pads should match (according to the |
| * underlying driver format matching criteria) in order to prepare for a |
| * successful streaming operation. For a more detailed description of the image |
| * format negotiation process when performed between V4L2 subdevices, refer to |
| * Section 4.15.3.1 of the above mentioned Linux kernel documentation section. |
| */ |
| |
| /** |
| * \var V4L2SubdeviceFormat::mbus_code |
| * \brief The image format bus code |
| */ |
| |
| /** |
| * \var V4L2SubdeviceFormat::size |
| * \brief The image size in pixels |
| */ |
| |
| /** |
| * \var V4L2SubdeviceFormat::colorSpace |
| * \brief The color space of the pixels |
| * |
| * The color space of the image. When setting the format this may be |
| * unset, in which case the driver gets to use its default color space. |
| * After being set, this value should contain the color space that |
| * was actually used. If this value is unset, then the color space chosen |
| * by the driver could not be represented by the ColorSpace class (and |
| * should probably be added). |
| * |
| * It is up to the pipeline handler or application to check if the |
| * resulting color space is acceptable. |
| */ |
| |
| /** |
| * \brief Assemble and return a string describing the format |
| * \return A string describing the V4L2SubdeviceFormat |
| */ |
| const std::string V4L2SubdeviceFormat::toString() const |
| { |
| std::stringstream mbus; |
| mbus << size.toString() << "-"; |
| |
| const auto it = formatInfoMap.find(mbus_code); |
| |
| if (it == formatInfoMap.end()) |
| mbus << utils::hex(mbus_code, 4); |
| else |
| mbus << it->second.name; |
| |
| return mbus.str(); |
| } |
| |
| /** |
| * \brief Retrieve the number of bits per pixel for the V4L2 subdevice format |
| * \return The number of bits per pixel for the format, or 0 if the format is |
| * not supported |
| */ |
| uint8_t V4L2SubdeviceFormat::bitsPerPixel() const |
| { |
| const auto it = formatInfoMap.find(mbus_code); |
| if (it == formatInfoMap.end()) { |
| LOG(V4L2, Error) << "No information available for format '" |
| << toString() << "'"; |
| return 0; |
| } |
| |
| return it->second.bitsPerPixel; |
| } |
| |
| /** |
| * \class V4L2Subdevice |
| * \brief A V4L2 subdevice as exposed by the Linux kernel |
| * |
| * The V4L2Subdevice class provides an API to the "Sub-device interface" as |
| * described in section 4.15 of the "Linux Media Infrastructure userspace API" |
| * chapter of the Linux Kernel documentation. |
| * |
| * A V4L2Subdevice is constructed from a MediaEntity instance, using the system |
| * path of the entity's device node. No API call other than open(), isOpen() |
| * and close() shall be called on an unopened device instance. Upon destruction |
| * any device left open will be closed, and any resources released. |
| */ |
| |
| /** |
| * \typedef V4L2Subdevice::Formats |
| * \brief A map of supported media bus formats to frame sizes |
| */ |
| |
| /** |
| * \enum V4L2Subdevice::Whence |
| * \brief Specify the type of format for getFormat() and setFormat() operations |
| * \var V4L2Subdevice::ActiveFormat |
| * \brief The format operation applies to ACTIVE formats |
| * \var V4L2Subdevice::TryFormat |
| * \brief The format operation applies to TRY formats |
| */ |
| |
| /** |
| * \brief Create a V4L2 subdevice from a MediaEntity using its device node |
| * path |
| */ |
| V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity) |
| : V4L2Device(entity->deviceNode()), entity_(entity) |
| { |
| } |
| |
| V4L2Subdevice::~V4L2Subdevice() |
| { |
| close(); |
| } |
| |
| /** |
| * \brief Open a V4L2 subdevice |
| * \return 0 on success or a negative error code otherwise |
| */ |
| int V4L2Subdevice::open() |
| { |
| return V4L2Device::open(O_RDWR); |
| } |
| |
| /** |
| * \fn V4L2Subdevice::entity() |
| * \brief Retrieve the media entity associated with the subdevice |
| * \return The subdevice's associated media entity. |
| */ |
| |
| /** |
| * \brief Get selection rectangle \a rect for \a target |
| * \param[in] pad The 0-indexed pad number the rectangle is retrieved from |
| * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags |
| * \param[out] rect The retrieved selection rectangle |
| * |
| * \todo Define a V4L2SelectionTarget enum for the selection target |
| * |
| * \return 0 on success or a negative error code otherwise |
| */ |
| int V4L2Subdevice::getSelection(unsigned int pad, unsigned int target, |
| Rectangle *rect) |
| { |
| struct v4l2_subdev_selection sel = {}; |
| |
| sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| sel.pad = pad; |
| sel.target = target; |
| sel.flags = 0; |
| |
| int ret = ioctl(VIDIOC_SUBDEV_G_SELECTION, &sel); |
| if (ret < 0) { |
| LOG(V4L2, Error) |
| << "Unable to get rectangle " << target << " on pad " |
| << pad << ": " << strerror(-ret); |
| return ret; |
| } |
| |
| rect->x = sel.r.left; |
| rect->y = sel.r.top; |
| rect->width = sel.r.width; |
| rect->height = sel.r.height; |
| |
| return 0; |
| } |
| |
| /** |
| * \brief Set selection rectangle \a rect for \a target |
| * \param[in] pad The 0-indexed pad number the rectangle is to be applied to |
| * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags |
| * \param[inout] rect The selection rectangle to be applied |
| * |
| * \todo Define a V4L2SelectionTarget enum for the selection target |
| * |
| * \return 0 on success or a negative error code otherwise |
| */ |
| int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, |
| Rectangle *rect) |
| { |
| struct v4l2_subdev_selection sel = {}; |
| |
| sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| sel.pad = pad; |
| sel.target = target; |
| sel.flags = 0; |
| |
| sel.r.left = rect->x; |
| sel.r.top = rect->y; |
| sel.r.width = rect->width; |
| sel.r.height = rect->height; |
| |
| int ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel); |
| if (ret < 0) { |
| LOG(V4L2, Error) |
| << "Unable to set rectangle " << target << " on pad " |
| << pad << ": " << strerror(-ret); |
| return ret; |
| } |
| |
| rect->x = sel.r.left; |
| rect->y = sel.r.top; |
| rect->width = sel.r.width; |
| rect->height = sel.r.height; |
| |
| return 0; |
| } |
| /** |
| * \brief Enumerate all media bus codes and frame sizes on a \a pad |
| * \param[in] pad The 0-indexed pad number to enumerate formats on |
| * |
| * Enumerate all media bus codes and frame sizes supported by the subdevice on |
| * a \a pad. |
| * |
| * \return A list of the supported device formats |
| */ |
| V4L2Subdevice::Formats V4L2Subdevice::formats(unsigned int pad) |
| { |
| Formats formats; |
| |
| if (pad >= entity_->pads().size()) { |
| LOG(V4L2, Error) << "Invalid pad: " << pad; |
| return {}; |
| } |
| |
| for (unsigned int code : enumPadCodes(pad)) { |
| std::vector<SizeRange> sizes = enumPadSizes(pad, code); |
| if (sizes.empty()) |
| return {}; |
| |
| const auto inserted = formats.insert({ code, sizes }); |
| if (!inserted.second) { |
| LOG(V4L2, Error) |
| << "Could not add sizes for media bus code " |
| << code << " on pad " << pad; |
| return {}; |
| } |
| } |
| |
| return formats; |
| } |
| |
| /** |
| * \brief Retrieve the image format set on one of the V4L2 subdevice pads |
| * \param[in] pad The 0-indexed pad number the format is to be retrieved from |
| * \param[out] format The image bus format |
| * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat |
| * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" |
| * \return 0 on success or a negative error code otherwise |
| */ |
| int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format, |
| Whence whence) |
| { |
| struct v4l2_subdev_format subdevFmt = {}; |
| subdevFmt.which = whence == ActiveFormat ? V4L2_SUBDEV_FORMAT_ACTIVE |
| : V4L2_SUBDEV_FORMAT_TRY; |
| subdevFmt.pad = pad; |
| |
| int ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt); |
| if (ret) { |
| LOG(V4L2, Error) |
| << "Unable to get format on pad " << pad |
| << ": " << strerror(-ret); |
| return ret; |
| } |
| |
| format->size.width = subdevFmt.format.width; |
| format->size.height = subdevFmt.format.height; |
| format->mbus_code = subdevFmt.format.code; |
| format->colorSpace = toColorSpace(subdevFmt.format); |
| |
| return 0; |
| } |
| |
| /** |
| * \brief Set an image format on one of the V4L2 subdevice pads |
| * \param[in] pad The 0-indexed pad number the format is to be applied to |
| * \param[inout] format The image bus format to apply to the subdevice's pad |
| * \param[in] whence The format to set, \ref V4L2Subdevice::ActiveFormat |
| * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" |
| * |
| * Apply the requested image format to the desired media pad and return the |
| * actually applied format parameters, as getFormat() would do. |
| * |
| * \return 0 on success or a negative error code otherwise |
| */ |
| int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format, |
| Whence whence) |
| { |
| struct v4l2_subdev_format subdevFmt = {}; |
| subdevFmt.which = whence == ActiveFormat ? V4L2_SUBDEV_FORMAT_ACTIVE |
| : V4L2_SUBDEV_FORMAT_TRY; |
| subdevFmt.pad = pad; |
| subdevFmt.format.width = format->size.width; |
| subdevFmt.format.height = format->size.height; |
| subdevFmt.format.code = format->mbus_code; |
| subdevFmt.format.field = V4L2_FIELD_NONE; |
| fromColorSpace(format->colorSpace, subdevFmt.format); |
| |
| int ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt); |
| if (ret) { |
| LOG(V4L2, Error) |
| << "Unable to set format on pad " << pad |
| << ": " << strerror(-ret); |
| return ret; |
| } |
| |
| format->size.width = subdevFmt.format.width; |
| format->size.height = subdevFmt.format.height; |
| format->mbus_code = subdevFmt.format.code; |
| format->colorSpace = toColorSpace(subdevFmt.format); |
| |
| return 0; |
| } |
| |
| /** |
| * \brief Retrieve the model name of the device |
| * |
| * The model name allows identification of the specific device model. This can |
| * be used to infer device characteristics, for instance to determine the |
| * analogue gain model of a camera sensor based on the sensor model name. |
| * |
| * Neither the V4L2 API nor the Media Controller API expose an explicit model |
| * name. This function implements a heuristics to extract the model name from |
| * the subdevice's entity name. This should produce accurate results for |
| * I2C-based devices. If the heuristics can't match a known naming pattern, |
| * the function returns the full entity name. |
| * |
| * \return The model name of the device |
| */ |
| const std::string &V4L2Subdevice::model() |
| { |
| if (!model_.empty()) |
| return model_; |
| |
| /* |
| * Extract model name from the media entity name. |
| * |
| * There is no standardized naming scheme for sensor or other entities |
| * in the Linux kernel at the moment. |
| * |
| * - The most common rule, used by I2C sensors, associates the model |
| * name with the I2C bus number and address (e.g. 'imx219 0-0010'). |
| * |
| * - When the sensor exposes multiple subdevs, the model name is |
| * usually followed by a function name, as in the smiapp driver (e.g. |
| * 'jt8ew9 pixel_array 0-0010'). |
| * |
| * - The vimc driver names its sensors 'Sensor A' and 'Sensor B'. |
| * |
| * Other schemes probably exist. As a best effort heuristic, use the |
| * part of the entity name before the first space if the name contains |
| * an I2C address, and use the full entity name otherwise. |
| */ |
| std::string entityName = entity_->name(); |
| std::regex i2cRegex{ " [0-9]+-[0-9a-f]{4}" }; |
| std::smatch match; |
| |
| std::string model; |
| if (std::regex_search(entityName, match, i2cRegex)) |
| model_ = entityName.substr(0, entityName.find(' ')); |
| else |
| model_ = entityName; |
| |
| return model_; |
| } |
| |
| /** |
| * \brief Create a new video subdevice instance from \a entity in media device |
| * \a media |
| * \param[in] media The media device where the entity is registered |
| * \param[in] entity The media entity name |
| * |
| * \return A newly created V4L2Subdevice on success, nullptr otherwise |
| */ |
| std::unique_ptr<V4L2Subdevice> |
| V4L2Subdevice::fromEntityName(const MediaDevice *media, |
| const std::string &entity) |
| { |
| MediaEntity *mediaEntity = media->getEntityByName(entity); |
| if (!mediaEntity) |
| return nullptr; |
| |
| return std::make_unique<V4L2Subdevice>(mediaEntity); |
| } |
| |
| std::string V4L2Subdevice::logPrefix() const |
| { |
| return "'" + entity_->name() + "'"; |
| } |
| |
| std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad) |
| { |
| std::vector<unsigned int> codes; |
| int ret; |
| |
| for (unsigned int index = 0; ; index++) { |
| struct v4l2_subdev_mbus_code_enum mbusEnum = {}; |
| mbusEnum.pad = pad; |
| mbusEnum.index = index; |
| mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| |
| ret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum); |
| if (ret) |
| break; |
| |
| codes.push_back(mbusEnum.code); |
| } |
| |
| if (ret < 0 && ret != -EINVAL) { |
| LOG(V4L2, Error) |
| << "Unable to enumerate formats on pad " << pad |
| << ": " << strerror(-ret); |
| return {}; |
| } |
| |
| return codes; |
| } |
| |
| std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad, |
| unsigned int code) |
| { |
| std::vector<SizeRange> sizes; |
| int ret; |
| |
| for (unsigned int index = 0;; index++) { |
| struct v4l2_subdev_frame_size_enum sizeEnum = {}; |
| sizeEnum.index = index; |
| sizeEnum.pad = pad; |
| sizeEnum.code = code; |
| sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE; |
| |
| ret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum); |
| if (ret) |
| break; |
| |
| sizes.emplace_back(Size{ sizeEnum.min_width, sizeEnum.min_height }, |
| Size{ sizeEnum.max_width, sizeEnum.max_height }); |
| } |
| |
| if (ret < 0 && ret != -EINVAL && ret != -ENOTTY) { |
| LOG(V4L2, Error) |
| << "Unable to enumerate sizes on pad " << pad |
| << ": " << strerror(-ret); |
| return {}; |
| } |
| |
| return sizes; |
| } |
| |
| } /* namespace libcamera */ |