mirror of
https://github.com/torvalds/linux.git
synced 2024-12-18 09:02:17 +00:00
[media] uvcvideo: Add UVCIOC_CTRL_QUERY ioctl
This ioctl extends UVCIOC_CTRL_GET/SET by not only allowing to get/set XU controls but to also send arbitrary UVC commands to XU controls, namely GET_CUR, SET_CUR, GET_MIN, GET_MAX, GET_RES, GET_LEN, GET_INFO and GET_DEF. This is required for applications to work with XU controls, so that they can properly query the size and allocate the necessary buffers. Signed-off-by: Martin Rubli <martin_rubli@logitech.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
837d50b564
commit
fe78d187fe
@ -1344,32 +1344,33 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
||||||
struct uvc_xu_control *xctrl, int set)
|
struct uvc_xu_control_query *xqry)
|
||||||
{
|
{
|
||||||
struct uvc_entity *entity;
|
struct uvc_entity *entity;
|
||||||
struct uvc_control *ctrl = NULL;
|
struct uvc_control *ctrl;
|
||||||
unsigned int i, found = 0;
|
unsigned int i, found = 0;
|
||||||
int restore = 0;
|
__u32 reqflags;
|
||||||
__u8 *data;
|
__u16 size;
|
||||||
|
__u8 *data = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Find the extension unit. */
|
/* Find the extension unit. */
|
||||||
list_for_each_entry(entity, &chain->entities, chain) {
|
list_for_each_entry(entity, &chain->entities, chain) {
|
||||||
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
|
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
|
||||||
entity->id == xctrl->unit)
|
entity->id == xqry->unit)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity->id != xctrl->unit) {
|
if (entity->id != xqry->unit) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
|
uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
|
||||||
xctrl->unit);
|
xqry->unit);
|
||||||
return -EINVAL;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the control and perform delayed initialization if needed. */
|
/* Find the control and perform delayed initialization if needed. */
|
||||||
for (i = 0; i < entity->ncontrols; ++i) {
|
for (i = 0; i < entity->ncontrols; ++i) {
|
||||||
ctrl = &entity->controls[i];
|
ctrl = &entity->controls[i];
|
||||||
if (ctrl->index == xctrl->selector - 1) {
|
if (ctrl->index == xqry->selector - 1) {
|
||||||
found = 1;
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1377,8 +1378,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
|||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
|
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
|
||||||
entity->extension.guidExtensionCode, xctrl->selector);
|
entity->extension.guidExtensionCode, xqry->selector);
|
||||||
return -EINVAL;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutex_lock_interruptible(&chain->ctrl_mutex))
|
if (mutex_lock_interruptible(&chain->ctrl_mutex))
|
||||||
@ -1390,43 +1391,72 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate control data size. */
|
/* Validate the required buffer size and flags for the request */
|
||||||
if (ctrl->info.size != xctrl->size) {
|
reqflags = 0;
|
||||||
|
size = ctrl->info.size;
|
||||||
|
|
||||||
|
switch (xqry->query) {
|
||||||
|
case UVC_GET_CUR:
|
||||||
|
reqflags = UVC_CONTROL_GET_CUR;
|
||||||
|
break;
|
||||||
|
case UVC_GET_MIN:
|
||||||
|
reqflags = UVC_CONTROL_GET_MIN;
|
||||||
|
break;
|
||||||
|
case UVC_GET_MAX:
|
||||||
|
reqflags = UVC_CONTROL_GET_MAX;
|
||||||
|
break;
|
||||||
|
case UVC_GET_DEF:
|
||||||
|
reqflags = UVC_CONTROL_GET_DEF;
|
||||||
|
break;
|
||||||
|
case UVC_GET_RES:
|
||||||
|
reqflags = UVC_CONTROL_GET_RES;
|
||||||
|
break;
|
||||||
|
case UVC_SET_CUR:
|
||||||
|
reqflags = UVC_CONTROL_SET_CUR;
|
||||||
|
break;
|
||||||
|
case UVC_GET_LEN:
|
||||||
|
size = 2;
|
||||||
|
break;
|
||||||
|
case UVC_GET_INFO:
|
||||||
|
size = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) ||
|
if (size != xqry->size) {
|
||||||
(!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) {
|
ret = -ENOBUFS;
|
||||||
ret = -EINVAL;
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
|
if (reqflags && !(ctrl->info.flags & reqflags)) {
|
||||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
|
ret = -EBADRQC;
|
||||||
ctrl->info.size);
|
goto done;
|
||||||
data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
|
}
|
||||||
restore = set;
|
|
||||||
|
|
||||||
if (set && copy_from_user(data, xctrl->data, xctrl->size)) {
|
data = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (data == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xqry->query == UVC_SET_CUR &&
|
||||||
|
copy_from_user(data, xqry->data, size)) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
|
ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit,
|
||||||
xctrl->unit, chain->dev->intfnum, xctrl->selector,
|
chain->dev->intfnum, xqry->selector, data, size);
|
||||||
data, xctrl->size);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (!set && copy_to_user(xctrl->data, data, xctrl->size))
|
if (xqry->query != UVC_SET_CUR &&
|
||||||
|
copy_to_user(xqry->data, data, size))
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
done:
|
done:
|
||||||
if (ret && restore)
|
kfree(data);
|
||||||
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
|
|
||||||
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
|
|
||||||
xctrl->size);
|
|
||||||
|
|
||||||
mutex_unlock(&chain->ctrl_mutex);
|
mutex_unlock(&chain->ctrl_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1029,10 +1029,23 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
|||||||
cmd == UVCIOC_CTRL_MAP_OLD);
|
cmd == UVCIOC_CTRL_MAP_OLD);
|
||||||
|
|
||||||
case UVCIOC_CTRL_GET:
|
case UVCIOC_CTRL_GET:
|
||||||
return uvc_xu_ctrl_query(chain, arg, 0);
|
|
||||||
|
|
||||||
case UVCIOC_CTRL_SET:
|
case UVCIOC_CTRL_SET:
|
||||||
return uvc_xu_ctrl_query(chain, arg, 1);
|
{
|
||||||
|
struct uvc_xu_control *xctrl = arg;
|
||||||
|
struct uvc_xu_control_query xqry = {
|
||||||
|
.unit = xctrl->unit,
|
||||||
|
.selector = xctrl->selector,
|
||||||
|
.query = cmd == UVCIOC_CTRL_GET
|
||||||
|
? UVC_GET_CUR : UVC_SET_CUR,
|
||||||
|
.size = xctrl->size,
|
||||||
|
.data = xctrl->data,
|
||||||
|
};
|
||||||
|
|
||||||
|
return uvc_xu_ctrl_query(chain, &xqry);
|
||||||
|
}
|
||||||
|
|
||||||
|
case UVCIOC_CTRL_QUERY:
|
||||||
|
return uvc_xu_ctrl_query(chain, arg);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
|
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
|
||||||
|
@ -57,7 +57,7 @@ struct uvc_xu_control_mapping {
|
|||||||
|
|
||||||
__u8 size;
|
__u8 size;
|
||||||
__u8 offset;
|
__u8 offset;
|
||||||
enum v4l2_ctrl_type v4l2_type;
|
__u32 v4l2_type;
|
||||||
__u32 data_type;
|
__u32 data_type;
|
||||||
|
|
||||||
struct uvc_menu_info __user *menu_info;
|
struct uvc_menu_info __user *menu_info;
|
||||||
@ -73,11 +73,20 @@ struct uvc_xu_control {
|
|||||||
__u8 __user *data;
|
__u8 __user *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct uvc_xu_control_query {
|
||||||
|
__u8 unit;
|
||||||
|
__u8 selector;
|
||||||
|
__u8 query;
|
||||||
|
__u16 size;
|
||||||
|
__u8 __user *data;
|
||||||
|
};
|
||||||
|
|
||||||
#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
|
#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
|
||||||
#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
|
#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
|
||||||
#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
|
#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
|
||||||
#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
|
#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
|
||||||
#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
|
#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
|
||||||
|
#define UVCIOC_CTRL_QUERY _IOWR('U', 5, struct uvc_xu_control_query)
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
@ -638,7 +647,7 @@ extern int uvc_ctrl_set(struct uvc_video_chain *chain,
|
|||||||
struct v4l2_ext_control *xctrl);
|
struct v4l2_ext_control *xctrl);
|
||||||
|
|
||||||
extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
|
||||||
struct uvc_xu_control *ctrl, int set);
|
struct uvc_xu_control_query *xqry);
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
|
extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
|
||||||
|
Loading…
Reference in New Issue
Block a user