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,32 +2,22 @@
#include "device/buffer.h"
#include "device/buffer_list.h"
#include "device/hw/v4l2.h"
#include "opts/opts.h"
device_t *device_open(const char *name, const char *path, device_hw_t *hw) {
device_t *dev = calloc(1, sizeof(device_t));
dev->name = strdup(name);
dev->path = strdup(path);
dev->hw = hw;
dev->fd = open(path, O_RDWR|O_NONBLOCK);
dev->fd = -1;
dev->subdev_fd = -1;
dev->allow_dma = true;
if(dev->fd < 0) {
if (dev->hw->device_open(dev) < 0) {
E_LOG_ERROR(dev, "Can't open device: %s", path);
}
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);
}
E_LOG_INFO(dev, "Device path=%s fd=%d opened", dev->path, dev->fd);
dev->subdev_fd = device_open_v4l2_subdev(dev, 0);
return dev;
error:
@ -50,14 +40,7 @@ void device_close(device_t *dev) {
dev->output_list = NULL;
}
if (dev->subdev_fd >= 0) {
close(dev->subdev_fd);
}
if(dev->fd >= 0) {
close(dev->fd);
}
dev->hw->device_close(dev);
free(dev->name);
free(dev->path);
free(dev);
@ -141,11 +124,14 @@ int device_open_buffer_list_capture(device_t *dev, buffer_list_t *output_list, f
int device_set_stream(device_t *dev, bool do_on)
{
// TODO: support events
#if 0
struct v4l2_event_subscription sub = {0};
sub.type = V4L2_EVENT_SOURCE_CHANGE;
E_LOG_DEBUG(dev, "Subscribing to DV-timings events ...");
xioctl(dev_name(dev), dev->fd, do_on ? VIDIOC_SUBSCRIBE_EVENT : VIDIOC_UNSUBSCRIBE_EVENT, &sub);
#endif
if (dev->capture_list) {
if (buffer_list_set_stream(dev->capture_list, do_on) < 0) {
@ -162,23 +148,11 @@ int device_set_stream(device_t *dev, bool do_on)
return 0;
}
int 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");
dev->decoder_started = do_on;
return 0;
error:
return -1;
}
int device_consume_event(device_t *dev)
{
// TODO: support events
#if 0
struct v4l2_event event;
if (!dev) {
@ -200,39 +174,69 @@ int device_consume_event(device_t *dev)
return 0;
error:
#endif
return -1;
}
int device_set_decoder_start(device_t *dev, bool do_on)
{
if (!dev || dev->hw->device_set_decoder_start(dev, do_on) < 0)
return -1;
dev->decoder_started = do_on;
return 0;
}
int device_video_force_key(device_t *dev)
{
if (!dev) {
if (!dev || dev->hw->device_video_force_key(dev) < 0)
return -1;
}
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 device_set_fps(device_t *dev, int desired_fps)
{
struct v4l2_streamparm setfps = {0};
if (!dev || dev->hw->device_set_fps(dev, desired_fps) < 0)
return -1;
return 0;
}
int device_set_option_string(device_t *dev, const char *key, const char *value)
{
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;
return dev->hw->device_set_option(dev, key, value);
}
void device_set_option_list(device_t *dev, const char *option_list)
{
if (!dev || !option_list || !option_list[0]) {
return;
}
char *start = strdup(option_list);
char *string = start;
char *option;
while (option = strsep(&string, OPTION_VALUE_LIST_SEP)) {
char *value = option;
char *key = strsep(&value, "=");
if (value) {
device_set_option_string(dev, key, value);
} else {
E_LOG_INFO(dev, "Missing 'key=value' for '%s'", option);
continue;
}
// consume all separators
while (strsep(&value, "="));
}
free(start);
}

View File

@ -10,6 +10,13 @@ typedef struct device_s device_t;
struct pollfd;
typedef struct device_hw_s {
int (*device_open)(device_t *dev);
void (*device_close)(device_t *dev);
int (*device_set_decoder_start)(device_t *dev, bool do_on);
int (*device_video_force_key)(device_t *dev);
int (*device_set_fps)(device_t *dev, int desired_fps);
int (*device_set_option)(device_t *dev, const char *key, const char *value);
int (*buffer_open)(buffer_t *buf);
void (*buffer_close)(buffer_t *buf);
int (*buffer_enqueue)(buffer_t *buf, const char *who);
@ -39,9 +46,6 @@ typedef struct device_s {
} device_t;
device_t *device_open(const char *name, const char *path, device_hw_t *hw);
device_t *device_v4l2_open(const char *name, const char *path);
int device_open_media_device(device_t *dev);
int device_open_v4l2_subdev(device_t *dev, int subdev);
void device_close(device_t *device);
int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, unsigned bytesperline, int nbufs, bool do_mmap);
@ -54,16 +58,7 @@ int device_set_decoder_start(device_t *dev, bool do_on);
int device_video_force_key(device_t *dev);
int device_set_fps(device_t *dev, int desired_fps);
int device_set_pad_format(device_t *device, unsigned width, unsigned height, unsigned format);
int device_set_option(device_t *dev, const char *name, uint32_t id, int32_t value);
int device_set_option_string(device_t *dev, const char *option);
int device_set_option_string(device_t *dev, const char *option, const char *value);
void device_set_option_list(device_t *dev, const char *option_list);
#define DEVICE_SET_OPTION(dev, name, value) \
device_set_option(dev, #name, V4L2_CID_##name, value)
#define DEVICE_SET_OPTION2(dev, type, name, value) \
device_set_option(dev, #name, V4L2_CID_##type##_##name, value)
#define DEVICE_SET_OPTION2_FATAL(dev, type, name, value) \
do { if (DEVICE_SET_OPTION2(dev, type, name, value) < 0) return -1; } while(0)
device_t *device_v4l2_open(const char *name, const char *path);

View File

@ -1,131 +0,0 @@
#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 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 device_open_v4l2_subdev(device_t *dev, int subdev)
{
int media_fd = -1;
unsigned int last_id = 0;
int ret = -1;
media_fd = 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 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

@ -1,191 +0,0 @@
#include "device/hw/device.h"
#include "device/hw/v4l2.h"
#include "opts/opts.h"
#include <ctype.h>
int device_set_option(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 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 device_set_option_string_fd_by_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;
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 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 = device_set_option_string_fd_by_id(dev, fd, &id, name, value)) == 0 && id) {
id |= V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
}
return ret;
}
int device_set_option_string(device_t *dev, const char *option)
{
int ret = -1;
char *name = strdup(option);
strcpy(name, option);
char *value = strchr(name, '=');
if (!value) {
E_LOG_ERROR(dev, "Missing 'key=value': '%s'", option);
}
*value++ = 0;
device_option_normalize_name(name);
if (dev->subdev_fd >= 0)
ret = device_set_option_string_fd(dev, dev->subdev_fd, name, value);
if (ret <= 0)
ret = device_set_option_string_fd(dev, dev->fd, name, value);
if (ret == 0)
E_LOG_ERROR(dev, "The '%s' was failed to find.", option);
else if (ret < 0)
E_LOG_ERROR(dev, "The '%s' did fail to be set.", option);
ret = 0;
error:
free(name);
return ret;
}
void device_set_option_list(device_t *dev, const char *option_list)
{
if (!dev || !option_list || !option_list[0]) {
return;
}
char *start = strdup(option_list);
char *string = start;
char *option;
while (option = strsep(&string, OPTION_VALUE_LIST_SEP)) {
device_set_option_string(dev, option);
}
free(start);
}

View File

@ -1,28 +1,4 @@
#include "device/hw/v4l2.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;
}
#include "v4l2.h"
fourcc_string fourcc_to_string(unsigned format)
{
@ -43,34 +19,3 @@ fourcc_string fourcc_to_string(unsigned format)
*ptr++ = 0;
return fourcc;
}
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

@ -21,23 +21,8 @@
#include "opts/log.h"
#ifndef CFG_XIOCTL_RETRIES
# define CFG_XIOCTL_RETRIES 4
#endif
#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
typedef struct {
char buf[10];
} fourcc_string;
fourcc_string fourcc_to_string(unsigned format);
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)