diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2010-02-18 14:38:52 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 14:33:39 -0400 |
commit | 561474c2d2a1e212ea186e0b65cc69fb330e7bd5 (patch) | |
tree | ff01d7c41345625982ec594fe80b85dfb73b0942 /drivers/media/video/uvc | |
parent | 3653639e5daf2ac5f4763e4f1b6cb57538184be9 (diff) |
V4L/DVB: uvcvideo: Support menu controls in the control mapping API
The UVCIOC_CTRL_MAP ioctl doesn't support menu entries for menu
controls. As the uvc_xu_control_mapping structure has no reserved
fields, this can't be fixed while keeping ABI compatibility.
Modify the UVCIOC_CTRL_MAP ioctl to add menu entries support, and define
UVCIOC_CTRL_MAP_OLD that supports the old ABI without any ability to add
menu controls.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 22 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_driver.c | 1 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 92 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 23 |
4 files changed, 110 insertions, 28 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 4e6d484911fe..bd72100a21dd 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -1606,6 +1606,28 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) | |||
1606 | } | 1606 | } |
1607 | } | 1607 | } |
1608 | 1608 | ||
1609 | void uvc_ctrl_cleanup(void) | ||
1610 | { | ||
1611 | struct uvc_control_info *info; | ||
1612 | struct uvc_control_info *ni; | ||
1613 | struct uvc_control_mapping *mapping; | ||
1614 | struct uvc_control_mapping *nm; | ||
1615 | |||
1616 | list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) { | ||
1617 | if (!(info->flags & UVC_CONTROL_EXTENSION)) | ||
1618 | continue; | ||
1619 | |||
1620 | list_for_each_entry_safe(mapping, nm, &info->mappings, list) { | ||
1621 | list_del(&mapping->list); | ||
1622 | kfree(mapping->menu_info); | ||
1623 | kfree(mapping); | ||
1624 | } | ||
1625 | |||
1626 | list_del(&info->list); | ||
1627 | kfree(info); | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1609 | void uvc_ctrl_init(void) | 1631 | void uvc_ctrl_init(void) |
1610 | { | 1632 | { |
1611 | struct uvc_control_info *ctrl = uvc_ctrls; | 1633 | struct uvc_control_info *ctrl = uvc_ctrls; |
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 45aac1041862..c5258b30777f 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c | |||
@@ -2260,6 +2260,7 @@ static int __init uvc_init(void) | |||
2260 | static void __exit uvc_cleanup(void) | 2260 | static void __exit uvc_cleanup(void) |
2261 | { | 2261 | { |
2262 | usb_deregister(&uvc_driver.driver); | 2262 | usb_deregister(&uvc_driver.driver); |
2263 | uvc_ctrl_cleanup(); | ||
2263 | } | 2264 | } |
2264 | 2265 | ||
2265 | module_init(uvc_init); | 2266 | module_init(uvc_init); |
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 7c9ab2933496..485a89912cef 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -29,6 +29,71 @@ | |||
29 | #include "uvcvideo.h" | 29 | #include "uvcvideo.h" |
30 | 30 | ||
31 | /* ------------------------------------------------------------------------ | 31 | /* ------------------------------------------------------------------------ |
32 | * UVC ioctls | ||
33 | */ | ||
34 | static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) | ||
35 | { | ||
36 | struct uvc_control_mapping *map; | ||
37 | unsigned int size; | ||
38 | int ret; | ||
39 | |||
40 | map = kzalloc(sizeof *map, GFP_KERNEL); | ||
41 | if (map == NULL) | ||
42 | return -ENOMEM; | ||
43 | |||
44 | map->id = xmap->id; | ||
45 | memcpy(map->name, xmap->name, sizeof map->name); | ||
46 | memcpy(map->entity, xmap->entity, sizeof map->entity); | ||
47 | map->selector = xmap->selector; | ||
48 | map->size = xmap->size; | ||
49 | map->offset = xmap->offset; | ||
50 | map->v4l2_type = xmap->v4l2_type; | ||
51 | map->data_type = xmap->data_type; | ||
52 | |||
53 | switch (xmap->v4l2_type) { | ||
54 | case V4L2_CTRL_TYPE_INTEGER: | ||
55 | case V4L2_CTRL_TYPE_BOOLEAN: | ||
56 | case V4L2_CTRL_TYPE_BUTTON: | ||
57 | break; | ||
58 | |||
59 | case V4L2_CTRL_TYPE_MENU: | ||
60 | if (old) { | ||
61 | ret = -EINVAL; | ||
62 | goto done; | ||
63 | } | ||
64 | |||
65 | size = xmap->menu_count * sizeof(*map->menu_info); | ||
66 | map->menu_info = kmalloc(size, GFP_KERNEL); | ||
67 | if (map->menu_info == NULL) { | ||
68 | ret = -ENOMEM; | ||
69 | goto done; | ||
70 | } | ||
71 | |||
72 | if (copy_from_user(map->menu_info, xmap->menu_info, size)) { | ||
73 | ret = -EFAULT; | ||
74 | goto done; | ||
75 | } | ||
76 | |||
77 | map->menu_count = xmap->menu_count; | ||
78 | break; | ||
79 | |||
80 | default: | ||
81 | ret = -EINVAL; | ||
82 | goto done; | ||
83 | } | ||
84 | |||
85 | ret = uvc_ctrl_add_mapping(map); | ||
86 | |||
87 | done: | ||
88 | if (ret < 0) { | ||
89 | kfree(map->menu_info); | ||
90 | kfree(map); | ||
91 | } | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | /* ------------------------------------------------------------------------ | ||
32 | * V4L2 interface | 97 | * V4L2 interface |
33 | */ | 98 | */ |
34 | 99 | ||
@@ -974,7 +1039,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
974 | info->flags = xinfo->flags; | 1039 | info->flags = xinfo->flags; |
975 | 1040 | ||
976 | info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | | 1041 | info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | |
977 | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; | 1042 | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | |
1043 | UVC_CONTROL_EXTENSION; | ||
978 | 1044 | ||
979 | ret = uvc_ctrl_add_info(info); | 1045 | ret = uvc_ctrl_add_info(info); |
980 | if (ret < 0) | 1046 | if (ret < 0) |
@@ -982,32 +1048,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
982 | break; | 1048 | break; |
983 | } | 1049 | } |
984 | 1050 | ||
1051 | case UVCIOC_CTRL_MAP_OLD: | ||
985 | case UVCIOC_CTRL_MAP: | 1052 | case UVCIOC_CTRL_MAP: |
986 | { | ||
987 | struct uvc_xu_control_mapping *xmap = arg; | ||
988 | struct uvc_control_mapping *map; | ||
989 | |||
990 | if (!capable(CAP_SYS_ADMIN)) | 1053 | if (!capable(CAP_SYS_ADMIN)) |
991 | return -EPERM; | 1054 | return -EPERM; |
992 | 1055 | ||
993 | map = kzalloc(sizeof *map, GFP_KERNEL); | 1056 | return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD); |
994 | if (map == NULL) | ||
995 | return -ENOMEM; | ||
996 | |||
997 | map->id = xmap->id; | ||
998 | memcpy(map->name, xmap->name, sizeof map->name); | ||
999 | memcpy(map->entity, xmap->entity, sizeof map->entity); | ||
1000 | map->selector = xmap->selector; | ||
1001 | map->size = xmap->size; | ||
1002 | map->offset = xmap->offset; | ||
1003 | map->v4l2_type = xmap->v4l2_type; | ||
1004 | map->data_type = xmap->data_type; | ||
1005 | |||
1006 | ret = uvc_ctrl_add_mapping(map); | ||
1007 | if (ret < 0) | ||
1008 | kfree(map); | ||
1009 | break; | ||
1010 | } | ||
1011 | 1057 | ||
1012 | case UVCIOC_CTRL_GET: | 1058 | case UVCIOC_CTRL_GET: |
1013 | return uvc_xu_ctrl_query(chain, arg, 0); | 1059 | return uvc_xu_ctrl_query(chain, arg, 0); |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d1f88406a5e7..14f77e42fd45 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -27,6 +27,8 @@ | |||
27 | #define UVC_CONTROL_RESTORE (1 << 6) | 27 | #define UVC_CONTROL_RESTORE (1 << 6) |
28 | /* Control can be updated by the camera. */ | 28 | /* Control can be updated by the camera. */ |
29 | #define UVC_CONTROL_AUTO_UPDATE (1 << 7) | 29 | #define UVC_CONTROL_AUTO_UPDATE (1 << 7) |
30 | /* Control is an extension unit control. */ | ||
31 | #define UVC_CONTROL_EXTENSION (1 << 8) | ||
30 | 32 | ||
31 | #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ | 33 | #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ |
32 | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ | 34 | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ |
@@ -40,6 +42,15 @@ struct uvc_xu_control_info { | |||
40 | __u32 flags; | 42 | __u32 flags; |
41 | }; | 43 | }; |
42 | 44 | ||
45 | struct uvc_menu_info { | ||
46 | __u32 value; | ||
47 | __u8 name[32]; | ||
48 | }; | ||
49 | |||
50 | struct uvc_xu_control_mapping_old { | ||
51 | __u8 reserved[64]; | ||
52 | }; | ||
53 | |||
43 | struct uvc_xu_control_mapping { | 54 | struct uvc_xu_control_mapping { |
44 | __u32 id; | 55 | __u32 id; |
45 | __u8 name[32]; | 56 | __u8 name[32]; |
@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping { | |||
50 | __u8 offset; | 61 | __u8 offset; |
51 | enum v4l2_ctrl_type v4l2_type; | 62 | enum v4l2_ctrl_type v4l2_type; |
52 | __u32 data_type; | 63 | __u32 data_type; |
64 | |||
65 | struct uvc_menu_info __user *menu_info; | ||
66 | __u32 menu_count; | ||
67 | |||
68 | __u32 reserved[4]; | ||
53 | }; | 69 | }; |
54 | 70 | ||
55 | struct uvc_xu_control { | 71 | struct uvc_xu_control { |
@@ -60,6 +76,7 @@ struct uvc_xu_control { | |||
60 | }; | 76 | }; |
61 | 77 | ||
62 | #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) | 78 | #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) |
79 | #define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) | ||
63 | #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) | 80 | #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) |
64 | #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) | 81 | #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) |
65 | #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) | 82 | #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) |
@@ -198,11 +215,6 @@ struct uvc_streaming_control { | |||
198 | __u8 bMaxVersion; | 215 | __u8 bMaxVersion; |
199 | }; | 216 | }; |
200 | 217 | ||
201 | struct uvc_menu_info { | ||
202 | __u32 value; | ||
203 | __u8 name[32]; | ||
204 | }; | ||
205 | |||
206 | struct uvc_control_info { | 218 | struct uvc_control_info { |
207 | struct list_head list; | 219 | struct list_head list; |
208 | struct list_head mappings; | 220 | struct list_head mappings; |
@@ -625,6 +637,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev); | |||
625 | extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); | 637 | extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); |
626 | extern int uvc_ctrl_resume_device(struct uvc_device *dev); | 638 | extern int uvc_ctrl_resume_device(struct uvc_device *dev); |
627 | extern void uvc_ctrl_init(void); | 639 | extern void uvc_ctrl_init(void); |
640 | extern void uvc_ctrl_cleanup(void); | ||
628 | 641 | ||
629 | extern int uvc_ctrl_begin(struct uvc_video_chain *chain); | 642 | extern int uvc_ctrl_begin(struct uvc_video_chain *chain); |
630 | extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); | 643 | extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); |