diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_ctrl.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 143 |
1 files changed, 129 insertions, 14 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f16aafe9cf14..2208165aa6f0 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -327,6 +327,31 @@ static struct uvc_menu_info exposure_auto_controls[] = { { 8, "Aperture Priority Mode" }, }; +static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + __s8 zoom = (__s8)data[0]; + + switch (query) { + case GET_CUR: + return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); + + case GET_MIN: + case GET_MAX: + case GET_RES: + case GET_DEF: + default: + return data[2]; + } +} + +static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[2] = min(abs(value), 0xff); +} + static struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -532,6 +557,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_ZOOM_ABSOLUTE, + .name = "Zoom, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_ZOOM_CONTINUOUS, + .name = "Zoom, Continuous", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_RELATIVE_CONTROL, + .size = 0, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_zoom, + .set = uvc_ctrl_set_zoom, + }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, }; /* ------------------------------------------------------------------------ @@ -543,18 +600,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) return ctrl->data + id * ctrl->info->size; } -static inline int uvc_get_bit(const __u8 *data, int bit) +static inline int uvc_test_bit(const __u8 *data, int bit) { return (data[bit >> 3] >> (bit & 7)) & 1; } +static inline void uvc_clear_bit(__u8 *data, int bit) +{ + data[bit >> 3] &= ~(1 << (bit & 7)); +} + /* Extract the bit string specified by mapping->offset and mapping->size * from the little-endian data stored at 'data' and return the result as * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. */ -static __s32 uvc_get_le_value(const __u8 *data, - struct uvc_control_mapping *mapping) +static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -583,8 +645,8 @@ static __s32 uvc_get_le_value(const __u8 *data, /* Set the bit string specified by mapping->offset and mapping->size * in the little-endian data stored at 'data' to the value 'value'. */ -static void uvc_set_le_value(__s32 value, __u8 *data, - struct uvc_control_mapping *mapping) +static void uvc_set_le_value(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -736,7 +798,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); } switch (mapping->v4l2_type) { @@ -772,21 +834,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->step = uvc_get_le_value(data, mapping); + v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); } ret = 0; @@ -923,8 +985,8 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = uvc_get_le_value( - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + xctrl->value = mapping->get(mapping, GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { menu = mapping->menu_info; @@ -980,8 +1042,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, ctrl->info->size); } - uvc_set_le_value(value, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + mapping->set(mapping, value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); ctrl->dirty = 1; ctrl->modified = 1; @@ -1257,6 +1319,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) struct uvc_control_mapping *map; int ret = -EINVAL; + if (mapping->get == NULL) + mapping->get = uvc_get_le_value; + if (mapping->set == NULL) + mapping->set = uvc_set_le_value; + if (mapping->id & ~V4L2_CTRL_ID_MASK) { uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " "invalid control id 0x%08x\n", mapping->name, @@ -1306,6 +1373,51 @@ end: } /* + * Prune an entity of its bogus controls. This currently includes processing + * unit auto controls for which no corresponding manual control is available. + * Such auto controls make little sense if any, and are known to crash at + * least the SiGma Micro webcam. + */ +static void +uvc_ctrl_prune_entity(struct uvc_entity *entity) +{ + static const struct { + u8 idx_manual; + u8 idx_auto; + } blacklist[] = { + { 2, 11 }, /* Hue */ + { 6, 12 }, /* White Balance Temperature */ + { 7, 13 }, /* White Balance Component */ + }; + + u8 *controls; + unsigned int size; + unsigned int i; + + if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + return; + + controls = entity->processing.bmControls; + size = entity->processing.bControlSize; + + for (i = 0; i < ARRAY_SIZE(blacklist); ++i) { + if (blacklist[i].idx_auto >= 8 * size || + blacklist[i].idx_manual >= 8 * size) + continue; + + if (!uvc_test_bit(controls, blacklist[i].idx_auto) || + uvc_test_bit(controls, blacklist[i].idx_manual)) + continue; + + uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no " + "matching manual control, removing it.\n", entity->id, + blacklist[i].idx_auto); + + uvc_clear_bit(controls, blacklist[i].idx_auto); + } +} + +/* * Initialize device controls. */ int uvc_ctrl_init_device(struct uvc_device *dev) @@ -1331,6 +1443,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev) bControlSize = entity->camera.bControlSize; } + if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS) + uvc_ctrl_prune_entity(entity); + for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); @@ -1345,7 +1460,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ctrl = entity->controls; for (i = 0; i < bControlSize * 8; ++i) { - if (uvc_get_bit(bmControls, i) == 0) + if (uvc_test_bit(bmControls, i) == 0) continue; ctrl->entity = entity; |