📄 uvc_ctrl.c
字号:
__u32 v4l2_id, struct uvc_control_mapping **mapping){ struct uvc_control *ctrl; struct uvc_entity *entity; unsigned int i; /* Find the control. */ ctrl = __uvc_find_control(video->processing, v4l2_id, mapping); if (ctrl) return ctrl; ctrl = __uvc_find_control(video->iterm, v4l2_id, mapping); if (ctrl) return ctrl; for (i = 0; i < 8; ++i) { entity = video->extension[i]; if (entity == NULL) break; ctrl = __uvc_find_control(entity, v4l2_id, mapping); if (ctrl) return ctrl; } uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", v4l2_id); return NULL;}int uvc_query_v4l2_ctrl(struct uvc_video_device *video, struct v4l2_queryctrl *v4l2_ctrl){ struct uvc_control *ctrl; struct uvc_control_mapping *mapping; __u8 data[4]; int ret = 0; ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); if (ctrl == NULL) { /* If the V4L2 control ID is in the private control ID range, * there's a chance the application is enumerating our private * controls, so we can't return EINVAL because (according to the * V4L2 spec) it will think that this was the last one. However, * it might just be this particular control that is not * supported and we want the enumeration to continue. */ if (v4l2_ctrl->id < V4L2_CID_PRIVATE_BASE || v4l2_ctrl->id > V4L2_CID_PRIVATE_LAST) { return -EINVAL; } else { v4l2_ctrl->name[0] = '\0'; v4l2_ctrl->flags = V4L2_CTRL_FLAG_DISABLED; return 0; } } if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { ret |= uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, &data, ctrl->info->size); v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); } if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { ret |= uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, &data, ctrl->info->size); v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { ret |= uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, &data, ctrl->info->size); v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { ret |= uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, &data, ctrl->info->size); v4l2_ctrl->step = uvc_get_le_value(data, mapping); } if (ret != 0) return -EIO; v4l2_ctrl->type = mapping->v4l2_type; strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); v4l2_ctrl->flags = 0; return 0;}/* -------------------------------------------------------------------------- * Control transactions * * To make extended set operations as atomic as the hardware allows, controls * are handled using begin/commit/rollback operations. * * At the beginning of a set request, uvc_ctrl_begin should be called to * initialize the request. This function acquires the control lock. * * When setting a control, the new value is stored in the control data field * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for * later processing. If the UVC and V4L2 control sizes differ, the current * value is loaded from the hardware before storing the new value in the data * field. * * After processing all controls in the transaction, uvc_ctrl_commit or * uvc_ctrl_rollback must be called to apply the pending changes to the * hardware or revert them. When applying changes, all controls marked as * dirty will be modified in the UVC device, and the dirty flag will be * cleared. When reverting controls, the control data field * UVC_CTRL_DATA_CURRENT is reverted to its previous value * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the * control lock. */int uvc_ctrl_begin(struct uvc_video_device *video){ return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;}static int uvc_ctrl_commit_entity(struct uvc_video_device *video, struct uvc_entity *entity, int rollback){ struct uvc_control *ctrl; unsigned int i; for (i = 0; i < entity->ncontrols; ++i) { ctrl = &entity->controls[i]; if (ctrl->info == NULL || !ctrl->dirty) continue; if (rollback) memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), ctrl->info->size); else uvc_query_ctrl(video->dev, SET_CUR, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) ctrl->loaded = 0; ctrl->dirty = 0; } return 0;}int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback){ struct uvc_entity *entity; unsigned int i; int ret = 0; /* Find the control. */ ret = uvc_ctrl_commit_entity(video, video->processing, rollback); if (ret < 0) goto done; ret = uvc_ctrl_commit_entity(video, video->iterm, rollback); if (ret < 0) goto done; for (i = 0; i < 8; ++i) { entity = video->extension[i]; if (entity == NULL) break; ret = uvc_ctrl_commit_entity(video, entity, rollback); if (ret < 0) goto done; }done: mutex_unlock(&video->ctrl_mutex); return ret;}int uvc_ctrl_get(struct uvc_video_device *video, struct v4l2_ext_control *xctrl){ struct uvc_control *ctrl; struct uvc_control_mapping *mapping; int ret; ctrl = uvc_find_control(video, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if (ret < 0) return ret; ctrl->loaded = 1; } xctrl->value = uvc_get_le_value( uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); return 0;}int uvc_ctrl_set(struct uvc_video_device *video, struct v4l2_ext_control *xctrl){ struct uvc_control *ctrl; struct uvc_control_mapping *mapping; int ret; ctrl = uvc_find_control(video, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) return -EINVAL; if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 0, ctrl->info->size); } else { ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if (ret < 0) return ret; } ctrl->loaded = 1; } if (!ctrl->dirty) { memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); } uvc_set_le_value(xctrl->value, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); ctrl->dirty = 1; return 0;}/* -------------------------------------------------------------------------- * Control and mapping handling */static void uvc_ctrl_add_ctrl(struct uvc_device *dev, struct uvc_control_info *info){ struct uvc_entity *entity; struct uvc_control *ctrl; unsigned int i; list_for_each_entry(entity, &dev->entities, list) { if (!uvc_entity_match_guid(entity, info->entity)) continue; for (i = 0; i < entity->ncontrols; ++i) { ctrl = &entity->controls[i]; if (ctrl->index == info->index) { ctrl->info = info; ctrl->data = kmalloc(info->size * UVC_CTRL_NDATA, GFP_KERNEL); uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), ctrl->info->selector, dev->udev->devpath, entity->id); break; } } }}/* * Add an item to the UVC control information list, and instanciate a control * structure for each device that supports the control. */void uvc_ctrl_add_info(struct uvc_control_info *info){ struct uvc_control_info *ctrl; struct uvc_device *dev; /* Find matching controls by walking the devices, entities and * controls list. */ mutex_lock(&uvc_driver.ctrl_mutex); /* First check if the list contains a control matching the new one. * Bail out if it does. */ list_for_each_entry(ctrl, &uvc_driver.controls, list) { if (memcmp(ctrl->entity, info->entity, 16)) continue; if (ctrl->selector == info->selector) { uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT "/%u is already defined.\n", UVC_GUID_ARGS(info->entity), info->selector); goto end; } if (ctrl->index == info->index) { uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT "/%u would overwrite index " "%d.\n", UVC_GUID_ARGS(info->entity), info->selector, info->index); goto end; } } list_for_each_entry(dev, &uvc_driver.devices, list) uvc_ctrl_add_ctrl(dev, info); INIT_LIST_HEAD(&info->mappings); list_add_tail(&info->list, &uvc_driver.controls);end: mutex_unlock(&uvc_driver.ctrl_mutex);}void uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping){ struct uvc_control_info *info; struct uvc_control_mapping *map; mutex_lock(&uvc_driver.ctrl_mutex); list_for_each_entry(info, &uvc_driver.controls, list) { if (memcmp(info->entity, mapping->entity, 16) || info->selector != mapping->selector) continue; if (info->size * 8 < mapping->size + mapping->offset) { uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " "overflow control " UVC_GUID_FORMAT "/%u\n", mapping->name, UVC_GUID_ARGS(info->entity), info->selector); goto end; } /* Check if the list contains a mapping matching the new one. * Bail out if it does. */ list_for_each_entry(map, &info->mappings, list) { if (map->id == mapping->id) { uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is already " "defined.\n", mapping->name); goto end; } } mapping->ctrl = info; list_add_tail(&mapping->list, &info->mappings); uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " UVC_GUID_FORMAT "/%u.\n", mapping->name, UVC_GUID_ARGS(info->entity), info->selector); break; }end: mutex_unlock(&uvc_driver.ctrl_mutex);}/* * Initialize device controls. */int uvc_ctrl_init_device(struct uvc_device *dev){ struct uvc_control_info *info; struct uvc_control *ctrl; struct uvc_entity *entity; unsigned int i; /* Walk the entities list and instanciate controls */ list_for_each_entry(entity, &dev->entities, list) { unsigned int bControlSize = 0, ncontrols = 0; __u8 *bmControls = NULL; if (entity->type == VC_EXTENSION_UNIT) { bmControls = entity->extension.bmControls; bControlSize = entity->extension.bControlSize; } else if (entity->type == VC_PROCESSING_UNIT) { bmControls = entity->processing.bmControls; bControlSize = entity->processing.bControlSize; } else if (entity->type == ITT_CAMERA) { bmControls = entity->camera.bmControls; bControlSize = entity->camera.bControlSize; } for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); if (ncontrols == 0) continue; entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); if (entity->controls == NULL) return -ENOMEM; entity->ncontrols = ncontrols; ctrl = entity->controls; for (i = 0; i < bControlSize * 8; ++i) { if (uvc_get_bit(bmControls, i) == 0) continue; ctrl->entity = entity; ctrl->index = i; ctrl++; } } /* Walk the controls info list and associate them with the device * controls, then add the device to the global device list. This has * to be done while holding the controls lock, to make sure * uvc_ctrl_add_info() will not get called in-between. */ mutex_lock(&uvc_driver.ctrl_mutex); list_for_each_entry(info, &uvc_driver.controls, list) uvc_ctrl_add_ctrl(dev, info); list_add_tail(&dev->list, &uvc_driver.devices); mutex_unlock(&uvc_driver.ctrl_mutex); return 0;}/* * Cleanup device controls. */void uvc_ctrl_cleanup_device(struct uvc_device *dev){ struct uvc_entity *entity; unsigned int i; /* Remove the device from the global devices list */ mutex_lock(&uvc_driver.ctrl_mutex); list_del(&dev->list); mutex_unlock(&uvc_driver.ctrl_mutex); list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) kfree(entity->controls[i].data); kfree(entity->controls); }}void uvc_ctrl_init(void){ struct uvc_control_info *ctrl = uvc_ctrls; struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls); struct uvc_control_mapping *mapping = uvc_ctrl_mappings; struct uvc_control_mapping *mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings); for (; ctrl < cend; ++ctrl) uvc_ctrl_add_info(ctrl); for (; mapping < mend; ++mapping) uvc_ctrl_add_mapping(mapping);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -