summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorDaniel Scally <dan.scally@ideasonboard.com>2023-03-09 10:58:25 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-03-29 08:51:26 +0200
commit7bf1c56a90decd3bec4d49815289d2e40bb771ae (patch)
tree01169e55e10ac21de4210df15b7836603cb4cd1a /drivers/usb/gadget
parent0d6a119cecd7ffa059970dbc5b557bfce737945f (diff)
downloadlinux-7bf1c56a90decd3bec4d49815289d2e40bb771ae.tar.gz
linux-7bf1c56a90decd3bec4d49815289d2e40bb771ae.tar.bz2
linux-7bf1c56a90decd3bec4d49815289d2e40bb771ae.zip
usb: gadget: uvc: Make bmControls attr read/write
For the Processing Unit and Camera Terminal descriptors defined in the UVC Gadget we currently hard-code values into their bmControls fields, which enumerates which controls the gadget is able to support. This isn't appropriate since only the userspace companion program to the kernel driver will know which controls are supported. Make the configfs attributes that point to those fields read/write so userspace can set them to appropriate values. Document the new behaviour at the same time so the functionality is clear. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Link: https://lore.kernel.org/r/20230309105825.216745-1-dan.scally@ideasonboard.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c121
1 files changed, 119 insertions, 2 deletions
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 62b759bb7613..9bf0e985acfa 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -334,6 +334,64 @@ UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, 8);
#undef UVCG_DEFAULT_PROCESSING_ATTR
+static ssize_t uvcg_default_processing_bm_controls_store(
+ struct config_item *item, const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvc_processing_unit_descriptor *pd;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ u8 *bm_controls, *tmp;
+ unsigned int i;
+ int ret, n = 0;
+
+ mutex_lock(su_mutex);
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ pd = &opts->uvc_processing;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n,
+ sizeof(u8));
+ if (ret)
+ goto unlock;
+
+ if (n > pd->bControlSize) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ tmp = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL);
+ if (!bm_controls) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &tmp,
+ sizeof(u8));
+ if (ret)
+ goto free_mem;
+
+ for (i = 0; i < n; i++)
+ pd->bmControls[i] = bm_controls[i];
+
+ ret = len;
+
+free_mem:
+ kfree(bm_controls);
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
static ssize_t uvcg_default_processing_bm_controls_show(
struct config_item *item, char *page)
{
@@ -363,7 +421,7 @@ static ssize_t uvcg_default_processing_bm_controls_show(
return result;
}
-UVC_ATTR_RO(uvcg_default_processing_, bm_controls, bmControls);
+UVC_ATTR(uvcg_default_processing_, bm_controls, bmControls);
static struct configfs_attribute *uvcg_default_processing_attrs[] = {
&uvcg_default_processing_attr_b_unit_id,
@@ -445,6 +503,65 @@ UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
#undef UVCG_DEFAULT_CAMERA_ATTR
+static ssize_t uvcg_default_camera_bm_controls_store(
+ struct config_item *item, const char *page, size_t len)
+{
+ struct config_group *group = to_config_group(item);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct uvc_camera_terminal_descriptor *cd;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ u8 *bm_controls, *tmp;
+ unsigned int i;
+ int ret, n = 0;
+
+ mutex_lock(su_mutex);
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent->
+ ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ cd = &opts->uvc_camera_terminal;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n,
+ sizeof(u8));
+ if (ret)
+ goto unlock;
+
+ if (n > cd->bControlSize) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ tmp = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL);
+ if (!bm_controls) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &tmp,
+ sizeof(u8));
+ if (ret)
+ goto free_mem;
+
+ for (i = 0; i < n; i++)
+ cd->bmControls[i] = bm_controls[i];
+
+ ret = len;
+
+free_mem:
+ kfree(bm_controls);
+unlock:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
static ssize_t uvcg_default_camera_bm_controls_show(
struct config_item *item, char *page)
{
@@ -474,7 +591,7 @@ static ssize_t uvcg_default_camera_bm_controls_show(
return result;
}
-UVC_ATTR_RO(uvcg_default_camera_, bm_controls, bmControls);
+UVC_ATTR(uvcg_default_camera_, bm_controls, bmControls);
static struct configfs_attribute *uvcg_default_camera_attrs[] = {
&uvcg_default_camera_attr_b_terminal_id,