Move remaining device methods

This commit is contained in:
Kamil Trzcinski
2022-04-10 11:58:59 +02:00
parent 33b7062bf1
commit 09c2f6e595
12 changed files with 307 additions and 210 deletions

View File

@ -2,18 +2,88 @@
#include "device/hw/v4l2.h"
#include "device/hw/device.h"
device_hw_t v4l2_device_hw = {
.buffer_open = v4l2_buffer_open,
.buffer_close = v4l2_buffer_close,
.buffer_enqueue = v4l2_buffer_enqueue,
.buffer_list_dequeue = v4l2_buffer_list_dequeue,
.buffer_list_pollfd = v4l2_buffer_list_pollfd,
.buffer_list_set_format = v4l2_buffer_list_set_format,
.buffer_list_set_buffers = v4l2_buffer_list_set_buffers,
.buffer_list_set_stream = v4l2_buffer_list_set_stream
};
device_t *device_v4l2_open(const char *name, const char *path)
int v4l2_device_open(device_t *dev)
{
return device_open(name, path, &v4l2_device_hw);
dev->fd = -1;
dev->subdev_fd = -1;
dev->fd = open(dev->path, O_RDWR|O_NONBLOCK);
if (dev->fd < 0) {
E_LOG_ERROR(dev, "Can't open device: %s", dev->path);
goto error;
}
E_LOG_DEBUG(dev, "Querying device capabilities ...");
struct v4l2_capability v4l2_cap;
E_XIOCTL(dev, dev->fd, VIDIOC_QUERYCAP, &v4l2_cap, "Can't query device capabilities");
if (!(v4l2_cap.capabilities & V4L2_CAP_STREAMING)) {
E_LOG_ERROR(dev, "Device doesn't support streaming IO");
}
strcpy(dev->bus_info, v4l2_cap.bus_info);
dev->subdev_fd = v4l2_device_open_v4l2_subdev(dev, 0);
return 0;
error:
return -1;
}
void v4l2_device_close(device_t *dev)
{
if (dev->subdev_fd >= 0) {
close(dev->subdev_fd);
}
if(dev->fd >= 0) {
close(dev->fd);
}
}
int v4l2_device_set_decoder_start(device_t *dev, bool do_on)
{
struct v4l2_decoder_cmd cmd = {0};
cmd.cmd = do_on ? V4L2_DEC_CMD_START : V4L2_DEC_CMD_STOP;
E_LOG_DEBUG(dev, "Setting decoder state %s...", do_on ? "Start" : "Stop");
E_XIOCTL(dev, dev->fd, VIDIOC_DECODER_CMD, &cmd, "Cannot set decoder state");
return 0;
error:
return -1;
}
int v4l2_device_video_force_key(device_t *dev)
{
struct v4l2_control ctl = {0};
ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
ctl.value = 1;
E_LOG_DEBUG(dev, "Forcing keyframe ...");
E_XIOCTL(dev, dev->fd, VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
return 0;
error:
return -1;
}
int v4l2_device_set_fps(device_t *dev, int desired_fps)
{
struct v4l2_streamparm setfps = {0};
if (!dev) {
return -1;
}
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
setfps.parm.output.timeperframe.numerator = 1;
setfps.parm.output.timeperframe.denominator = desired_fps;
E_LOG_DEBUG(dev, "Configuring FPS ...");
E_XIOCTL(dev, dev->fd, VIDIOC_S_PARM, &setfps, "Can't set FPS");
return 0;
error:
return -1;
}

27
device/v4l2/device_hw.c Normal file
View File

@ -0,0 +1,27 @@
#include "device/v4l2/v4l2.h"
#include "device/hw/v4l2.h"
#include "device/hw/device.h"
device_hw_t v4l2_device_hw = {
.device_open = v4l2_device_open,
.device_close = v4l2_device_close,
.device_set_decoder_start = v4l2_device_set_decoder_start,
.device_video_force_key = v4l2_device_video_force_key,
.device_set_fps = v4l2_device_set_fps,
.device_set_option = v4l2_device_set_option,
.buffer_open = v4l2_buffer_open,
.buffer_close = v4l2_buffer_close,
.buffer_enqueue = v4l2_buffer_enqueue,
.buffer_list_dequeue = v4l2_buffer_list_dequeue,
.buffer_list_pollfd = v4l2_buffer_list_pollfd,
.buffer_list_set_format = v4l2_buffer_list_set_format,
.buffer_list_set_buffers = v4l2_buffer_list_set_buffers,
.buffer_list_set_stream = v4l2_buffer_list_set_stream
};
device_t *device_v4l2_open(const char *name, const char *path)
{
return device_open(name, path, &v4l2_device_hw);
}

132
device/v4l2/device_media.c Normal file
View File

@ -0,0 +1,132 @@
#include "device/v4l2/v4l2.h"
#include "device/hw/device.h"
#include "device/hw/v4l2.h"
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <linux/media.h>
int v4l2_device_open_media_device(device_t *dev)
{
struct stat st;
if (fstat(dev->fd, &st) < 0) {
E_LOG_ERROR(dev, "Cannot get fstat");
return -1;
}
if (~st.st_mode & S_IFCHR) {
E_LOG_ERROR(dev, "FD is not char");
return -1;
}
char path[256];
sprintf(path, "/sys/dev/char/%d:%d/device", major(st.st_rdev), minor(st.st_rdev));
struct dirent **namelist;
int n = scandir(path, &namelist, NULL, NULL);
if (n < 0) {
E_LOG_ERROR(dev, "Cannot scan: %s", path);
return -1;
}
int ret = -1;
while (n--) {
if (ret == -1 && strstr(namelist[n]->d_name, "media") == namelist[n]->d_name) {
path[0] = 0;
sprintf(path, "/dev/%s", namelist[n]->d_name);
ret = open(path, O_RDWR);
if (ret >= 0) {
E_LOG_VERBOSE(dev, "Opened '%s' (fd=%d)", path, ret);
}
}
free(namelist[n]);
}
free(namelist);
error:
return ret;
}
int v4l2_device_open_v4l2_subdev(device_t *dev, int subdev)
{
int media_fd = -1;
unsigned int last_id = 0;
int ret = -1;
media_fd = v4l2_device_open_media_device(dev);
if (media_fd < 0) {
E_LOG_ERROR(dev, "Cannot find media controller");
}
for (;;) {
struct media_entity_desc entity = {
.id = MEDIA_ENT_ID_FLAG_NEXT | last_id,
};
int rc = ioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
if (rc < 0 && errno == EINVAL)
break;
if (rc < 0) {
goto error;
}
last_id = entity.id;
char path[256];
sprintf(path, "/sys/dev/char/%d:%d", entity.dev.major, entity.dev.minor);
char link[256];
if (readlink(path, link, sizeof(link)) < 0) {
E_LOG_ERROR(dev, "Cannot readlink '%s'", path);
goto error;
}
char * last = strrchr(link, '/');
if (!last) {
E_LOG_ERROR(dev, "Link '%s' for '%s' does not end with '/'", link, path);
goto error;
}
if (strstr(last, "/v4l-subdev") != last) {
goto error;
}
sprintf(path, "/dev%s", last);
ret = open(path, O_RDWR);
if (ret >= 0) {
E_LOG_INFO(dev, "Opened '%s' (fd=%d)", path, ret);
}
break;
}
error:
close(media_fd);
return ret;
}
int v4l2_device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format)
{
struct v4l2_subdev_format fmt = {0};
if (dev->subdev_fd < 0) {
return -1;
}
fmt.pad = 0;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.format.code = format;
fmt.format.width = width;
fmt.format.height = height;
fmt.format.colorspace = V4L2_COLORSPACE_RAW;
fmt.format.field = V4L2_FIELD_ANY;
E_LOG_DEBUG(dev, "Configuring mpad %d (subdev_fd=%d)...", fmt.pad, dev->subdev_fd);
E_XIOCTL(dev, dev->subdev_fd, VIDIOC_SUBDEV_S_FMT, &fmt, "Can't configure mpad %d (subdev_fd=%d)", fmt.pad, dev->subdev_fd);
return 0;
error:
return -1;
}

View File

@ -0,0 +1,172 @@
#include "device/v4l2/v4l2.h"
#include "device/hw/v4l2.h"
#include "device/hw/device.h"
#include <ctype.h>
int v4l2_device_set_option_by_id(device_t *dev, const char *name, uint32_t id, int32_t value)
{
struct v4l2_control ctl = {0};
if (!dev) {
return -1;
}
ctl.id = id;
ctl.value = value;
E_LOG_DEBUG(dev, "Configuring option %s (%08x) = %d", name, id, value);
E_XIOCTL(dev, dev->subdev_fd >= 0 ? dev->subdev_fd : dev->fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
return 0;
error:
return -1;
}
void v4l2_device_option_normalize_name(char *in)
{
char *out = in;
while (*in) {
if (isalnum(*in)) {
*out++ = tolower(*in++);
} else if (isprint(*in)) {
*out++ = '_';
while (*++in && isprint(*in) && !isalnum(*in));
} else {
in++;
}
}
*out++ = 0;
}
static int v4l2_device_set_option_string_fd_iter_id(device_t *dev, int fd, uint32_t *id, char *name, char *value)
{
struct v4l2_query_ext_ctrl qctrl = { .id = *id };
void *data = NULL;
if (0 != ioctl (fd, VIDIOC_QUERY_EXT_CTRL, &qctrl)) {
*id = 0;
return 0;
}
*id = qctrl.id;
v4l2_device_option_normalize_name(qctrl.name);
if (strcmp(qctrl.name, name) != 0)
return 0;
if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
E_LOG_INFO(dev, "The '%s' is disabled", name);
return 0;
} else if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
E_LOG_INFO(dev, "The '%s' is read-only", name);
return 0;
}
switch(qctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
{
struct v4l2_control ctl = {
.id = *id,
.value = atoi(value)
};
E_LOG_INFO(dev, "Configuring option %s (%08x) = %d", name, ctl.id, ctl.value);
E_XIOCTL(dev, fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
}
return 1;
case V4L2_CTRL_TYPE_U8:
case V4L2_CTRL_TYPE_U16:
case V4L2_CTRL_TYPE_U32:
{
struct v4l2_ext_control ctl = {
.id = *id,
.size = qctrl.elem_size * qctrl.elems,
.ptr = data = calloc(qctrl.elems, qctrl.elem_size)
};
struct v4l2_ext_controls ctrls = {
.count = 1,
.ctrl_class = V4L2_CTRL_ID2CLASS(*id),
.controls = &ctl,
};
char *string = value;
char *token;
int tokens = 0;
for ( ; token = strsep(&string, ","); tokens++) {
if (tokens >= qctrl.elems)
continue;
switch(qctrl.type) {
case V4L2_CTRL_TYPE_U8:
ctl.p_u8[tokens] = atoi(token);
break;
case V4L2_CTRL_TYPE_U16:
ctl.p_u16[tokens] = atoi(token);
break;
case V4L2_CTRL_TYPE_U32:
ctl.p_u32[tokens] = atoi(token);
break;
}
}
E_LOG_INFO(dev, "Configuring option %s (%08x) = [%d tokens, expected %d]", name, ctl.id, tokens, qctrl.elems);
E_XIOCTL(dev, fd, VIDIOC_S_EXT_CTRLS, &ctrls, "Can't set option %s", name);
free(data);
}
return 1;
default:
E_LOG_INFO(dev, "The '%s' control type '%d' is not supported", name, qctrl.type);
return -1;
}
error:
free(data);
return -1;
}
static int v4l2_device_set_option_string_fd(device_t *dev, int fd, char *name, char *value)
{
int ret = 0;
uint32_t id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
while ((ret = v4l2_device_set_option_string_fd_iter_id(dev, fd, &id, name, value)) == 0 && id) {
id |= V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
}
return ret;
}
int v4l2_device_set_option(device_t *dev, const char *key, const char *value)
{
char *keyp = strdup(key);
char *valuep = strdup(value);
int ret = 0;
v4l2_device_option_normalize_name(keyp);
if (dev->subdev_fd >= 0)
ret = v4l2_device_set_option_string_fd(dev, dev->subdev_fd, keyp, valuep);
if (ret <= 0)
ret = v4l2_device_set_option_string_fd(dev, dev->fd, keyp, valuep);
free(keyp);
free(valuep);
if (ret == 0)
E_LOG_ERROR(dev, "The '%s=%s' was failed to find.", key, value);
else if (ret < 0)
E_LOG_ERROR(dev, "The '%s=%s' did fail to be set.", key, value);
return 0;
error:
return -1;
}

59
device/v4l2/v4l2.c Normal file
View File

@ -0,0 +1,59 @@
#include "device/v4l2/v4l2.h"
#include <linux/videodev2.h>
#include <linux/v4l2-subdev.h>
int xioctl(const char *name, int fd, int request, void *arg)
{
int retries = XIOCTL_RETRIES;
int retval = -1;
do {
retval = ioctl(fd, request, arg);
} while (
retval
&& retries--
&& (
errno == EINTR
|| errno == EAGAIN
|| errno == ETIMEDOUT
)
);
// cppcheck-suppress knownConditionTrueFalse
if (retval && retries <= 0) {
E_LOG_PERROR(NULL, "%s: ioctl(%08x) retried %u times; giving up", name, request, XIOCTL_RETRIES);
}
return retval;
}
static size_t align_size(size_t size, size_t to)
{
return ((size + (to - 1)) & ~(to - 1));
}
unsigned fourcc_to_stride(unsigned width, unsigned format)
{
switch (format) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_RGB565:
return align_size(width * 2, 32);
case V4L2_PIX_FMT_YUV420:
return align_size(width * 3 / 2, 32);
case V4L2_PIX_FMT_RGB24:
return align_size(width * 3, 32);
case V4L2_PIX_FMT_SRGGB10P:
return align_size(width * 5 / 4, 32);
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_H264:
return 0;
default:
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format).buf);
}
}

View File

@ -5,8 +5,16 @@
typedef struct buffer_s buffer_t;
typedef struct buffer_list_s buffer_list_t;
typedef struct device_s device_t;
struct pollfd;
int v4l2_device_open(device_t *dev);
void v4l2_device_close(device_t *dev);
int v4l2_device_set_decoder_start(device_t *dev, bool do_on);
int v4l2_device_video_force_key(device_t *dev);
int v4l2_device_set_fps(device_t *dev, int desired_fps);
int v4l2_device_set_option(device_t *dev, const char *key, const char *value);
int v4l2_buffer_open(buffer_t *buf);
void v4l2_buffer_close(buffer_t *buf);
int v4l2_buffer_enqueue(buffer_t *buf, const char *who);
@ -17,3 +25,22 @@ int v4l2_buffer_list_pollfd(buffer_list_t *buf_list, struct pollfd *pollfd, bool
int v4l2_buffer_list_set_format(buffer_list_t *buf_list, unsigned width, unsigned height, unsigned format, unsigned bytesperline);
int v4l2_buffer_list_set_buffers(buffer_list_t *buf_list, int nbufs);
int v4l2_buffer_list_set_stream(buffer_list_t *buf_list, bool do_on);
int v4l2_device_open_media_device(device_t *dev);
int v4l2_device_open_v4l2_subdev(device_t *dev, int subdev);
int v4l2_device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format);
#ifndef CFG_XIOCTL_RETRIES
# define CFG_XIOCTL_RETRIES 4
#endif
#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
unsigned fourcc_to_stride(unsigned width, unsigned format);
int xioctl(const char *name, int fd, int request, void *arg);
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
int ret; \
if ((ret = xioctl(dev_name(dev), _fd, _request, _value)) < 0) { \
E_LOG_ERROR(dev, "xioctl(ret=%d): " _msg, ret, ##__VA_ARGS__); \
} \
} while(0)