diff --git a/device/hw/device_options.c b/device/hw/device_options.c index 49741a2..d13927d 100644 --- a/device/hw/device_options.c +++ b/device/hw/device_options.c @@ -38,50 +38,110 @@ void device_option_normalize_name(char *in) *out++ = 0; } -int device_set_option_string_fd(device_t *dev, int fd, const char *name, const char *value) +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 = {0}; - struct v4l2_control ctl = {0}; + struct v4l2_query_ext_ctrl qctrl = { .id = *id }; + void *data = NULL; - qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; - while (0 == ioctl (fd, VIDIOC_QUERY_EXT_CTRL, &qctrl)) { - ctl.id = qctrl.id; - qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + if (0 != ioctl (fd, VIDIOC_QUERY_EXT_CTRL, &qctrl)) { + *id = 0; + return 0; + } + *id = qctrl.id; - device_option_normalize_name(qctrl.name); + device_option_normalize_name(qctrl.name); - if (strcmp(qctrl.name, name) != 0) - continue; + if (strcmp(qctrl.name, name) != 0) + return 0; - if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) { - E_LOG_INFO(dev, "The '%s' is disabled", name); - continue; - } else if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) { - E_LOG_INFO(dev, "The '%s' is read-only", name); - continue; - } - - switch(qctrl.type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - ctl.value = atoi(value); - E_LOG_VERBOSE(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; - - default: - E_LOG_INFO(dev, "The '%s' control type '%d' is not supported", name, qctrl.type); - 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; } - 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; @@ -96,7 +156,8 @@ int device_set_option_string(device_t *dev, const char *option) device_option_normalize_name(name); - ret = device_set_option_string_fd(dev, dev->subdev_fd, name, value); + 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)