diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-04-08 11:59:51 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-07 15:35:05 -0400 |
commit | b4012002f3a3983ba2797fde1613691d7f287048 (patch) | |
tree | 149ff2a22013fb77efd8bb374b9069083f4d5cd5 /drivers/media/video/uvc | |
parent | cb74d482f81bf6a3ef3e29cb228c917e371773f2 (diff) |
[media] uvcvideo: Add support for control events
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
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 | 121 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 43 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 20 |
3 files changed, 170 insertions, 14 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0c27cc154eaf..f15a437e5861 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/vmalloc.h> | 21 | #include <linux/vmalloc.h> |
22 | #include <linux/wait.h> | 22 | #include <linux/wait.h> |
23 | #include <linux/atomic.h> | 23 | #include <linux/atomic.h> |
24 | #include <media/v4l2-ctrls.h> | ||
24 | 25 | ||
25 | #include "uvcvideo.h" | 26 | #include "uvcvideo.h" |
26 | 27 | ||
@@ -1102,6 +1103,117 @@ done: | |||
1102 | return ret; | 1103 | return ret; |
1103 | } | 1104 | } |
1104 | 1105 | ||
1106 | /* -------------------------------------------------------------------------- | ||
1107 | * Ctrl event handling | ||
1108 | */ | ||
1109 | |||
1110 | static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, | ||
1111 | struct v4l2_event *ev, | ||
1112 | struct uvc_control *ctrl, | ||
1113 | struct uvc_control_mapping *mapping, | ||
1114 | s32 value, u32 changes) | ||
1115 | { | ||
1116 | struct v4l2_queryctrl v4l2_ctrl; | ||
1117 | |||
1118 | __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); | ||
1119 | |||
1120 | memset(ev->reserved, 0, sizeof(ev->reserved)); | ||
1121 | ev->type = V4L2_EVENT_CTRL; | ||
1122 | ev->id = v4l2_ctrl.id; | ||
1123 | ev->u.ctrl.value = value; | ||
1124 | ev->u.ctrl.changes = changes; | ||
1125 | ev->u.ctrl.type = v4l2_ctrl.type; | ||
1126 | ev->u.ctrl.flags = v4l2_ctrl.flags; | ||
1127 | ev->u.ctrl.minimum = v4l2_ctrl.minimum; | ||
1128 | ev->u.ctrl.maximum = v4l2_ctrl.maximum; | ||
1129 | ev->u.ctrl.step = v4l2_ctrl.step; | ||
1130 | ev->u.ctrl.default_value = v4l2_ctrl.default_value; | ||
1131 | } | ||
1132 | |||
1133 | static void uvc_ctrl_send_event(struct uvc_fh *handle, | ||
1134 | struct uvc_control *ctrl, struct uvc_control_mapping *mapping, | ||
1135 | s32 value, u32 changes) | ||
1136 | { | ||
1137 | struct v4l2_subscribed_event *sev; | ||
1138 | struct v4l2_event ev; | ||
1139 | |||
1140 | if (list_empty(&mapping->ev_subs)) | ||
1141 | return; | ||
1142 | |||
1143 | uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); | ||
1144 | |||
1145 | list_for_each_entry(sev, &mapping->ev_subs, node) { | ||
1146 | if (sev->fh && (sev->fh != &handle->vfh || | ||
1147 | (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))) | ||
1148 | v4l2_event_queue_fh(sev->fh, &ev); | ||
1149 | } | ||
1150 | } | ||
1151 | |||
1152 | static void uvc_ctrl_send_events(struct uvc_fh *handle, | ||
1153 | const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) | ||
1154 | { | ||
1155 | struct uvc_control_mapping *mapping; | ||
1156 | struct uvc_control *ctrl; | ||
1157 | unsigned int i; | ||
1158 | |||
1159 | for (i = 0; i < xctrls_count; ++i) { | ||
1160 | ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); | ||
1161 | uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, | ||
1162 | V4L2_EVENT_CTRL_CH_VALUE); | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev) | ||
1167 | { | ||
1168 | struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); | ||
1169 | struct uvc_control_mapping *mapping; | ||
1170 | struct uvc_control *ctrl; | ||
1171 | int ret; | ||
1172 | |||
1173 | ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); | ||
1174 | if (ret < 0) | ||
1175 | return -ERESTARTSYS; | ||
1176 | |||
1177 | ctrl = uvc_find_control(handle->chain, sev->id, &mapping); | ||
1178 | if (ctrl == NULL) { | ||
1179 | ret = -EINVAL; | ||
1180 | goto done; | ||
1181 | } | ||
1182 | |||
1183 | list_add_tail(&sev->node, &mapping->ev_subs); | ||
1184 | if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { | ||
1185 | struct v4l2_event ev; | ||
1186 | u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; | ||
1187 | s32 val = 0; | ||
1188 | |||
1189 | if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) | ||
1190 | changes |= V4L2_EVENT_CTRL_CH_VALUE; | ||
1191 | |||
1192 | uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, | ||
1193 | changes); | ||
1194 | v4l2_event_queue_fh(sev->fh, &ev); | ||
1195 | } | ||
1196 | |||
1197 | done: | ||
1198 | mutex_unlock(&handle->chain->ctrl_mutex); | ||
1199 | return ret; | ||
1200 | } | ||
1201 | |||
1202 | static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) | ||
1203 | { | ||
1204 | struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); | ||
1205 | |||
1206 | mutex_lock(&handle->chain->ctrl_mutex); | ||
1207 | list_del(&sev->node); | ||
1208 | mutex_unlock(&handle->chain->ctrl_mutex); | ||
1209 | } | ||
1210 | |||
1211 | const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { | ||
1212 | .add = uvc_ctrl_add_event, | ||
1213 | .del = uvc_ctrl_del_event, | ||
1214 | .replace = v4l2_ctrl_replace, | ||
1215 | .merge = v4l2_ctrl_merge, | ||
1216 | }; | ||
1105 | 1217 | ||
1106 | /* -------------------------------------------------------------------------- | 1218 | /* -------------------------------------------------------------------------- |
1107 | * Control transactions | 1219 | * Control transactions |
@@ -1179,8 +1291,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, | |||
1179 | return 0; | 1291 | return 0; |
1180 | } | 1292 | } |
1181 | 1293 | ||
1182 | int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) | 1294 | int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, |
1295 | const struct v4l2_ext_control *xctrls, | ||
1296 | unsigned int xctrls_count) | ||
1183 | { | 1297 | { |
1298 | struct uvc_video_chain *chain = handle->chain; | ||
1184 | struct uvc_entity *entity; | 1299 | struct uvc_entity *entity; |
1185 | int ret = 0; | 1300 | int ret = 0; |
1186 | 1301 | ||
@@ -1191,6 +1306,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) | |||
1191 | goto done; | 1306 | goto done; |
1192 | } | 1307 | } |
1193 | 1308 | ||
1309 | if (!rollback) | ||
1310 | uvc_ctrl_send_events(handle, xctrls, xctrls_count); | ||
1194 | done: | 1311 | done: |
1195 | mutex_unlock(&chain->ctrl_mutex); | 1312 | mutex_unlock(&chain->ctrl_mutex); |
1196 | return ret; | 1313 | return ret; |
@@ -1662,6 +1779,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, | |||
1662 | if (map == NULL) | 1779 | if (map == NULL) |
1663 | return -ENOMEM; | 1780 | return -ENOMEM; |
1664 | 1781 | ||
1782 | INIT_LIST_HEAD(&map->ev_subs); | ||
1783 | |||
1665 | size = sizeof(*mapping->menu_info) * mapping->menu_count; | 1784 | size = sizeof(*mapping->menu_info) * mapping->menu_count; |
1666 | map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); | 1785 | map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); |
1667 | if (map->menu_info == NULL) { | 1786 | if (map->menu_info == NULL) { |
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 4ef21e9cb28c..a3322ed81e39 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -25,6 +25,8 @@ | |||
25 | #include <linux/atomic.h> | 25 | #include <linux/atomic.h> |
26 | 26 | ||
27 | #include <media/v4l2-common.h> | 27 | #include <media/v4l2-common.h> |
28 | #include <media/v4l2-ctrls.h> | ||
29 | #include <media/v4l2-event.h> | ||
28 | #include <media/v4l2-ioctl.h> | 30 | #include <media/v4l2-ioctl.h> |
29 | 31 | ||
30 | #include "uvcvideo.h" | 32 | #include "uvcvideo.h" |
@@ -505,6 +507,8 @@ static int uvc_v4l2_open(struct file *file) | |||
505 | } | 507 | } |
506 | } | 508 | } |
507 | 509 | ||
510 | v4l2_fh_init(&handle->vfh, stream->vdev); | ||
511 | v4l2_fh_add(&handle->vfh); | ||
508 | handle->chain = stream->chain; | 512 | handle->chain = stream->chain; |
509 | handle->stream = stream; | 513 | handle->stream = stream; |
510 | handle->state = UVC_HANDLE_PASSIVE; | 514 | handle->state = UVC_HANDLE_PASSIVE; |
@@ -528,6 +532,8 @@ static int uvc_v4l2_release(struct file *file) | |||
528 | 532 | ||
529 | /* Release the file handle. */ | 533 | /* Release the file handle. */ |
530 | uvc_dismiss_privileges(handle); | 534 | uvc_dismiss_privileges(handle); |
535 | v4l2_fh_del(&handle->vfh); | ||
536 | v4l2_fh_exit(&handle->vfh); | ||
531 | kfree(handle); | 537 | kfree(handle); |
532 | file->private_data = NULL; | 538 | file->private_data = NULL; |
533 | 539 | ||
@@ -584,7 +590,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
584 | return ret; | 590 | return ret; |
585 | 591 | ||
586 | ret = uvc_ctrl_get(chain, &xctrl); | 592 | ret = uvc_ctrl_get(chain, &xctrl); |
587 | uvc_ctrl_rollback(chain); | 593 | uvc_ctrl_rollback(handle); |
588 | if (ret >= 0) | 594 | if (ret >= 0) |
589 | ctrl->value = xctrl.value; | 595 | ctrl->value = xctrl.value; |
590 | break; | 596 | break; |
@@ -605,10 +611,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
605 | 611 | ||
606 | ret = uvc_ctrl_set(chain, &xctrl); | 612 | ret = uvc_ctrl_set(chain, &xctrl); |
607 | if (ret < 0) { | 613 | if (ret < 0) { |
608 | uvc_ctrl_rollback(chain); | 614 | uvc_ctrl_rollback(handle); |
609 | return ret; | 615 | return ret; |
610 | } | 616 | } |
611 | ret = uvc_ctrl_commit(chain); | 617 | ret = uvc_ctrl_commit(handle, &xctrl, 1); |
612 | if (ret == 0) | 618 | if (ret == 0) |
613 | ctrl->value = xctrl.value; | 619 | ctrl->value = xctrl.value; |
614 | break; | 620 | break; |
@@ -630,13 +636,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
630 | for (i = 0; i < ctrls->count; ++ctrl, ++i) { | 636 | for (i = 0; i < ctrls->count; ++ctrl, ++i) { |
631 | ret = uvc_ctrl_get(chain, ctrl); | 637 | ret = uvc_ctrl_get(chain, ctrl); |
632 | if (ret < 0) { | 638 | if (ret < 0) { |
633 | uvc_ctrl_rollback(chain); | 639 | uvc_ctrl_rollback(handle); |
634 | ctrls->error_idx = i; | 640 | ctrls->error_idx = i; |
635 | return ret; | 641 | return ret; |
636 | } | 642 | } |
637 | } | 643 | } |
638 | ctrls->error_idx = 0; | 644 | ctrls->error_idx = 0; |
639 | ret = uvc_ctrl_rollback(chain); | 645 | ret = uvc_ctrl_rollback(handle); |
640 | break; | 646 | break; |
641 | } | 647 | } |
642 | 648 | ||
@@ -654,7 +660,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
654 | for (i = 0; i < ctrls->count; ++ctrl, ++i) { | 660 | for (i = 0; i < ctrls->count; ++ctrl, ++i) { |
655 | ret = uvc_ctrl_set(chain, ctrl); | 661 | ret = uvc_ctrl_set(chain, ctrl); |
656 | if (ret < 0) { | 662 | if (ret < 0) { |
657 | uvc_ctrl_rollback(chain); | 663 | uvc_ctrl_rollback(handle); |
658 | ctrls->error_idx = i; | 664 | ctrls->error_idx = i; |
659 | return ret; | 665 | return ret; |
660 | } | 666 | } |
@@ -663,9 +669,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
663 | ctrls->error_idx = 0; | 669 | ctrls->error_idx = 0; |
664 | 670 | ||
665 | if (cmd == VIDIOC_S_EXT_CTRLS) | 671 | if (cmd == VIDIOC_S_EXT_CTRLS) |
666 | ret = uvc_ctrl_commit(chain); | 672 | ret = uvc_ctrl_commit(handle, |
673 | ctrls->controls, ctrls->count); | ||
667 | else | 674 | else |
668 | ret = uvc_ctrl_rollback(chain); | 675 | ret = uvc_ctrl_rollback(handle); |
669 | break; | 676 | break; |
670 | } | 677 | } |
671 | 678 | ||
@@ -990,6 +997,26 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
990 | return uvc_video_enable(stream, 0); | 997 | return uvc_video_enable(stream, 0); |
991 | } | 998 | } |
992 | 999 | ||
1000 | case VIDIOC_SUBSCRIBE_EVENT: | ||
1001 | { | ||
1002 | struct v4l2_event_subscription *sub = arg; | ||
1003 | |||
1004 | switch (sub->type) { | ||
1005 | case V4L2_EVENT_CTRL: | ||
1006 | return v4l2_event_subscribe(&handle->vfh, sub, 0, | ||
1007 | &uvc_ctrl_sub_ev_ops); | ||
1008 | default: | ||
1009 | return -EINVAL; | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | case VIDIOC_UNSUBSCRIBE_EVENT: | ||
1014 | return v4l2_event_unsubscribe(&handle->vfh, arg); | ||
1015 | |||
1016 | case VIDIOC_DQEVENT: | ||
1017 | return v4l2_event_dequeue(&handle->vfh, arg, | ||
1018 | file->f_flags & O_NONBLOCK); | ||
1019 | |||
993 | /* Analog video standards make no sense for digital cameras. */ | 1020 | /* Analog video standards make no sense for digital cameras. */ |
994 | case VIDIOC_ENUMSTD: | 1021 | case VIDIOC_ENUMSTD: |
995 | case VIDIOC_QUERYSTD: | 1022 | case VIDIOC_QUERYSTD: |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 67f88d85bb16..e43deb715d98 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -13,6 +13,8 @@ | |||
13 | #include <linux/videodev2.h> | 13 | #include <linux/videodev2.h> |
14 | #include <media/media-device.h> | 14 | #include <media/media-device.h> |
15 | #include <media/v4l2-device.h> | 15 | #include <media/v4l2-device.h> |
16 | #include <media/v4l2-event.h> | ||
17 | #include <media/v4l2-fh.h> | ||
16 | #include <media/videobuf2-core.h> | 18 | #include <media/videobuf2-core.h> |
17 | 19 | ||
18 | /* -------------------------------------------------------------------------- | 20 | /* -------------------------------------------------------------------------- |
@@ -153,6 +155,7 @@ struct uvc_control_info { | |||
153 | 155 | ||
154 | struct uvc_control_mapping { | 156 | struct uvc_control_mapping { |
155 | struct list_head list; | 157 | struct list_head list; |
158 | struct list_head ev_subs; | ||
156 | 159 | ||
157 | struct uvc_control_info *ctrl; | 160 | struct uvc_control_info *ctrl; |
158 | 161 | ||
@@ -524,6 +527,7 @@ enum uvc_handle_state { | |||
524 | }; | 527 | }; |
525 | 528 | ||
526 | struct uvc_fh { | 529 | struct uvc_fh { |
530 | struct v4l2_fh vfh; | ||
527 | struct uvc_video_chain *chain; | 531 | struct uvc_video_chain *chain; |
528 | struct uvc_streaming *stream; | 532 | struct uvc_streaming *stream; |
529 | enum uvc_handle_state state; | 533 | enum uvc_handle_state state; |
@@ -643,6 +647,8 @@ extern int uvc_status_suspend(struct uvc_device *dev); | |||
643 | extern int uvc_status_resume(struct uvc_device *dev); | 647 | extern int uvc_status_resume(struct uvc_device *dev); |
644 | 648 | ||
645 | /* Controls */ | 649 | /* Controls */ |
650 | extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; | ||
651 | |||
646 | extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, | 652 | extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, |
647 | struct v4l2_queryctrl *v4l2_ctrl); | 653 | struct v4l2_queryctrl *v4l2_ctrl); |
648 | extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, | 654 | extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, |
@@ -655,14 +661,18 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); | |||
655 | extern int uvc_ctrl_resume_device(struct uvc_device *dev); | 661 | extern int uvc_ctrl_resume_device(struct uvc_device *dev); |
656 | 662 | ||
657 | extern int uvc_ctrl_begin(struct uvc_video_chain *chain); | 663 | extern int uvc_ctrl_begin(struct uvc_video_chain *chain); |
658 | extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); | 664 | extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, |
659 | static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) | 665 | const struct v4l2_ext_control *xctrls, |
666 | unsigned int xctrls_count); | ||
667 | static inline int uvc_ctrl_commit(struct uvc_fh *handle, | ||
668 | const struct v4l2_ext_control *xctrls, | ||
669 | unsigned int xctrls_count) | ||
660 | { | 670 | { |
661 | return __uvc_ctrl_commit(chain, 0); | 671 | return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); |
662 | } | 672 | } |
663 | static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) | 673 | static inline int uvc_ctrl_rollback(struct uvc_fh *handle) |
664 | { | 674 | { |
665 | return __uvc_ctrl_commit(chain, 1); | 675 | return __uvc_ctrl_commit(handle, 1, NULL, 0); |
666 | } | 676 | } |
667 | 677 | ||
668 | extern int uvc_ctrl_get(struct uvc_video_chain *chain, | 678 | extern int uvc_ctrl_get(struct uvc_video_chain *chain, |