summaryrefslogtreecommitdiff
path: root/src/v4l2/v4l2_camera_proxy.h
blob: 92a79abb9e21f8edf8e89f1ae7f2dd7c7c251423 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera
 */

#ifndef __V4L2_CAMERA_PROXY_H__
#define __V4L2_CAMERA_PROXY_H__

#include <linux/videodev2.h>
#include <map>
#include <memory>
#include <set>
#include <sys/mman.h>
#include <sys/types.h>
#include <vector>

#include <libcamera/camera.h>

#include "v4l2_camera.h"

using namespace libcamera;

class V4L2CameraFile;

class V4L2CameraProxy
{
public:
	V4L2CameraProxy(unsigned int index, std::shared_ptr<Camera> camera);

	int open(V4L2CameraFile *file);
	void close(V4L2CameraFile *file);
	void *mmap(void *addr, size_t length, int prot, int flags, off64_t offset);
	int munmap(void *addr, size_t length);

	int ioctl(V4L2CameraFile *file, unsigned long request, void *arg);

private:
	bool validateBufferType(uint32_t type);
	bool validateMemoryType(uint32_t memory);
	void setFmtFromConfig(const StreamConfiguration &streamConfig);
	void querycap(std::shared_ptr<Camera> camera);
	int tryFormat(struct v4l2_format *arg);
	enum v4l2_priority maxPriority();
	void updateBuffers();
	void freeBuffers();

	int vidioc_querycap(struct v4l2_capability *arg);
	int vidioc_enum_framesizes(V4L2CameraFile *file, struct v4l2_frmsizeenum *arg);
	int vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *arg);
	int vidioc_g_fmt(V4L2CameraFile *file, struct v4l2_format *arg);
	int vidioc_s_fmt(V4L2CameraFile *file, struct v4l2_format *arg);
	int vidioc_try_fmt(V4L2CameraFile *file, struct v4l2_format *arg);
	int vidioc_g_priority(V4L2CameraFile *file, enum v4l2_priority *arg);
	int vidioc_s_priority(V4L2CameraFile *file, enum v4l2_priority *arg);
	int vidioc_enuminput(V4L2CameraFile *file, struct v4l2_input *arg);
	int vidioc_g_input(V4L2CameraFile *file, int *arg);
	int vidioc_s_input(V4L2CameraFile *file, int *arg);
	int vidioc_reqbufs(V4L2CameraFile *file, struct v4l2_requestbuffers *arg);
	int vidioc_querybuf(V4L2CameraFile *file, struct v4l2_buffer *arg);
	int vidioc_qbuf(V4L2CameraFile *file, struct v4l2_buffer *arg);
	int vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg, MutexLocker *locker);
	int vidioc_streamon(V4L2CameraFile *file, int *arg);
	int vidioc_streamoff(V4L2CameraFile *file, int *arg);

	bool hasOwnership(V4L2CameraFile *file);
	int acquire(V4L2CameraFile *file);
	void release(V4L2CameraFile *file);

	static const std::set<unsigned long> supportedIoctls_;

	unsigned int refcount_;
	unsigned int index_;

	StreamConfiguration streamConfig_;
	unsigned int bufferCount_;
	unsigned int currentBuf_;
	unsigned int sizeimage_;

	struct v4l2_capability capabilities_;
	struct v4l2_pix_format v4l2PixFormat_;

	std::vector<struct v4l2_buffer> buffers_;
	std::map<void *, unsigned int> mmaps_;

	std::set<V4L2CameraFile *> files_;

	std::unique_ptr<V4L2Camera> vcam_;

	/*
	 * This is the exclusive owner of this V4L2CameraProxy instance.
	 * When there is no owner, anybody can call any ioctl before reqbufs.
	 * The first file to call reqbufs with count > 0 or s_fmt will become
	 * the owner, and when the owner calls reqbufs with count = 0 it will
	 * release ownership. Any buffer-related ioctl (except querybuf) or
	 * s_fmt that is called by a non-owner while there exists an owner
	 * will return -EBUSY.
	 */
	V4L2CameraFile *owner_;

	/* This mutex is to serialize access to the proxy. */
	Mutex proxyMutex_;
};

#endif /* __V4L2_CAMERA_PROXY_H__ */
ding zeros. A hyphen (\"-\") introduces this optional part. -- BUILD is a dot separated sequence of identifiers composed of alphanumeric characters and hyphens. A plus (\"+\") introduces this optional part. <other_version> See <version> definition. <prerel> A string as defined by PRERELEASE above. Or, it can be a PRERELEASE prototype string followed by a dot. <build> A string as defined by BUILD above. Options: -v, --version Print the version of this tool. -h, --help Print this help message. Commands: bump Bump by one of major, minor, patch; zeroing or removing subsequent parts. \"bump prerel\" (or its synonym \"bump prerelease\") sets the PRERELEASE part and removes any BUILD part. A trailing dot in the <prerel> argument introduces an incrementing numeric field which is added or bumped. If no <prerel> argument is provided, an incrementing numeric field is introduced/bumped. \"bump build\" sets the BUILD part. \"bump release\" removes any PRERELEASE or BUILD parts. The bumped version is written to stdout. get Extract given part of <version>, where part is one of major, minor, patch, prerel (alternatively: prerelease), build, or release. compare Compare <version> with <other_version>, output to stdout the following values: -1 if <other_version> is newer, 0 if equal, 1 if older. The BUILD part is not used in comparisons. diff Compare <version> with <other_version>, output to stdout the difference between two versions by the release type (MAJOR, MINOR, PATCH, PRERELEASE, BUILD). validate Validate if <version> follows the SEMVER pattern (see <version> definition). Print 'valid' to stdout if the version is valid, otherwise print 'invalid'. See also: https://semver.org -- Semantic Versioning 2.0.0" function error { echo -e "$1" >&2 exit 1 } function usage_help { error "$USAGE" } function usage_version { echo -e "${PROG}: $PROG_VERSION" exit 0 } # normalize the "part" keywords to a canonical string. At present, # only "prerelease" is normalized to "prerel". function normalize_part { if [ "$1" == "prerelease" ] then echo "prerel" else echo "$1" fi } function validate_version { local version=$1 if [[ "$version" =~ $SEMVER_REGEX ]]; then # if a second argument is passed, store the result in var named by $2 if [ "$#" -eq "2" ]; then local major=${BASH_REMATCH[1]} local minor=${BASH_REMATCH[2]} local patch=${BASH_REMATCH[3]} local prere=${BASH_REMATCH[4]} local build=${BASH_REMATCH[8]} eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")" else echo "$version" fi else error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information." fi } function is_nat { [[ "$1" =~ ^($NAT)$ ]] } function is_null { [ -z "$1" ] } function order_nat { [ "$1" -lt "$2" ] && { echo -1 ; return ; } [ "$1" -gt "$2" ] && { echo 1 ; return ; } echo 0 } function order_string { [[ $1 < $2 ]] && { echo -1 ; return ; } [[ $1 > $2 ]] && { echo 1 ; return ; } echo 0 } # given two (named) arrays containing NAT and/or ALPHANUM fields, compare them # one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1) # is less-than, equal, or greater-than the right array ($2). The longer array # is considered greater-than the shorter if the shorter is a prefix of the longer. # function compare_fields { local l="$1[@]" local r="$2[@]" local leftfield=( "${!l}" ) local rightfield=( "${!r}" ) local left local right local i=$(( -1 )) local order=$(( 0 )) while true do [ $order -ne 0 ] && { echo $order ; return ; } : $(( i++ )) left="${leftfield[$i]}" right="${rightfield[$i]}" is_null "$left" && is_null "$right" && { echo 0 ; return ; } is_null "$left" && { echo -1 ; return ; } is_null "$right" && { echo 1 ; return ; } is_nat "$left" && is_nat "$right" && { order=$(order_nat "$left" "$right") ; continue ; } is_nat "$left" && { echo -1 ; return ; } is_nat "$right" && { echo 1 ; return ; } { order=$(order_string "$left" "$right") ; continue ; } done } # shellcheck disable=SC2206 # checked by "validate"; ok to expand prerel id's into array function compare_version { local order validate_version "$1" V validate_version "$2" V_ # compare major, minor, patch local left=( "${V[0]}" "${V[1]}" "${V[2]}" ) local right=( "${V_[0]}" "${V_[1]}" "${V_[2]}" ) order=$(compare_fields left right) [ "$order" -ne 0 ] && { echo "$order" ; return ; } # compare pre-release ids when M.m.p are equal local prerel="${V[3]:1}" local prerel_="${V_[3]:1}" local left=( ${prerel//./ } ) local right=( ${prerel_//./ } ) # if left and right have no pre-release part, then left equals right # if only one of left/right has pre-release part, that one is less than simple M.m.p [ -z "$prerel" ] && [ -z "$prerel_" ] && { echo 0 ; return ; } [ -z "$prerel" ] && { echo 1 ; return ; } [ -z "$prerel_" ] && { echo -1 ; return ; } # otherwise, compare the pre-release id's compare_fields left right } # render_prerel -- return a prerel field with a trailing numeric string # usage: render_prerel numeric [prefix-string] # function render_prerel { if [ -z "$2" ] then echo "${1}" else echo "${2}${1}" fi } # extract_prerel -- extract prefix and trailing numeric portions of a pre-release part # usage: extract_prerel prerel prerel_parts # The prefix and trailing numeric parts are returned in "prerel_parts". # PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]' DIGITS='[0-9][0-9]*' EXTRACT_REGEX="^(${PREFIX_ALPHANUM})*(${DIGITS})$" function extract_prerel { local prefix; local numeric; if [[ "$1" =~ $EXTRACT_REGEX ]] then # found prefix and trailing numeric parts prefix="${BASH_REMATCH[1]}" numeric="${BASH_REMATCH[2]}" else # no numeric part prefix="${1}" numeric= fi eval "$2=(\"$prefix\" \"$numeric\")" } # bump_prerel -- return the new pre-release part based on previous pre-release part # and prototype for bump # usage: bump_prerel proto previous # function bump_prerel { local proto; local prev_prefix; local prev_numeric; # case one: no trailing dot in prototype => simply replace previous with proto if [[ ! ( "$1" =~ \.$ ) ]] then echo "$1" return fi proto="${1%.}" # discard trailing dot marker from prototype extract_prerel "${2#-}" prerel_parts # extract parts of previous pre-release # shellcheck disable=SC2154 prev_prefix="${prerel_parts[0]}" prev_numeric="${prerel_parts[1]}" # case two: bump or append numeric to previous pre-release part if [ "$proto" == "+" ] # dummy "+" indicates no prototype argument provided then if [ -n "$prev_numeric" ] then : $(( ++prev_numeric )) # previous pre-release is already numbered, bump it render_prerel "$prev_numeric" "$prev_prefix" else render_prerel 1 "$prev_prefix" # append starting number fi return fi # case three: set, bump, or append using prototype prefix if [ "$prev_prefix" != "$proto" ] then render_prerel 1 "$proto" # proto not same pre-release; set and start at '1' elif [ -n "$prev_numeric" ] then : $(( ++prev_numeric )) # pre-release is numbered; bump it render_prerel "$prev_numeric" "$prev_prefix" else render_prerel 1 "$prev_prefix" # start pre-release at number '1' fi } function command_bump { local new; local version; local sub_version; local command; command="$(normalize_part "$1")" case $# in 2) case "$command" in major|minor|patch|prerel|release) sub_version="+."; version=$2;; *) usage_help;; esac ;; 3) case "$command" in prerel|build) sub_version=$2 version=$3 ;; *) usage_help;; esac ;; *) usage_help;; esac validate_version "$version" parts # shellcheck disable=SC2154 local major="${parts[0]}" local minor="${parts[1]}" local patch="${parts[2]}" local prere="${parts[3]}" local build="${parts[4]}" case "$command" in major) new="$((major + 1)).0.0";; minor) new="${major}.$((minor + 1)).0";; patch) new="${major}.${minor}.$((patch + 1))";; release) new="${major}.${minor}.${patch}";; prerel) new=$(validate_version "${major}.${minor}.${patch}-$(bump_prerel "$sub_version" "$prere")");; build) new=$(validate_version "${major}.${minor}.${patch}${prere}+${sub_version}");; *) usage_help ;; esac echo "$new" exit 0 } function command_compare { local v; local v_; case $# in 2) v=$(validate_version "$1"); v_=$(validate_version "$2") ;; *) usage_help ;; esac set +u # need unset array element to evaluate to null compare_version "$v" "$v_" exit 0 } function command_diff { validate_version "$1" v1_parts # shellcheck disable=SC2154 local v1_major="${v1_parts[0]}" local v1_minor="${v1_parts[1]}" local v1_patch="${v1_parts[2]}" local v1_prere="${v1_parts[3]}" local v1_build="${v1_parts[4]}" validate_version "$2" v2_parts # shellcheck disable=SC2154 local v2_major="${v2_parts[0]}" local v2_minor="${v2_parts[1]}" local v2_patch="${v2_parts[2]}" local v2_prere="${v2_parts[3]}" local v2_build="${v2_parts[4]}" if [ "${v1_major}" != "${v2_major}" ]; then echo "major" elif [ "${v1_minor}" != "${v2_minor}" ]; then echo "minor" elif [ "${v1_patch}" != "${v2_patch}" ]; then echo "patch" elif [ "${v1_prere}" != "${v2_prere}" ]; then echo "prerelease" elif [ "${v1_build}" != "${v2_build}" ]; then echo "build" fi } # shellcheck disable=SC2034 function command_get { local part version if [[ "$#" -ne "2" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then usage_help exit 0 fi part="$1" version="$2" validate_version "$version" parts local major="${parts[0]}" local minor="${parts[1]}" local patch="${parts[2]}" local prerel="${parts[3]:1}" local build="${parts[4]:1}" local release="${major}.${minor}.${patch}" part="$(normalize_part "$part")" case "$part" in major|minor|patch|release|prerel|build) echo "${!part}" ;; *) usage_help ;; esac exit 0 } function command_validate { if [[ "$#" -ne "1" ]]; then usage_help fi if [[ "$1" =~ $SEMVER_REGEX ]]; then echo "valid" else echo "invalid" fi exit 0 } case $# in 0) echo "Unknown command: $*"; usage_help;; esac case $1 in --help|-h) echo -e "$USAGE"; exit 0;; --version|-v) usage_version ;; bump) shift; command_bump "$@";; get) shift; command_get "$@";; compare) shift; command_compare "$@";; diff) shift; command_diff "$@";; validate) shift; command_validate "$@";; *) echo "Unknown arguments: $*"; usage_help;; esac