162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * uvc_configfs.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Configfs support for the uvc function. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd. 862306a36Sopenharmony_ci * http://www.samsung.com 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "uvc_configfs.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/sort.h> 1662306a36Sopenharmony_ci#include <linux/usb/video.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 1962306a36Sopenharmony_ci * Global Utility Structures and Macros 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define UVC_ATTR(prefix, cname, aname) \ 2362306a36Sopenharmony_cistatic struct configfs_attribute prefix##attr_##cname = { \ 2462306a36Sopenharmony_ci .ca_name = __stringify(aname), \ 2562306a36Sopenharmony_ci .ca_mode = S_IRUGO | S_IWUGO, \ 2662306a36Sopenharmony_ci .ca_owner = THIS_MODULE, \ 2762306a36Sopenharmony_ci .show = prefix##cname##_show, \ 2862306a36Sopenharmony_ci .store = prefix##cname##_store, \ 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define UVC_ATTR_RO(prefix, cname, aname) \ 3262306a36Sopenharmony_cistatic struct configfs_attribute prefix##attr_##cname = { \ 3362306a36Sopenharmony_ci .ca_name = __stringify(aname), \ 3462306a36Sopenharmony_ci .ca_mode = S_IRUGO, \ 3562306a36Sopenharmony_ci .ca_owner = THIS_MODULE, \ 3662306a36Sopenharmony_ci .show = prefix##cname##_show, \ 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define le8_to_cpu(x) (x) 4062306a36Sopenharmony_ci#define cpu_to_le8(x) (x) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int uvcg_config_compare_u32(const void *l, const void *r) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 li = *(const u32 *)l; 4562306a36Sopenharmony_ci u32 ri = *(const u32 *)r; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return li < ri ? -1 : li == ri ? 0 : 1; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic inline int __uvcg_count_item_entries(char *buf, void *priv, unsigned int size) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci ++*((int *)priv); 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic inline int __uvcg_fill_item_entries(char *buf, void *priv, unsigned int size) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci unsigned int num; 5962306a36Sopenharmony_ci u8 **values; 6062306a36Sopenharmony_ci int ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = kstrtouint(buf, 0, &num); 6362306a36Sopenharmony_ci if (ret) 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (num != (num & GENMASK((size * 8) - 1, 0))) 6762306a36Sopenharmony_ci return -ERANGE; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci values = priv; 7062306a36Sopenharmony_ci memcpy(*values, &num, size); 7162306a36Sopenharmony_ci *values += size; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int __uvcg_iter_item_entries(const char *page, size_t len, 7762306a36Sopenharmony_ci int (*fun)(char *, void *, unsigned int), 7862306a36Sopenharmony_ci void *priv, unsigned int size) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci /* sign, base 2 representation, newline, terminator */ 8162306a36Sopenharmony_ci unsigned int bufsize = 1 + size * 8 + 1 + 1; 8262306a36Sopenharmony_ci const char *pg = page; 8362306a36Sopenharmony_ci int i, ret = 0; 8462306a36Sopenharmony_ci char *buf; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!fun) 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci buf = kzalloc(bufsize, GFP_KERNEL); 9062306a36Sopenharmony_ci if (!buf) 9162306a36Sopenharmony_ci return -ENOMEM; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci while (pg - page < len) { 9462306a36Sopenharmony_ci i = 0; 9562306a36Sopenharmony_ci while (i < sizeof(buf) && (pg - page < len) && 9662306a36Sopenharmony_ci *pg != '\0' && *pg != '\n') 9762306a36Sopenharmony_ci buf[i++] = *pg++; 9862306a36Sopenharmony_ci if (i == sizeof(buf)) { 9962306a36Sopenharmony_ci ret = -EINVAL; 10062306a36Sopenharmony_ci goto out_free_buf; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) 10362306a36Sopenharmony_ci ++pg; 10462306a36Sopenharmony_ci buf[i] = '\0'; 10562306a36Sopenharmony_ci ret = fun(buf, priv, size); 10662306a36Sopenharmony_ci if (ret) 10762306a36Sopenharmony_ci goto out_free_buf; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciout_free_buf: 11162306a36Sopenharmony_ci kfree(buf); 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct uvcg_config_group_type { 11662306a36Sopenharmony_ci struct config_item_type type; 11762306a36Sopenharmony_ci const char *name; 11862306a36Sopenharmony_ci const struct uvcg_config_group_type **children; 11962306a36Sopenharmony_ci int (*create_children)(struct config_group *group); 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void uvcg_config_item_release(struct config_item *item) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci kfree(group); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_config_item_ops = { 13062306a36Sopenharmony_ci .release = uvcg_config_item_release, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int uvcg_config_create_group(struct config_group *parent, 13462306a36Sopenharmony_ci const struct uvcg_config_group_type *type); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int uvcg_config_create_children(struct config_group *group, 13762306a36Sopenharmony_ci const struct uvcg_config_group_type *type) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci const struct uvcg_config_group_type **child; 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (type->create_children) 14362306a36Sopenharmony_ci return type->create_children(group); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (child = type->children; child && *child; ++child) { 14662306a36Sopenharmony_ci ret = uvcg_config_create_group(group, *child); 14762306a36Sopenharmony_ci if (ret < 0) 14862306a36Sopenharmony_ci return ret; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int uvcg_config_create_group(struct config_group *parent, 15562306a36Sopenharmony_ci const struct uvcg_config_group_type *type) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct config_group *group; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci group = kzalloc(sizeof(*group), GFP_KERNEL); 16062306a36Sopenharmony_ci if (!group) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci config_group_init_type_name(group, type->name, &type->type); 16462306a36Sopenharmony_ci configfs_add_default_group(group, parent); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return uvcg_config_create_children(group, type); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void uvcg_config_remove_children(struct config_group *group) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct config_group *child, *n; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci list_for_each_entry_safe(child, n, &group->default_groups, group_entry) { 17462306a36Sopenharmony_ci list_del(&child->group_entry); 17562306a36Sopenharmony_ci uvcg_config_remove_children(child); 17662306a36Sopenharmony_ci config_item_put(&child->cg_item); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 18162306a36Sopenharmony_ci * control/header/<NAME> 18262306a36Sopenharmony_ci * control/header 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit) \ 18662306a36Sopenharmony_cistatic ssize_t uvcg_control_header_##cname##_show( \ 18762306a36Sopenharmony_ci struct config_item *item, char *page) \ 18862306a36Sopenharmony_ci{ \ 18962306a36Sopenharmony_ci struct uvcg_control_header *ch = to_uvcg_control_header(item); \ 19062306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 19162306a36Sopenharmony_ci struct config_item *opts_item; \ 19262306a36Sopenharmony_ci struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ 19362306a36Sopenharmony_ci int result; \ 19462306a36Sopenharmony_ci \ 19562306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 19662306a36Sopenharmony_ci \ 19762306a36Sopenharmony_ci opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ 19862306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 19962306a36Sopenharmony_ci \ 20062306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 20162306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(ch->desc.aname));\ 20262306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 20362306a36Sopenharmony_ci \ 20462306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 20562306a36Sopenharmony_ci return result; \ 20662306a36Sopenharmony_ci} \ 20762306a36Sopenharmony_ci \ 20862306a36Sopenharmony_cistatic ssize_t \ 20962306a36Sopenharmony_ciuvcg_control_header_##cname##_store(struct config_item *item, \ 21062306a36Sopenharmony_ci const char *page, size_t len) \ 21162306a36Sopenharmony_ci{ \ 21262306a36Sopenharmony_ci struct uvcg_control_header *ch = to_uvcg_control_header(item); \ 21362306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 21462306a36Sopenharmony_ci struct config_item *opts_item; \ 21562306a36Sopenharmony_ci struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ 21662306a36Sopenharmony_ci int ret; \ 21762306a36Sopenharmony_ci u##bits num; \ 21862306a36Sopenharmony_ci \ 21962306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 22062306a36Sopenharmony_ci \ 22162306a36Sopenharmony_ci opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ 22262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 22362306a36Sopenharmony_ci \ 22462306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 22562306a36Sopenharmony_ci if (ch->linked || opts->refcnt) { \ 22662306a36Sopenharmony_ci ret = -EBUSY; \ 22762306a36Sopenharmony_ci goto end; \ 22862306a36Sopenharmony_ci } \ 22962306a36Sopenharmony_ci \ 23062306a36Sopenharmony_ci ret = kstrtou##bits(page, 0, &num); \ 23162306a36Sopenharmony_ci if (ret) \ 23262306a36Sopenharmony_ci goto end; \ 23362306a36Sopenharmony_ci \ 23462306a36Sopenharmony_ci if (num > limit) { \ 23562306a36Sopenharmony_ci ret = -EINVAL; \ 23662306a36Sopenharmony_ci goto end; \ 23762306a36Sopenharmony_ci } \ 23862306a36Sopenharmony_ci ch->desc.aname = cpu_to_le##bits(num); \ 23962306a36Sopenharmony_ci ret = len; \ 24062306a36Sopenharmony_ciend: \ 24162306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 24262306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 24362306a36Sopenharmony_ci return ret; \ 24462306a36Sopenharmony_ci} \ 24562306a36Sopenharmony_ci \ 24662306a36Sopenharmony_ciUVC_ATTR(uvcg_control_header_, cname, aname) 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciUVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, 16, 0xffff); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciUVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, 32, 0x7fffffff); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#undef UVCG_CTRL_HDR_ATTR 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_control_header_attrs[] = { 25562306a36Sopenharmony_ci &uvcg_control_header_attr_bcd_uvc, 25662306a36Sopenharmony_ci &uvcg_control_header_attr_dw_clock_frequency, 25762306a36Sopenharmony_ci NULL, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const struct config_item_type uvcg_control_header_type = { 26162306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 26262306a36Sopenharmony_ci .ct_attrs = uvcg_control_header_attrs, 26362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic struct config_item *uvcg_control_header_make(struct config_group *group, 26762306a36Sopenharmony_ci const char *name) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct uvcg_control_header *h; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 27262306a36Sopenharmony_ci if (!h) 27362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci h->desc.bLength = UVC_DT_HEADER_SIZE(1); 27662306a36Sopenharmony_ci h->desc.bDescriptorType = USB_DT_CS_INTERFACE; 27762306a36Sopenharmony_ci h->desc.bDescriptorSubType = UVC_VC_HEADER; 27862306a36Sopenharmony_ci h->desc.bcdUVC = cpu_to_le16(0x0110); 27962306a36Sopenharmony_ci h->desc.dwClockFrequency = cpu_to_le32(48000000); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci config_item_init_type_name(&h->item, name, &uvcg_control_header_type); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return &h->item; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_control_header_grp_ops = { 28762306a36Sopenharmony_ci .make_item = uvcg_control_header_make, 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_control_header_grp_type = { 29162306a36Sopenharmony_ci .type = { 29262306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 29362306a36Sopenharmony_ci .ct_group_ops = &uvcg_control_header_grp_ops, 29462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci .name = "header", 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 30062306a36Sopenharmony_ci * control/processing/default 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, bits) \ 30462306a36Sopenharmony_cistatic ssize_t uvcg_default_processing_##cname##_show( \ 30562306a36Sopenharmony_ci struct config_item *item, char *page) \ 30662306a36Sopenharmony_ci{ \ 30762306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 30862306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 30962306a36Sopenharmony_ci struct config_item *opts_item; \ 31062306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 31162306a36Sopenharmony_ci struct uvc_processing_unit_descriptor *pd; \ 31262306a36Sopenharmony_ci int result; \ 31362306a36Sopenharmony_ci \ 31462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 31562306a36Sopenharmony_ci \ 31662306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \ 31762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 31862306a36Sopenharmony_ci pd = &opts->uvc_processing; \ 31962306a36Sopenharmony_ci \ 32062306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 32162306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(pd->aname)); \ 32262306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 32362306a36Sopenharmony_ci \ 32462306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 32562306a36Sopenharmony_ci return result; \ 32662306a36Sopenharmony_ci} \ 32762306a36Sopenharmony_ci \ 32862306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_default_processing_, cname, aname) 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciUVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, 8); 33162306a36Sopenharmony_ciUVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, 8); 33262306a36Sopenharmony_ciUVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, 16); 33362306a36Sopenharmony_ciUVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, 8); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci#undef UVCG_DEFAULT_PROCESSING_ATTR 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic ssize_t uvcg_default_processing_bm_controls_store( 33862306a36Sopenharmony_ci struct config_item *item, const char *page, size_t len) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 34162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 34262306a36Sopenharmony_ci struct uvc_processing_unit_descriptor *pd; 34362306a36Sopenharmony_ci struct config_item *opts_item; 34462306a36Sopenharmony_ci struct f_uvc_opts *opts; 34562306a36Sopenharmony_ci u8 *bm_controls, *tmp; 34662306a36Sopenharmony_ci unsigned int i; 34762306a36Sopenharmony_ci int ret, n = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci mutex_lock(su_mutex); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; 35262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 35362306a36Sopenharmony_ci pd = &opts->uvc_processing; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci mutex_lock(&opts->lock); 35662306a36Sopenharmony_ci if (opts->refcnt) { 35762306a36Sopenharmony_ci ret = -EBUSY; 35862306a36Sopenharmony_ci goto unlock; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n, 36262306a36Sopenharmony_ci sizeof(u8)); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci goto unlock; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (n > pd->bControlSize) { 36762306a36Sopenharmony_ci ret = -EINVAL; 36862306a36Sopenharmony_ci goto unlock; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci tmp = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL); 37262306a36Sopenharmony_ci if (!bm_controls) { 37362306a36Sopenharmony_ci ret = -ENOMEM; 37462306a36Sopenharmony_ci goto unlock; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &tmp, 37862306a36Sopenharmony_ci sizeof(u8)); 37962306a36Sopenharmony_ci if (ret) 38062306a36Sopenharmony_ci goto free_mem; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < n; i++) 38362306a36Sopenharmony_ci pd->bmControls[i] = bm_controls[i]; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ret = len; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cifree_mem: 38862306a36Sopenharmony_ci kfree(bm_controls); 38962306a36Sopenharmony_ciunlock: 39062306a36Sopenharmony_ci mutex_unlock(&opts->lock); 39162306a36Sopenharmony_ci mutex_unlock(su_mutex); 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic ssize_t uvcg_default_processing_bm_controls_show( 39662306a36Sopenharmony_ci struct config_item *item, char *page) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 39962306a36Sopenharmony_ci struct f_uvc_opts *opts; 40062306a36Sopenharmony_ci struct config_item *opts_item; 40162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 40262306a36Sopenharmony_ci struct uvc_processing_unit_descriptor *pd; 40362306a36Sopenharmony_ci int result, i; 40462306a36Sopenharmony_ci char *pg = page; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; 40962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 41062306a36Sopenharmony_ci pd = &opts->uvc_processing; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci mutex_lock(&opts->lock); 41362306a36Sopenharmony_ci for (result = 0, i = 0; i < pd->bControlSize; ++i) { 41462306a36Sopenharmony_ci result += sprintf(pg, "%u\n", pd->bmControls[i]); 41562306a36Sopenharmony_ci pg = page + result; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci mutex_unlock(su_mutex); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return result; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciUVC_ATTR(uvcg_default_processing_, bm_controls, bmControls); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_default_processing_attrs[] = { 42762306a36Sopenharmony_ci &uvcg_default_processing_attr_b_unit_id, 42862306a36Sopenharmony_ci &uvcg_default_processing_attr_b_source_id, 42962306a36Sopenharmony_ci &uvcg_default_processing_attr_w_max_multiplier, 43062306a36Sopenharmony_ci &uvcg_default_processing_attr_bm_controls, 43162306a36Sopenharmony_ci &uvcg_default_processing_attr_i_processing, 43262306a36Sopenharmony_ci NULL, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_default_processing_type = { 43662306a36Sopenharmony_ci .type = { 43762306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 43862306a36Sopenharmony_ci .ct_attrs = uvcg_default_processing_attrs, 43962306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 44062306a36Sopenharmony_ci }, 44162306a36Sopenharmony_ci .name = "default", 44262306a36Sopenharmony_ci}; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 44562306a36Sopenharmony_ci * control/processing 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_processing_grp_type = { 44962306a36Sopenharmony_ci .type = { 45062306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 45162306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 45262306a36Sopenharmony_ci }, 45362306a36Sopenharmony_ci .name = "processing", 45462306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 45562306a36Sopenharmony_ci &uvcg_default_processing_type, 45662306a36Sopenharmony_ci NULL, 45762306a36Sopenharmony_ci }, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 46162306a36Sopenharmony_ci * control/terminal/camera/default 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, bits) \ 46562306a36Sopenharmony_cistatic ssize_t uvcg_default_camera_##cname##_show( \ 46662306a36Sopenharmony_ci struct config_item *item, char *page) \ 46762306a36Sopenharmony_ci{ \ 46862306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 46962306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 47062306a36Sopenharmony_ci struct config_item *opts_item; \ 47162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 47262306a36Sopenharmony_ci struct uvc_camera_terminal_descriptor *cd; \ 47362306a36Sopenharmony_ci int result; \ 47462306a36Sopenharmony_ci \ 47562306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 47662306a36Sopenharmony_ci \ 47762306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> \ 47862306a36Sopenharmony_ci ci_parent; \ 47962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 48062306a36Sopenharmony_ci cd = &opts->uvc_camera_terminal; \ 48162306a36Sopenharmony_ci \ 48262306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 48362306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \ 48462306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 48562306a36Sopenharmony_ci \ 48662306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 48762306a36Sopenharmony_ci \ 48862306a36Sopenharmony_ci return result; \ 48962306a36Sopenharmony_ci} \ 49062306a36Sopenharmony_ci \ 49162306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_default_camera_, cname, aname) 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, 8); 49462306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, 16); 49562306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, 8); 49662306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, 8); 49762306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin, 49862306a36Sopenharmony_ci 16); 49962306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax, 50062306a36Sopenharmony_ci 16); 50162306a36Sopenharmony_ciUVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength, 50262306a36Sopenharmony_ci 16); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci#undef UVCG_DEFAULT_CAMERA_ATTR 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic ssize_t uvcg_default_camera_bm_controls_store( 50762306a36Sopenharmony_ci struct config_item *item, const char *page, size_t len) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 51062306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 51162306a36Sopenharmony_ci struct uvc_camera_terminal_descriptor *cd; 51262306a36Sopenharmony_ci struct config_item *opts_item; 51362306a36Sopenharmony_ci struct f_uvc_opts *opts; 51462306a36Sopenharmony_ci u8 *bm_controls, *tmp; 51562306a36Sopenharmony_ci unsigned int i; 51662306a36Sopenharmony_ci int ret, n = 0; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci mutex_lock(su_mutex); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> 52162306a36Sopenharmony_ci ci_parent; 52262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 52362306a36Sopenharmony_ci cd = &opts->uvc_camera_terminal; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mutex_lock(&opts->lock); 52662306a36Sopenharmony_ci if (opts->refcnt) { 52762306a36Sopenharmony_ci ret = -EBUSY; 52862306a36Sopenharmony_ci goto unlock; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n, 53262306a36Sopenharmony_ci sizeof(u8)); 53362306a36Sopenharmony_ci if (ret) 53462306a36Sopenharmony_ci goto unlock; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (n > cd->bControlSize) { 53762306a36Sopenharmony_ci ret = -EINVAL; 53862306a36Sopenharmony_ci goto unlock; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci tmp = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL); 54262306a36Sopenharmony_ci if (!bm_controls) { 54362306a36Sopenharmony_ci ret = -ENOMEM; 54462306a36Sopenharmony_ci goto unlock; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &tmp, 54862306a36Sopenharmony_ci sizeof(u8)); 54962306a36Sopenharmony_ci if (ret) 55062306a36Sopenharmony_ci goto free_mem; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci for (i = 0; i < n; i++) 55362306a36Sopenharmony_ci cd->bmControls[i] = bm_controls[i]; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ret = len; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cifree_mem: 55862306a36Sopenharmony_ci kfree(bm_controls); 55962306a36Sopenharmony_ciunlock: 56062306a36Sopenharmony_ci mutex_unlock(&opts->lock); 56162306a36Sopenharmony_ci mutex_unlock(su_mutex); 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic ssize_t uvcg_default_camera_bm_controls_show( 56662306a36Sopenharmony_ci struct config_item *item, char *page) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 56962306a36Sopenharmony_ci struct f_uvc_opts *opts; 57062306a36Sopenharmony_ci struct config_item *opts_item; 57162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 57262306a36Sopenharmony_ci struct uvc_camera_terminal_descriptor *cd; 57362306a36Sopenharmony_ci int result, i; 57462306a36Sopenharmony_ci char *pg = page; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> 57962306a36Sopenharmony_ci ci_parent; 58062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 58162306a36Sopenharmony_ci cd = &opts->uvc_camera_terminal; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci mutex_lock(&opts->lock); 58462306a36Sopenharmony_ci for (result = 0, i = 0; i < cd->bControlSize; ++i) { 58562306a36Sopenharmony_ci result += sprintf(pg, "%u\n", cd->bmControls[i]); 58662306a36Sopenharmony_ci pg = page + result; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci mutex_unlock(su_mutex); 59162306a36Sopenharmony_ci return result; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ciUVC_ATTR(uvcg_default_camera_, bm_controls, bmControls); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_default_camera_attrs[] = { 59762306a36Sopenharmony_ci &uvcg_default_camera_attr_b_terminal_id, 59862306a36Sopenharmony_ci &uvcg_default_camera_attr_w_terminal_type, 59962306a36Sopenharmony_ci &uvcg_default_camera_attr_b_assoc_terminal, 60062306a36Sopenharmony_ci &uvcg_default_camera_attr_i_terminal, 60162306a36Sopenharmony_ci &uvcg_default_camera_attr_w_objective_focal_length_min, 60262306a36Sopenharmony_ci &uvcg_default_camera_attr_w_objective_focal_length_max, 60362306a36Sopenharmony_ci &uvcg_default_camera_attr_w_ocular_focal_length, 60462306a36Sopenharmony_ci &uvcg_default_camera_attr_bm_controls, 60562306a36Sopenharmony_ci NULL, 60662306a36Sopenharmony_ci}; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_default_camera_type = { 60962306a36Sopenharmony_ci .type = { 61062306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 61162306a36Sopenharmony_ci .ct_attrs = uvcg_default_camera_attrs, 61262306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 61362306a36Sopenharmony_ci }, 61462306a36Sopenharmony_ci .name = "default", 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 61862306a36Sopenharmony_ci * control/terminal/camera 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_camera_grp_type = { 62262306a36Sopenharmony_ci .type = { 62362306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 62462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 62562306a36Sopenharmony_ci }, 62662306a36Sopenharmony_ci .name = "camera", 62762306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 62862306a36Sopenharmony_ci &uvcg_default_camera_type, 62962306a36Sopenharmony_ci NULL, 63062306a36Sopenharmony_ci }, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 63462306a36Sopenharmony_ci * control/terminal/output/default 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, bits) \ 63862306a36Sopenharmony_cistatic ssize_t uvcg_default_output_##cname##_show( \ 63962306a36Sopenharmony_ci struct config_item *item, char *page) \ 64062306a36Sopenharmony_ci{ \ 64162306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 64262306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 64362306a36Sopenharmony_ci struct config_item *opts_item; \ 64462306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 64562306a36Sopenharmony_ci struct uvc_output_terminal_descriptor *cd; \ 64662306a36Sopenharmony_ci int result; \ 64762306a36Sopenharmony_ci \ 64862306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 64962306a36Sopenharmony_ci \ 65062306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent-> \ 65162306a36Sopenharmony_ci ci_parent->ci_parent; \ 65262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 65362306a36Sopenharmony_ci cd = &opts->uvc_output_terminal; \ 65462306a36Sopenharmony_ci \ 65562306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 65662306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \ 65762306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 65862306a36Sopenharmony_ci \ 65962306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 66062306a36Sopenharmony_ci \ 66162306a36Sopenharmony_ci return result; \ 66262306a36Sopenharmony_ci} \ 66362306a36Sopenharmony_ci \ 66462306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_default_output_, cname, aname) 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ciUVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, 8); 66762306a36Sopenharmony_ciUVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, 16); 66862306a36Sopenharmony_ciUVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, 8); 66962306a36Sopenharmony_ciUVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, 8); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci#undef UVCG_DEFAULT_OUTPUT_ATTR 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic ssize_t uvcg_default_output_b_source_id_show(struct config_item *item, 67462306a36Sopenharmony_ci char *page) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 67762306a36Sopenharmony_ci struct f_uvc_opts *opts; 67862306a36Sopenharmony_ci struct config_item *opts_item; 67962306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 68062306a36Sopenharmony_ci struct uvc_output_terminal_descriptor *cd; 68162306a36Sopenharmony_ci int result; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent-> 68662306a36Sopenharmony_ci ci_parent->ci_parent; 68762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 68862306a36Sopenharmony_ci cd = &opts->uvc_output_terminal; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci mutex_lock(&opts->lock); 69162306a36Sopenharmony_ci result = sprintf(page, "%u\n", le8_to_cpu(cd->bSourceID)); 69262306a36Sopenharmony_ci mutex_unlock(&opts->lock); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci mutex_unlock(su_mutex); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return result; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic ssize_t uvcg_default_output_b_source_id_store(struct config_item *item, 70062306a36Sopenharmony_ci const char *page, size_t len) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 70362306a36Sopenharmony_ci struct f_uvc_opts *opts; 70462306a36Sopenharmony_ci struct config_item *opts_item; 70562306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 70662306a36Sopenharmony_ci struct uvc_output_terminal_descriptor *cd; 70762306a36Sopenharmony_ci int result; 70862306a36Sopenharmony_ci u8 num; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci result = kstrtou8(page, 0, &num); 71162306a36Sopenharmony_ci if (result) 71262306a36Sopenharmony_ci return result; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent-> 71762306a36Sopenharmony_ci ci_parent->ci_parent; 71862306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 71962306a36Sopenharmony_ci cd = &opts->uvc_output_terminal; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci mutex_lock(&opts->lock); 72262306a36Sopenharmony_ci cd->bSourceID = num; 72362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci mutex_unlock(su_mutex); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return len; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ciUVC_ATTR(uvcg_default_output_, b_source_id, bSourceID); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_default_output_attrs[] = { 73262306a36Sopenharmony_ci &uvcg_default_output_attr_b_terminal_id, 73362306a36Sopenharmony_ci &uvcg_default_output_attr_w_terminal_type, 73462306a36Sopenharmony_ci &uvcg_default_output_attr_b_assoc_terminal, 73562306a36Sopenharmony_ci &uvcg_default_output_attr_b_source_id, 73662306a36Sopenharmony_ci &uvcg_default_output_attr_i_terminal, 73762306a36Sopenharmony_ci NULL, 73862306a36Sopenharmony_ci}; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_default_output_type = { 74162306a36Sopenharmony_ci .type = { 74262306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 74362306a36Sopenharmony_ci .ct_attrs = uvcg_default_output_attrs, 74462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 74562306a36Sopenharmony_ci }, 74662306a36Sopenharmony_ci .name = "default", 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 75062306a36Sopenharmony_ci * control/terminal/output 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_output_grp_type = { 75462306a36Sopenharmony_ci .type = { 75562306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 75662306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 75762306a36Sopenharmony_ci }, 75862306a36Sopenharmony_ci .name = "output", 75962306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 76062306a36Sopenharmony_ci &uvcg_default_output_type, 76162306a36Sopenharmony_ci NULL, 76262306a36Sopenharmony_ci }, 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 76662306a36Sopenharmony_ci * control/terminal 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_terminal_grp_type = { 77062306a36Sopenharmony_ci .type = { 77162306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 77262306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 77362306a36Sopenharmony_ci }, 77462306a36Sopenharmony_ci .name = "terminal", 77562306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 77662306a36Sopenharmony_ci &uvcg_camera_grp_type, 77762306a36Sopenharmony_ci &uvcg_output_grp_type, 77862306a36Sopenharmony_ci NULL, 77962306a36Sopenharmony_ci }, 78062306a36Sopenharmony_ci}; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 78362306a36Sopenharmony_ci * control/extensions 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci#define UVCG_EXTENSION_ATTR(cname, aname, ro...) \ 78762306a36Sopenharmony_cistatic ssize_t uvcg_extension_##cname##_show(struct config_item *item, \ 78862306a36Sopenharmony_ci char *page) \ 78962306a36Sopenharmony_ci{ \ 79062306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); \ 79162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 79262306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); \ 79362306a36Sopenharmony_ci struct config_item *opts_item; \ 79462306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 79562306a36Sopenharmony_ci int ret; \ 79662306a36Sopenharmony_ci \ 79762306a36Sopenharmony_ci mutex_lock(su_mutex); \ 79862306a36Sopenharmony_ci \ 79962306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; \ 80062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 80162306a36Sopenharmony_ci \ 80262306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 80362306a36Sopenharmony_ci ret = sprintf(page, "%u\n", xu->desc.aname); \ 80462306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 80562306a36Sopenharmony_ci \ 80662306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 80762306a36Sopenharmony_ci \ 80862306a36Sopenharmony_ci return ret; \ 80962306a36Sopenharmony_ci} \ 81062306a36Sopenharmony_ciUVC_ATTR##ro(uvcg_extension_, cname, aname) 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(b_length, bLength, _RO); 81362306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(b_unit_id, bUnitID, _RO); 81462306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(i_extension, iExtension, _RO); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic ssize_t uvcg_extension_b_num_controls_store(struct config_item *item, 81762306a36Sopenharmony_ci const char *page, size_t len) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 82062306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 82162306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 82262306a36Sopenharmony_ci struct config_item *opts_item; 82362306a36Sopenharmony_ci struct f_uvc_opts *opts; 82462306a36Sopenharmony_ci int ret; 82562306a36Sopenharmony_ci u8 num; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); 82862306a36Sopenharmony_ci if (ret) 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci mutex_lock(su_mutex); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 83462306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci mutex_lock(&opts->lock); 83762306a36Sopenharmony_ci xu->desc.bNumControls = num; 83862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci mutex_unlock(su_mutex); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return len; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(b_num_controls, bNumControls); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci/* 84762306a36Sopenharmony_ci * In addition to storing bNrInPins, this function needs to realloc the 84862306a36Sopenharmony_ci * memory for the baSourceID array and additionally expand bLength. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_cistatic ssize_t uvcg_extension_b_nr_in_pins_store(struct config_item *item, 85162306a36Sopenharmony_ci const char *page, size_t len) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 85462306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 85562306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 85662306a36Sopenharmony_ci struct config_item *opts_item; 85762306a36Sopenharmony_ci struct f_uvc_opts *opts; 85862306a36Sopenharmony_ci void *tmp_buf; 85962306a36Sopenharmony_ci int ret; 86062306a36Sopenharmony_ci u8 num; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); 86362306a36Sopenharmony_ci if (ret) 86462306a36Sopenharmony_ci return ret; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci mutex_lock(su_mutex); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 86962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci mutex_lock(&opts->lock); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (num == xu->desc.bNrInPins) { 87462306a36Sopenharmony_ci ret = len; 87562306a36Sopenharmony_ci goto unlock; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci tmp_buf = krealloc_array(xu->desc.baSourceID, num, sizeof(u8), 87962306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 88062306a36Sopenharmony_ci if (!tmp_buf) { 88162306a36Sopenharmony_ci ret = -ENOMEM; 88262306a36Sopenharmony_ci goto unlock; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci xu->desc.baSourceID = tmp_buf; 88662306a36Sopenharmony_ci xu->desc.bNrInPins = num; 88762306a36Sopenharmony_ci xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins, 88862306a36Sopenharmony_ci xu->desc.bControlSize); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci ret = len; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ciunlock: 89362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 89462306a36Sopenharmony_ci mutex_unlock(su_mutex); 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(b_nr_in_pins, bNrInPins); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* 90062306a36Sopenharmony_ci * In addition to storing bControlSize, this function needs to realloc the 90162306a36Sopenharmony_ci * memory for the bmControls array and additionally expand bLength. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistatic ssize_t uvcg_extension_b_control_size_store(struct config_item *item, 90462306a36Sopenharmony_ci const char *page, size_t len) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 90762306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 90862306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 90962306a36Sopenharmony_ci struct config_item *opts_item; 91062306a36Sopenharmony_ci struct f_uvc_opts *opts; 91162306a36Sopenharmony_ci void *tmp_buf; 91262306a36Sopenharmony_ci int ret; 91362306a36Sopenharmony_ci u8 num; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); 91662306a36Sopenharmony_ci if (ret) 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci mutex_lock(su_mutex); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 92262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci mutex_lock(&opts->lock); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (num == xu->desc.bControlSize) { 92762306a36Sopenharmony_ci ret = len; 92862306a36Sopenharmony_ci goto unlock; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci tmp_buf = krealloc_array(xu->desc.bmControls, num, sizeof(u8), 93262306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 93362306a36Sopenharmony_ci if (!tmp_buf) { 93462306a36Sopenharmony_ci ret = -ENOMEM; 93562306a36Sopenharmony_ci goto unlock; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci xu->desc.bmControls = tmp_buf; 93962306a36Sopenharmony_ci xu->desc.bControlSize = num; 94062306a36Sopenharmony_ci xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins, 94162306a36Sopenharmony_ci xu->desc.bControlSize); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci ret = len; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ciunlock: 94662306a36Sopenharmony_ci mutex_unlock(&opts->lock); 94762306a36Sopenharmony_ci mutex_unlock(su_mutex); 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciUVCG_EXTENSION_ATTR(b_control_size, bControlSize); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic ssize_t uvcg_extension_guid_extension_code_show(struct config_item *item, 95462306a36Sopenharmony_ci char *page) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 95762306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 95862306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 95962306a36Sopenharmony_ci struct config_item *opts_item; 96062306a36Sopenharmony_ci struct f_uvc_opts *opts; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci mutex_lock(su_mutex); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 96562306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci mutex_lock(&opts->lock); 96862306a36Sopenharmony_ci memcpy(page, xu->desc.guidExtensionCode, sizeof(xu->desc.guidExtensionCode)); 96962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci mutex_unlock(su_mutex); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci return sizeof(xu->desc.guidExtensionCode); 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic ssize_t uvcg_extension_guid_extension_code_store(struct config_item *item, 97762306a36Sopenharmony_ci const char *page, size_t len) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 98062306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 98162306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 98262306a36Sopenharmony_ci struct config_item *opts_item; 98362306a36Sopenharmony_ci struct f_uvc_opts *opts; 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci mutex_lock(su_mutex); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 98962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci mutex_lock(&opts->lock); 99262306a36Sopenharmony_ci memcpy(xu->desc.guidExtensionCode, page, 99362306a36Sopenharmony_ci min(sizeof(xu->desc.guidExtensionCode), len)); 99462306a36Sopenharmony_ci mutex_unlock(&opts->lock); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci mutex_unlock(su_mutex); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci ret = sizeof(xu->desc.guidExtensionCode); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return ret; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ciUVC_ATTR(uvcg_extension_, guid_extension_code, guidExtensionCode); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic ssize_t uvcg_extension_ba_source_id_show(struct config_item *item, 100662306a36Sopenharmony_ci char *page) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 100962306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 101062306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 101162306a36Sopenharmony_ci struct config_item *opts_item; 101262306a36Sopenharmony_ci struct f_uvc_opts *opts; 101362306a36Sopenharmony_ci char *pg = page; 101462306a36Sopenharmony_ci int ret, i; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci mutex_lock(su_mutex); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 101962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci mutex_lock(&opts->lock); 102262306a36Sopenharmony_ci for (ret = 0, i = 0; i < xu->desc.bNrInPins; ++i) { 102362306a36Sopenharmony_ci ret += sprintf(pg, "%u\n", xu->desc.baSourceID[i]); 102462306a36Sopenharmony_ci pg = page + ret; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci mutex_unlock(&opts->lock); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mutex_unlock(su_mutex); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return ret; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic ssize_t uvcg_extension_ba_source_id_store(struct config_item *item, 103462306a36Sopenharmony_ci const char *page, size_t len) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 103762306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 103862306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 103962306a36Sopenharmony_ci struct config_item *opts_item; 104062306a36Sopenharmony_ci struct f_uvc_opts *opts; 104162306a36Sopenharmony_ci u8 *source_ids, *iter; 104262306a36Sopenharmony_ci int ret, n = 0; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci mutex_lock(su_mutex); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 104762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci mutex_lock(&opts->lock); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n, 105262306a36Sopenharmony_ci sizeof(u8)); 105362306a36Sopenharmony_ci if (ret) 105462306a36Sopenharmony_ci goto unlock; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci iter = source_ids = kcalloc(n, sizeof(u8), GFP_KERNEL); 105762306a36Sopenharmony_ci if (!source_ids) { 105862306a36Sopenharmony_ci ret = -ENOMEM; 105962306a36Sopenharmony_ci goto unlock; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &iter, 106362306a36Sopenharmony_ci sizeof(u8)); 106462306a36Sopenharmony_ci if (ret) { 106562306a36Sopenharmony_ci kfree(source_ids); 106662306a36Sopenharmony_ci goto unlock; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci kfree(xu->desc.baSourceID); 107062306a36Sopenharmony_ci xu->desc.baSourceID = source_ids; 107162306a36Sopenharmony_ci xu->desc.bNrInPins = n; 107262306a36Sopenharmony_ci xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins, 107362306a36Sopenharmony_ci xu->desc.bControlSize); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci ret = len; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ciunlock: 107862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 107962306a36Sopenharmony_ci mutex_unlock(su_mutex); 108062306a36Sopenharmony_ci return ret; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ciUVC_ATTR(uvcg_extension_, ba_source_id, baSourceID); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic ssize_t uvcg_extension_bm_controls_show(struct config_item *item, 108562306a36Sopenharmony_ci char *page) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 108862306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 108962306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 109062306a36Sopenharmony_ci struct config_item *opts_item; 109162306a36Sopenharmony_ci struct f_uvc_opts *opts; 109262306a36Sopenharmony_ci char *pg = page; 109362306a36Sopenharmony_ci int ret, i; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci mutex_lock(su_mutex); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 109862306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci mutex_lock(&opts->lock); 110162306a36Sopenharmony_ci for (ret = 0, i = 0; i < xu->desc.bControlSize; ++i) { 110262306a36Sopenharmony_ci ret += sprintf(pg, "0x%02x\n", xu->desc.bmControls[i]); 110362306a36Sopenharmony_ci pg = page + ret; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci mutex_unlock(&opts->lock); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci mutex_unlock(su_mutex); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic ssize_t uvcg_extension_bm_controls_store(struct config_item *item, 111362306a36Sopenharmony_ci const char *page, size_t len) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct config_group *group = to_config_group(item->ci_parent); 111662306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 111762306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(item); 111862306a36Sopenharmony_ci struct config_item *opts_item; 111962306a36Sopenharmony_ci struct f_uvc_opts *opts; 112062306a36Sopenharmony_ci u8 *bm_controls, *iter; 112162306a36Sopenharmony_ci int ret, n = 0; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci mutex_lock(su_mutex); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci opts_item = item->ci_parent->ci_parent->ci_parent; 112662306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci mutex_lock(&opts->lock); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n, 113162306a36Sopenharmony_ci sizeof(u8)); 113262306a36Sopenharmony_ci if (ret) 113362306a36Sopenharmony_ci goto unlock; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci iter = bm_controls = kcalloc(n, sizeof(u8), GFP_KERNEL); 113662306a36Sopenharmony_ci if (!bm_controls) { 113762306a36Sopenharmony_ci ret = -ENOMEM; 113862306a36Sopenharmony_ci goto unlock; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &iter, 114262306a36Sopenharmony_ci sizeof(u8)); 114362306a36Sopenharmony_ci if (ret) { 114462306a36Sopenharmony_ci kfree(bm_controls); 114562306a36Sopenharmony_ci goto unlock; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci kfree(xu->desc.bmControls); 114962306a36Sopenharmony_ci xu->desc.bmControls = bm_controls; 115062306a36Sopenharmony_ci xu->desc.bControlSize = n; 115162306a36Sopenharmony_ci xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(xu->desc.bNrInPins, 115262306a36Sopenharmony_ci xu->desc.bControlSize); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ret = len; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ciunlock: 115762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 115862306a36Sopenharmony_ci mutex_unlock(su_mutex); 115962306a36Sopenharmony_ci return ret; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ciUVC_ATTR(uvcg_extension_, bm_controls, bmControls); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_extension_attrs[] = { 116562306a36Sopenharmony_ci &uvcg_extension_attr_b_length, 116662306a36Sopenharmony_ci &uvcg_extension_attr_b_unit_id, 116762306a36Sopenharmony_ci &uvcg_extension_attr_b_num_controls, 116862306a36Sopenharmony_ci &uvcg_extension_attr_b_nr_in_pins, 116962306a36Sopenharmony_ci &uvcg_extension_attr_b_control_size, 117062306a36Sopenharmony_ci &uvcg_extension_attr_guid_extension_code, 117162306a36Sopenharmony_ci &uvcg_extension_attr_ba_source_id, 117262306a36Sopenharmony_ci &uvcg_extension_attr_bm_controls, 117362306a36Sopenharmony_ci &uvcg_extension_attr_i_extension, 117462306a36Sopenharmony_ci NULL, 117562306a36Sopenharmony_ci}; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic void uvcg_extension_release(struct config_item *item) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci kfree(xu); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int uvcg_extension_allow_link(struct config_item *src, struct config_item *tgt) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 118762306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(src); 118862306a36Sopenharmony_ci struct config_item *gadget_item; 118962306a36Sopenharmony_ci struct gadget_string *string; 119062306a36Sopenharmony_ci struct config_item *strings; 119162306a36Sopenharmony_ci int ret = 0; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Validate that the target of the link is an entry in strings/<langid> */ 119662306a36Sopenharmony_ci gadget_item = src->ci_parent->ci_parent->ci_parent->ci_parent->ci_parent; 119762306a36Sopenharmony_ci strings = config_group_find_item(to_config_group(gadget_item), "strings"); 119862306a36Sopenharmony_ci if (!strings || tgt->ci_parent->ci_parent != strings) { 119962306a36Sopenharmony_ci ret = -EINVAL; 120062306a36Sopenharmony_ci goto put_strings; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci string = to_gadget_string(tgt); 120462306a36Sopenharmony_ci xu->string_descriptor_index = string->usb_string.id; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ciput_strings: 120762306a36Sopenharmony_ci config_item_put(strings); 120862306a36Sopenharmony_ci mutex_unlock(su_mutex); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return ret; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic void uvcg_extension_drop_link(struct config_item *src, struct config_item *tgt) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 121662306a36Sopenharmony_ci struct uvcg_extension *xu = to_uvcg_extension(src); 121762306a36Sopenharmony_ci struct config_item *opts_item; 121862306a36Sopenharmony_ci struct f_uvc_opts *opts; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci opts_item = src->ci_parent->ci_parent->ci_parent; 122362306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci mutex_lock(&opts->lock); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci xu->string_descriptor_index = 0; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci mutex_unlock(su_mutex); 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_extension_item_ops = { 123562306a36Sopenharmony_ci .release = uvcg_extension_release, 123662306a36Sopenharmony_ci .allow_link = uvcg_extension_allow_link, 123762306a36Sopenharmony_ci .drop_link = uvcg_extension_drop_link, 123862306a36Sopenharmony_ci}; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic const struct config_item_type uvcg_extension_type = { 124162306a36Sopenharmony_ci .ct_item_ops = &uvcg_extension_item_ops, 124262306a36Sopenharmony_ci .ct_attrs = uvcg_extension_attrs, 124362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 124462306a36Sopenharmony_ci}; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic void uvcg_extension_drop(struct config_group *group, struct config_item *item) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci struct uvcg_extension *xu = container_of(item, struct uvcg_extension, item); 124962306a36Sopenharmony_ci struct config_item *opts_item; 125062306a36Sopenharmony_ci struct f_uvc_opts *opts; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent; 125362306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci mutex_lock(&opts->lock); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci config_item_put(item); 125862306a36Sopenharmony_ci list_del(&xu->list); 125962306a36Sopenharmony_ci kfree(xu->desc.baSourceID); 126062306a36Sopenharmony_ci kfree(xu->desc.bmControls); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci mutex_unlock(&opts->lock); 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic struct config_item *uvcg_extension_make(struct config_group *group, const char *name) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct config_item *opts_item; 126862306a36Sopenharmony_ci struct uvcg_extension *xu; 126962306a36Sopenharmony_ci struct f_uvc_opts *opts; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent; 127262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci xu = kzalloc(sizeof(*xu), GFP_KERNEL); 127562306a36Sopenharmony_ci if (!xu) 127662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci xu->desc.bLength = UVC_DT_EXTENSION_UNIT_SIZE(0, 0); 127962306a36Sopenharmony_ci xu->desc.bDescriptorType = USB_DT_CS_INTERFACE; 128062306a36Sopenharmony_ci xu->desc.bDescriptorSubType = UVC_VC_EXTENSION_UNIT; 128162306a36Sopenharmony_ci xu->desc.bNumControls = 0; 128262306a36Sopenharmony_ci xu->desc.bNrInPins = 0; 128362306a36Sopenharmony_ci xu->desc.baSourceID = NULL; 128462306a36Sopenharmony_ci xu->desc.bControlSize = 0; 128562306a36Sopenharmony_ci xu->desc.bmControls = NULL; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci mutex_lock(&opts->lock); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci xu->desc.bUnitID = ++opts->last_unit_id; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci config_item_init_type_name(&xu->item, name, &uvcg_extension_type); 129262306a36Sopenharmony_ci list_add_tail(&xu->list, &opts->extension_units); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci mutex_unlock(&opts->lock); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return &xu->item; 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_extensions_grp_ops = { 130062306a36Sopenharmony_ci .make_item = uvcg_extension_make, 130162306a36Sopenharmony_ci .drop_item = uvcg_extension_drop, 130262306a36Sopenharmony_ci}; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_extensions_grp_type = { 130562306a36Sopenharmony_ci .type = { 130662306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 130762306a36Sopenharmony_ci .ct_group_ops = &uvcg_extensions_grp_ops, 130862306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 130962306a36Sopenharmony_ci }, 131062306a36Sopenharmony_ci .name = "extensions", 131162306a36Sopenharmony_ci}; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 131462306a36Sopenharmony_ci * control/class/{fs|ss} 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistruct uvcg_control_class_group { 131862306a36Sopenharmony_ci struct config_group group; 131962306a36Sopenharmony_ci const char *name; 132062306a36Sopenharmony_ci}; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic inline struct uvc_descriptor_header 132362306a36Sopenharmony_ci**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci struct uvcg_control_class_group *group = 132662306a36Sopenharmony_ci container_of(i, struct uvcg_control_class_group, 132762306a36Sopenharmony_ci group.cg_item); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (!strcmp(group->name, "fs")) 133062306a36Sopenharmony_ci return o->uvc_fs_control_cls; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (!strcmp(group->name, "ss")) 133362306a36Sopenharmony_ci return o->uvc_ss_control_cls; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci return NULL; 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic int uvcg_control_class_allow_link(struct config_item *src, 133962306a36Sopenharmony_ci struct config_item *target) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct config_item *control, *header; 134262306a36Sopenharmony_ci struct f_uvc_opts *opts; 134362306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 134462306a36Sopenharmony_ci struct uvc_descriptor_header **class_array; 134562306a36Sopenharmony_ci struct uvcg_control_header *target_hdr; 134662306a36Sopenharmony_ci int ret = -EINVAL; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci control = src->ci_parent->ci_parent; 135162306a36Sopenharmony_ci header = config_group_find_item(to_config_group(control), "header"); 135262306a36Sopenharmony_ci if (!header || target->ci_parent != header) 135362306a36Sopenharmony_ci goto out; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci opts = to_f_uvc_opts(control->ci_parent); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci mutex_lock(&opts->lock); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci class_array = uvcg_get_ctl_class_arr(src, opts); 136062306a36Sopenharmony_ci if (!class_array) 136162306a36Sopenharmony_ci goto unlock; 136262306a36Sopenharmony_ci if (opts->refcnt || class_array[0]) { 136362306a36Sopenharmony_ci ret = -EBUSY; 136462306a36Sopenharmony_ci goto unlock; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci target_hdr = to_uvcg_control_header(target); 136862306a36Sopenharmony_ci ++target_hdr->linked; 136962306a36Sopenharmony_ci class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc; 137062306a36Sopenharmony_ci ret = 0; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ciunlock: 137362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 137462306a36Sopenharmony_ciout: 137562306a36Sopenharmony_ci config_item_put(header); 137662306a36Sopenharmony_ci mutex_unlock(su_mutex); 137762306a36Sopenharmony_ci return ret; 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic void uvcg_control_class_drop_link(struct config_item *src, 138162306a36Sopenharmony_ci struct config_item *target) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct config_item *control, *header; 138462306a36Sopenharmony_ci struct f_uvc_opts *opts; 138562306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 138662306a36Sopenharmony_ci struct uvc_descriptor_header **class_array; 138762306a36Sopenharmony_ci struct uvcg_control_header *target_hdr; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci control = src->ci_parent->ci_parent; 139262306a36Sopenharmony_ci header = config_group_find_item(to_config_group(control), "header"); 139362306a36Sopenharmony_ci if (!header || target->ci_parent != header) 139462306a36Sopenharmony_ci goto out; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci opts = to_f_uvc_opts(control->ci_parent); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci mutex_lock(&opts->lock); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci class_array = uvcg_get_ctl_class_arr(src, opts); 140162306a36Sopenharmony_ci if (!class_array || opts->refcnt) 140262306a36Sopenharmony_ci goto unlock; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci target_hdr = to_uvcg_control_header(target); 140562306a36Sopenharmony_ci --target_hdr->linked; 140662306a36Sopenharmony_ci class_array[0] = NULL; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ciunlock: 140962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 141062306a36Sopenharmony_ciout: 141162306a36Sopenharmony_ci config_item_put(header); 141262306a36Sopenharmony_ci mutex_unlock(su_mutex); 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_control_class_item_ops = { 141662306a36Sopenharmony_ci .release = uvcg_config_item_release, 141762306a36Sopenharmony_ci .allow_link = uvcg_control_class_allow_link, 141862306a36Sopenharmony_ci .drop_link = uvcg_control_class_drop_link, 141962306a36Sopenharmony_ci}; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic const struct config_item_type uvcg_control_class_type = { 142262306a36Sopenharmony_ci .ct_item_ops = &uvcg_control_class_item_ops, 142362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 142462306a36Sopenharmony_ci}; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 142762306a36Sopenharmony_ci * control/class 142862306a36Sopenharmony_ci */ 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic int uvcg_control_class_create_children(struct config_group *parent) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci static const char * const names[] = { "fs", "ss" }; 143362306a36Sopenharmony_ci unsigned int i; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(names); ++i) { 143662306a36Sopenharmony_ci struct uvcg_control_class_group *group; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci group = kzalloc(sizeof(*group), GFP_KERNEL); 143962306a36Sopenharmony_ci if (!group) 144062306a36Sopenharmony_ci return -ENOMEM; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci group->name = names[i]; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci config_group_init_type_name(&group->group, group->name, 144562306a36Sopenharmony_ci &uvcg_control_class_type); 144662306a36Sopenharmony_ci configfs_add_default_group(&group->group, parent); 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci return 0; 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_control_class_grp_type = { 145362306a36Sopenharmony_ci .type = { 145462306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 145562306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 145662306a36Sopenharmony_ci }, 145762306a36Sopenharmony_ci .name = "class", 145862306a36Sopenharmony_ci .create_children = uvcg_control_class_create_children, 145962306a36Sopenharmony_ci}; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 146262306a36Sopenharmony_ci * control 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic ssize_t uvcg_default_control_b_interface_number_show( 146662306a36Sopenharmony_ci struct config_item *item, char *page) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 146962306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 147062306a36Sopenharmony_ci struct config_item *opts_item; 147162306a36Sopenharmony_ci struct f_uvc_opts *opts; 147262306a36Sopenharmony_ci int result = 0; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci opts_item = item->ci_parent; 147762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci mutex_lock(&opts->lock); 148062306a36Sopenharmony_ci result += sprintf(page, "%u\n", opts->control_interface); 148162306a36Sopenharmony_ci mutex_unlock(&opts->lock); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci mutex_unlock(su_mutex); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci return result; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_default_control_, b_interface_number, bInterfaceNumber); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic ssize_t uvcg_default_control_enable_interrupt_ep_show( 149162306a36Sopenharmony_ci struct config_item *item, char *page) 149262306a36Sopenharmony_ci{ 149362306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 149462306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 149562306a36Sopenharmony_ci struct config_item *opts_item; 149662306a36Sopenharmony_ci struct f_uvc_opts *opts; 149762306a36Sopenharmony_ci int result = 0; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci opts_item = item->ci_parent; 150262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci mutex_lock(&opts->lock); 150562306a36Sopenharmony_ci result += sprintf(page, "%u\n", opts->enable_interrupt_ep); 150662306a36Sopenharmony_ci mutex_unlock(&opts->lock); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci mutex_unlock(su_mutex); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci return result; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic ssize_t uvcg_default_control_enable_interrupt_ep_store( 151462306a36Sopenharmony_ci struct config_item *item, const char *page, size_t len) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 151762306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 151862306a36Sopenharmony_ci struct config_item *opts_item; 151962306a36Sopenharmony_ci struct f_uvc_opts *opts; 152062306a36Sopenharmony_ci ssize_t ret; 152162306a36Sopenharmony_ci u8 num; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); 152462306a36Sopenharmony_ci if (ret) 152562306a36Sopenharmony_ci return ret; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci opts_item = item->ci_parent; 153062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci mutex_lock(&opts->lock); 153362306a36Sopenharmony_ci opts->enable_interrupt_ep = num; 153462306a36Sopenharmony_ci mutex_unlock(&opts->lock); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci mutex_unlock(su_mutex); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci return len; 153962306a36Sopenharmony_ci} 154062306a36Sopenharmony_ciUVC_ATTR(uvcg_default_control_, enable_interrupt_ep, enable_interrupt_ep); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_default_control_attrs[] = { 154362306a36Sopenharmony_ci &uvcg_default_control_attr_b_interface_number, 154462306a36Sopenharmony_ci &uvcg_default_control_attr_enable_interrupt_ep, 154562306a36Sopenharmony_ci NULL, 154662306a36Sopenharmony_ci}; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_control_grp_type = { 154962306a36Sopenharmony_ci .type = { 155062306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 155162306a36Sopenharmony_ci .ct_attrs = uvcg_default_control_attrs, 155262306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 155362306a36Sopenharmony_ci }, 155462306a36Sopenharmony_ci .name = "control", 155562306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 155662306a36Sopenharmony_ci &uvcg_control_header_grp_type, 155762306a36Sopenharmony_ci &uvcg_processing_grp_type, 155862306a36Sopenharmony_ci &uvcg_terminal_grp_type, 155962306a36Sopenharmony_ci &uvcg_control_class_grp_type, 156062306a36Sopenharmony_ci &uvcg_extensions_grp_type, 156162306a36Sopenharmony_ci NULL, 156262306a36Sopenharmony_ci }, 156362306a36Sopenharmony_ci}; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 156662306a36Sopenharmony_ci * streaming/uncompressed 156762306a36Sopenharmony_ci * streaming/mjpeg 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic const char * const uvcg_format_names[] = { 157162306a36Sopenharmony_ci "uncompressed", 157262306a36Sopenharmony_ci "mjpeg", 157362306a36Sopenharmony_ci}; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic struct uvcg_color_matching * 157662306a36Sopenharmony_ciuvcg_format_get_default_color_match(struct config_item *streaming) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci struct config_item *color_matching_item, *cm_default; 157962306a36Sopenharmony_ci struct uvcg_color_matching *color_match; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci color_matching_item = config_group_find_item(to_config_group(streaming), 158262306a36Sopenharmony_ci "color_matching"); 158362306a36Sopenharmony_ci if (!color_matching_item) 158462306a36Sopenharmony_ci return NULL; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci cm_default = config_group_find_item(to_config_group(color_matching_item), 158762306a36Sopenharmony_ci "default"); 158862306a36Sopenharmony_ci config_item_put(color_matching_item); 158962306a36Sopenharmony_ci if (!cm_default) 159062306a36Sopenharmony_ci return NULL; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci color_match = to_uvcg_color_matching(to_config_group(cm_default)); 159362306a36Sopenharmony_ci config_item_put(cm_default); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return color_match; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic int uvcg_format_allow_link(struct config_item *src, struct config_item *tgt) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 160162306a36Sopenharmony_ci struct uvcg_color_matching *color_matching_desc; 160262306a36Sopenharmony_ci struct config_item *streaming, *color_matching; 160362306a36Sopenharmony_ci struct uvcg_format *fmt; 160462306a36Sopenharmony_ci int ret = 0; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci mutex_lock(su_mutex); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci streaming = src->ci_parent->ci_parent; 160962306a36Sopenharmony_ci color_matching = config_group_find_item(to_config_group(streaming), "color_matching"); 161062306a36Sopenharmony_ci if (!color_matching || color_matching != tgt->ci_parent) { 161162306a36Sopenharmony_ci ret = -EINVAL; 161262306a36Sopenharmony_ci goto out_put_cm; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci fmt = to_uvcg_format(src); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci /* 161862306a36Sopenharmony_ci * There's always a color matching descriptor associated with the format 161962306a36Sopenharmony_ci * but without a symlink it should only ever be the default one. If it's 162062306a36Sopenharmony_ci * not the default, there's already a symlink and we should bail out. 162162306a36Sopenharmony_ci */ 162262306a36Sopenharmony_ci color_matching_desc = uvcg_format_get_default_color_match(streaming); 162362306a36Sopenharmony_ci if (fmt->color_matching != color_matching_desc) { 162462306a36Sopenharmony_ci ret = -EBUSY; 162562306a36Sopenharmony_ci goto out_put_cm; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci color_matching_desc->refcnt--; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci color_matching_desc = to_uvcg_color_matching(to_config_group(tgt)); 163162306a36Sopenharmony_ci fmt->color_matching = color_matching_desc; 163262306a36Sopenharmony_ci color_matching_desc->refcnt++; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ciout_put_cm: 163562306a36Sopenharmony_ci config_item_put(color_matching); 163662306a36Sopenharmony_ci mutex_unlock(su_mutex); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci return ret; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic void uvcg_format_drop_link(struct config_item *src, struct config_item *tgt) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 164462306a36Sopenharmony_ci struct uvcg_color_matching *color_matching_desc; 164562306a36Sopenharmony_ci struct config_item *streaming; 164662306a36Sopenharmony_ci struct uvcg_format *fmt; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci mutex_lock(su_mutex); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci color_matching_desc = to_uvcg_color_matching(to_config_group(tgt)); 165162306a36Sopenharmony_ci color_matching_desc->refcnt--; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci streaming = src->ci_parent->ci_parent; 165462306a36Sopenharmony_ci color_matching_desc = uvcg_format_get_default_color_match(streaming); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci fmt = to_uvcg_format(src); 165762306a36Sopenharmony_ci fmt->color_matching = color_matching_desc; 165862306a36Sopenharmony_ci color_matching_desc->refcnt++; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci mutex_unlock(su_mutex); 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_format_item_operations = { 166462306a36Sopenharmony_ci .release = uvcg_config_item_release, 166562306a36Sopenharmony_ci .allow_link = uvcg_format_allow_link, 166662306a36Sopenharmony_ci .drop_link = uvcg_format_drop_link, 166762306a36Sopenharmony_ci}; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct f_uvc_opts *opts; 167262306a36Sopenharmony_ci struct config_item *opts_item; 167362306a36Sopenharmony_ci struct mutex *su_mutex = &f->group.cg_subsys->su_mutex; 167462306a36Sopenharmony_ci int result, i; 167562306a36Sopenharmony_ci char *pg = page; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent; 168062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci mutex_lock(&opts->lock); 168362306a36Sopenharmony_ci result = sprintf(pg, "0x"); 168462306a36Sopenharmony_ci pg += result; 168562306a36Sopenharmony_ci for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) { 168662306a36Sopenharmony_ci result += sprintf(pg, "%x\n", f->bmaControls[i]); 168762306a36Sopenharmony_ci pg = page + result; 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci mutex_unlock(su_mutex); 169262306a36Sopenharmony_ci return result; 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch, 169662306a36Sopenharmony_ci const char *page, size_t len) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci struct f_uvc_opts *opts; 169962306a36Sopenharmony_ci struct config_item *opts_item; 170062306a36Sopenharmony_ci struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex; 170162306a36Sopenharmony_ci int ret = -EINVAL; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent; 170662306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci mutex_lock(&opts->lock); 170962306a36Sopenharmony_ci if (ch->linked || opts->refcnt) { 171062306a36Sopenharmony_ci ret = -EBUSY; 171162306a36Sopenharmony_ci goto end; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (len < 4 || *page != '0' || 171562306a36Sopenharmony_ci (*(page + 1) != 'x' && *(page + 1) != 'X')) 171662306a36Sopenharmony_ci goto end; 171762306a36Sopenharmony_ci ret = hex2bin(ch->bmaControls, page + 2, 1); 171862306a36Sopenharmony_ci if (ret < 0) 171962306a36Sopenharmony_ci goto end; 172062306a36Sopenharmony_ci ret = len; 172162306a36Sopenharmony_ciend: 172262306a36Sopenharmony_ci mutex_unlock(&opts->lock); 172362306a36Sopenharmony_ci mutex_unlock(su_mutex); 172462306a36Sopenharmony_ci return ret; 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 172862306a36Sopenharmony_ci * streaming/header/<NAME> 172962306a36Sopenharmony_ci * streaming/header 173062306a36Sopenharmony_ci */ 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void uvcg_format_set_indices(struct config_group *fmt); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_cistatic int uvcg_streaming_header_allow_link(struct config_item *src, 173562306a36Sopenharmony_ci struct config_item *target) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 173862306a36Sopenharmony_ci struct config_item *opts_item; 173962306a36Sopenharmony_ci struct f_uvc_opts *opts; 174062306a36Sopenharmony_ci struct uvcg_streaming_header *src_hdr; 174162306a36Sopenharmony_ci struct uvcg_format *target_fmt = NULL; 174262306a36Sopenharmony_ci struct uvcg_format_ptr *format_ptr; 174362306a36Sopenharmony_ci int i, ret = -EINVAL; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci src_hdr = to_uvcg_streaming_header(src); 174662306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci opts_item = src->ci_parent->ci_parent->ci_parent; 174962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci mutex_lock(&opts->lock); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (src_hdr->linked) { 175462306a36Sopenharmony_ci ret = -EBUSY; 175562306a36Sopenharmony_ci goto out; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci /* 175962306a36Sopenharmony_ci * Linking is only allowed to direct children of the format nodes 176062306a36Sopenharmony_ci * (streaming/uncompressed or streaming/mjpeg nodes). First check that 176162306a36Sopenharmony_ci * the grand-parent of the target matches the grand-parent of the source 176262306a36Sopenharmony_ci * (the streaming node), and then verify that the target parent is a 176362306a36Sopenharmony_ci * format node. 176462306a36Sopenharmony_ci */ 176562306a36Sopenharmony_ci if (src->ci_parent->ci_parent != target->ci_parent->ci_parent) 176662306a36Sopenharmony_ci goto out; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(uvcg_format_names); ++i) { 176962306a36Sopenharmony_ci if (!strcmp(target->ci_parent->ci_name, uvcg_format_names[i])) 177062306a36Sopenharmony_ci break; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (i == ARRAY_SIZE(uvcg_format_names)) 177462306a36Sopenharmony_ci goto out; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci target_fmt = container_of(to_config_group(target), struct uvcg_format, 177762306a36Sopenharmony_ci group); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci uvcg_format_set_indices(to_config_group(target)); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); 178262306a36Sopenharmony_ci if (!format_ptr) { 178362306a36Sopenharmony_ci ret = -ENOMEM; 178462306a36Sopenharmony_ci goto out; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci ret = 0; 178762306a36Sopenharmony_ci format_ptr->fmt = target_fmt; 178862306a36Sopenharmony_ci list_add_tail(&format_ptr->entry, &src_hdr->formats); 178962306a36Sopenharmony_ci ++src_hdr->num_fmt; 179062306a36Sopenharmony_ci ++target_fmt->linked; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ciout: 179362306a36Sopenharmony_ci mutex_unlock(&opts->lock); 179462306a36Sopenharmony_ci mutex_unlock(su_mutex); 179562306a36Sopenharmony_ci return ret; 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic void uvcg_streaming_header_drop_link(struct config_item *src, 179962306a36Sopenharmony_ci struct config_item *target) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 180262306a36Sopenharmony_ci struct config_item *opts_item; 180362306a36Sopenharmony_ci struct f_uvc_opts *opts; 180462306a36Sopenharmony_ci struct uvcg_streaming_header *src_hdr; 180562306a36Sopenharmony_ci struct uvcg_format *target_fmt = NULL; 180662306a36Sopenharmony_ci struct uvcg_format_ptr *format_ptr, *tmp; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci src_hdr = to_uvcg_streaming_header(src); 180962306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci opts_item = src->ci_parent->ci_parent->ci_parent; 181262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci mutex_lock(&opts->lock); 181562306a36Sopenharmony_ci target_fmt = container_of(to_config_group(target), struct uvcg_format, 181662306a36Sopenharmony_ci group); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) 181962306a36Sopenharmony_ci if (format_ptr->fmt == target_fmt) { 182062306a36Sopenharmony_ci list_del(&format_ptr->entry); 182162306a36Sopenharmony_ci kfree(format_ptr); 182262306a36Sopenharmony_ci --src_hdr->num_fmt; 182362306a36Sopenharmony_ci break; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci --target_fmt->linked; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 182962306a36Sopenharmony_ci mutex_unlock(su_mutex); 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_streaming_header_item_ops = { 183362306a36Sopenharmony_ci .release = uvcg_config_item_release, 183462306a36Sopenharmony_ci .allow_link = uvcg_streaming_header_allow_link, 183562306a36Sopenharmony_ci .drop_link = uvcg_streaming_header_drop_link, 183662306a36Sopenharmony_ci}; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci#define UVCG_STREAMING_HEADER_ATTR(cname, aname, bits) \ 183962306a36Sopenharmony_cistatic ssize_t uvcg_streaming_header_##cname##_show( \ 184062306a36Sopenharmony_ci struct config_item *item, char *page) \ 184162306a36Sopenharmony_ci{ \ 184262306a36Sopenharmony_ci struct uvcg_streaming_header *sh = to_uvcg_streaming_header(item); \ 184362306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 184462306a36Sopenharmony_ci struct config_item *opts_item; \ 184562306a36Sopenharmony_ci struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\ 184662306a36Sopenharmony_ci int result; \ 184762306a36Sopenharmony_ci \ 184862306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 184962306a36Sopenharmony_ci \ 185062306a36Sopenharmony_ci opts_item = sh->item.ci_parent->ci_parent->ci_parent; \ 185162306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 185262306a36Sopenharmony_ci \ 185362306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 185462306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(sh->desc.aname));\ 185562306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 185662306a36Sopenharmony_ci \ 185762306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 185862306a36Sopenharmony_ci return result; \ 185962306a36Sopenharmony_ci} \ 186062306a36Sopenharmony_ci \ 186162306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_streaming_header_, cname, aname) 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ciUVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, 8); 186462306a36Sopenharmony_ciUVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, 8); 186562306a36Sopenharmony_ciUVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, 8); 186662306a36Sopenharmony_ciUVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, 8); 186762306a36Sopenharmony_ciUVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, 8); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci#undef UVCG_STREAMING_HEADER_ATTR 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_streaming_header_attrs[] = { 187262306a36Sopenharmony_ci &uvcg_streaming_header_attr_bm_info, 187362306a36Sopenharmony_ci &uvcg_streaming_header_attr_b_terminal_link, 187462306a36Sopenharmony_ci &uvcg_streaming_header_attr_b_still_capture_method, 187562306a36Sopenharmony_ci &uvcg_streaming_header_attr_b_trigger_support, 187662306a36Sopenharmony_ci &uvcg_streaming_header_attr_b_trigger_usage, 187762306a36Sopenharmony_ci NULL, 187862306a36Sopenharmony_ci}; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_cistatic const struct config_item_type uvcg_streaming_header_type = { 188162306a36Sopenharmony_ci .ct_item_ops = &uvcg_streaming_header_item_ops, 188262306a36Sopenharmony_ci .ct_attrs = uvcg_streaming_header_attrs, 188362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 188462306a36Sopenharmony_ci}; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_cistatic struct config_item 188762306a36Sopenharmony_ci*uvcg_streaming_header_make(struct config_group *group, const char *name) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci struct uvcg_streaming_header *h; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 189262306a36Sopenharmony_ci if (!h) 189362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci INIT_LIST_HEAD(&h->formats); 189662306a36Sopenharmony_ci h->desc.bDescriptorType = USB_DT_CS_INTERFACE; 189762306a36Sopenharmony_ci h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER; 189862306a36Sopenharmony_ci h->desc.bTerminalLink = 3; 189962306a36Sopenharmony_ci h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return &h->item; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_streaming_header_grp_ops = { 190762306a36Sopenharmony_ci .make_item = uvcg_streaming_header_make, 190862306a36Sopenharmony_ci}; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_streaming_header_grp_type = { 191162306a36Sopenharmony_ci .type = { 191262306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 191362306a36Sopenharmony_ci .ct_group_ops = &uvcg_streaming_header_grp_ops, 191462306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 191562306a36Sopenharmony_ci }, 191662306a36Sopenharmony_ci .name = "header", 191762306a36Sopenharmony_ci}; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 192062306a36Sopenharmony_ci * streaming/<mode>/<format>/<NAME> 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci#define UVCG_FRAME_ATTR(cname, aname, bits) \ 192462306a36Sopenharmony_cistatic ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ 192562306a36Sopenharmony_ci{ \ 192662306a36Sopenharmony_ci struct uvcg_frame *f = to_uvcg_frame(item); \ 192762306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 192862306a36Sopenharmony_ci struct config_item *opts_item; \ 192962306a36Sopenharmony_ci struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ 193062306a36Sopenharmony_ci int result; \ 193162306a36Sopenharmony_ci \ 193262306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 193362306a36Sopenharmony_ci \ 193462306a36Sopenharmony_ci opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ 193562306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 193662306a36Sopenharmony_ci \ 193762306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 193862306a36Sopenharmony_ci result = sprintf(page, "%u\n", f->frame.cname); \ 193962306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 194062306a36Sopenharmony_ci \ 194162306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 194262306a36Sopenharmony_ci return result; \ 194362306a36Sopenharmony_ci} \ 194462306a36Sopenharmony_ci \ 194562306a36Sopenharmony_cistatic ssize_t uvcg_frame_##cname##_store(struct config_item *item, \ 194662306a36Sopenharmony_ci const char *page, size_t len)\ 194762306a36Sopenharmony_ci{ \ 194862306a36Sopenharmony_ci struct uvcg_frame *f = to_uvcg_frame(item); \ 194962306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 195062306a36Sopenharmony_ci struct config_item *opts_item; \ 195162306a36Sopenharmony_ci struct uvcg_format *fmt; \ 195262306a36Sopenharmony_ci struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ 195362306a36Sopenharmony_ci typeof(f->frame.cname) num; \ 195462306a36Sopenharmony_ci int ret; \ 195562306a36Sopenharmony_ci \ 195662306a36Sopenharmony_ci ret = kstrtou##bits(page, 0, &num); \ 195762306a36Sopenharmony_ci if (ret) \ 195862306a36Sopenharmony_ci return ret; \ 195962306a36Sopenharmony_ci \ 196062306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 196162306a36Sopenharmony_ci \ 196262306a36Sopenharmony_ci opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ 196362306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 196462306a36Sopenharmony_ci fmt = to_uvcg_format(f->item.ci_parent); \ 196562306a36Sopenharmony_ci \ 196662306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 196762306a36Sopenharmony_ci if (fmt->linked || opts->refcnt) { \ 196862306a36Sopenharmony_ci ret = -EBUSY; \ 196962306a36Sopenharmony_ci goto end; \ 197062306a36Sopenharmony_ci } \ 197162306a36Sopenharmony_ci \ 197262306a36Sopenharmony_ci f->frame.cname = num; \ 197362306a36Sopenharmony_ci ret = len; \ 197462306a36Sopenharmony_ciend: \ 197562306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 197662306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 197762306a36Sopenharmony_ci return ret; \ 197862306a36Sopenharmony_ci} \ 197962306a36Sopenharmony_ci \ 198062306a36Sopenharmony_ciUVC_ATTR(uvcg_frame_, cname, aname); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic ssize_t uvcg_frame_b_frame_index_show(struct config_item *item, 198362306a36Sopenharmony_ci char *page) 198462306a36Sopenharmony_ci{ 198562306a36Sopenharmony_ci struct uvcg_frame *f = to_uvcg_frame(item); 198662306a36Sopenharmony_ci struct uvcg_format *fmt; 198762306a36Sopenharmony_ci struct f_uvc_opts *opts; 198862306a36Sopenharmony_ci struct config_item *opts_item; 198962306a36Sopenharmony_ci struct config_item *fmt_item; 199062306a36Sopenharmony_ci struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex; 199162306a36Sopenharmony_ci int result; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci fmt_item = f->item.ci_parent; 199662306a36Sopenharmony_ci fmt = to_uvcg_format(fmt_item); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (!fmt->linked) { 199962306a36Sopenharmony_ci result = -EBUSY; 200062306a36Sopenharmony_ci goto out; 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci opts_item = fmt_item->ci_parent->ci_parent->ci_parent; 200462306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci mutex_lock(&opts->lock); 200762306a36Sopenharmony_ci result = sprintf(page, "%u\n", f->frame.b_frame_index); 200862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ciout: 201162306a36Sopenharmony_ci mutex_unlock(su_mutex); 201262306a36Sopenharmony_ci return result; 201362306a36Sopenharmony_ci} 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_frame_, b_frame_index, bFrameIndex); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ciUVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, 8); 201862306a36Sopenharmony_ciUVCG_FRAME_ATTR(w_width, wWidth, 16); 201962306a36Sopenharmony_ciUVCG_FRAME_ATTR(w_height, wHeight, 16); 202062306a36Sopenharmony_ciUVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); 202162306a36Sopenharmony_ciUVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); 202262306a36Sopenharmony_ciUVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); 202362306a36Sopenharmony_ciUVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci#undef UVCG_FRAME_ATTR 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_cistatic ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, 202862306a36Sopenharmony_ci char *page) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci struct uvcg_frame *frm = to_uvcg_frame(item); 203162306a36Sopenharmony_ci struct f_uvc_opts *opts; 203262306a36Sopenharmony_ci struct config_item *opts_item; 203362306a36Sopenharmony_ci struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex; 203462306a36Sopenharmony_ci int result, i; 203562306a36Sopenharmony_ci char *pg = page; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; 204062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci mutex_lock(&opts->lock); 204362306a36Sopenharmony_ci for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) { 204462306a36Sopenharmony_ci result += sprintf(pg, "%u\n", frm->dw_frame_interval[i]); 204562306a36Sopenharmony_ci pg = page + result; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci mutex_unlock(su_mutex); 205062306a36Sopenharmony_ci return result; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, 205462306a36Sopenharmony_ci const char *page, size_t len) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci struct uvcg_frame *ch = to_uvcg_frame(item); 205762306a36Sopenharmony_ci struct f_uvc_opts *opts; 205862306a36Sopenharmony_ci struct config_item *opts_item; 205962306a36Sopenharmony_ci struct uvcg_format *fmt; 206062306a36Sopenharmony_ci struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex; 206162306a36Sopenharmony_ci int ret = 0, n = 0; 206262306a36Sopenharmony_ci u32 *frm_intrv, *tmp; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent; 206762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 206862306a36Sopenharmony_ci fmt = to_uvcg_format(ch->item.ci_parent); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci mutex_lock(&opts->lock); 207162306a36Sopenharmony_ci if (fmt->linked || opts->refcnt) { 207262306a36Sopenharmony_ci ret = -EBUSY; 207362306a36Sopenharmony_ci goto end; 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_count_item_entries, &n, sizeof(u32)); 207762306a36Sopenharmony_ci if (ret) 207862306a36Sopenharmony_ci goto end; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL); 208162306a36Sopenharmony_ci if (!frm_intrv) { 208262306a36Sopenharmony_ci ret = -ENOMEM; 208362306a36Sopenharmony_ci goto end; 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci ret = __uvcg_iter_item_entries(page, len, __uvcg_fill_item_entries, &tmp, sizeof(u32)); 208762306a36Sopenharmony_ci if (ret) { 208862306a36Sopenharmony_ci kfree(frm_intrv); 208962306a36Sopenharmony_ci goto end; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci kfree(ch->dw_frame_interval); 209362306a36Sopenharmony_ci ch->dw_frame_interval = frm_intrv; 209462306a36Sopenharmony_ci ch->frame.b_frame_interval_type = n; 209562306a36Sopenharmony_ci sort(ch->dw_frame_interval, n, sizeof(*ch->dw_frame_interval), 209662306a36Sopenharmony_ci uvcg_config_compare_u32, NULL); 209762306a36Sopenharmony_ci ret = len; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ciend: 210062306a36Sopenharmony_ci mutex_unlock(&opts->lock); 210162306a36Sopenharmony_ci mutex_unlock(su_mutex); 210262306a36Sopenharmony_ci return ret; 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ciUVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_frame_attrs[] = { 210862306a36Sopenharmony_ci &uvcg_frame_attr_b_frame_index, 210962306a36Sopenharmony_ci &uvcg_frame_attr_bm_capabilities, 211062306a36Sopenharmony_ci &uvcg_frame_attr_w_width, 211162306a36Sopenharmony_ci &uvcg_frame_attr_w_height, 211262306a36Sopenharmony_ci &uvcg_frame_attr_dw_min_bit_rate, 211362306a36Sopenharmony_ci &uvcg_frame_attr_dw_max_bit_rate, 211462306a36Sopenharmony_ci &uvcg_frame_attr_dw_max_video_frame_buffer_size, 211562306a36Sopenharmony_ci &uvcg_frame_attr_dw_default_frame_interval, 211662306a36Sopenharmony_ci &uvcg_frame_attr_dw_frame_interval, 211762306a36Sopenharmony_ci NULL, 211862306a36Sopenharmony_ci}; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_cistatic const struct config_item_type uvcg_frame_type = { 212162306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 212262306a36Sopenharmony_ci .ct_attrs = uvcg_frame_attrs, 212362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 212462306a36Sopenharmony_ci}; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_cistatic struct config_item *uvcg_frame_make(struct config_group *group, 212762306a36Sopenharmony_ci const char *name) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci struct uvcg_frame *h; 213062306a36Sopenharmony_ci struct uvcg_format *fmt; 213162306a36Sopenharmony_ci struct f_uvc_opts *opts; 213262306a36Sopenharmony_ci struct config_item *opts_item; 213362306a36Sopenharmony_ci struct uvcg_frame_ptr *frame_ptr; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 213662306a36Sopenharmony_ci if (!h) 213762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; 214062306a36Sopenharmony_ci h->frame.b_frame_index = 1; 214162306a36Sopenharmony_ci h->frame.w_width = 640; 214262306a36Sopenharmony_ci h->frame.w_height = 360; 214362306a36Sopenharmony_ci h->frame.dw_min_bit_rate = 18432000; 214462306a36Sopenharmony_ci h->frame.dw_max_bit_rate = 55296000; 214562306a36Sopenharmony_ci h->frame.dw_max_video_frame_buffer_size = 460800; 214662306a36Sopenharmony_ci h->frame.dw_default_frame_interval = 666666; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; 214962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci mutex_lock(&opts->lock); 215262306a36Sopenharmony_ci fmt = to_uvcg_format(&group->cg_item); 215362306a36Sopenharmony_ci if (fmt->type == UVCG_UNCOMPRESSED) { 215462306a36Sopenharmony_ci h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED; 215562306a36Sopenharmony_ci h->fmt_type = UVCG_UNCOMPRESSED; 215662306a36Sopenharmony_ci } else if (fmt->type == UVCG_MJPEG) { 215762306a36Sopenharmony_ci h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; 215862306a36Sopenharmony_ci h->fmt_type = UVCG_MJPEG; 215962306a36Sopenharmony_ci } else { 216062306a36Sopenharmony_ci mutex_unlock(&opts->lock); 216162306a36Sopenharmony_ci kfree(h); 216262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL); 216662306a36Sopenharmony_ci if (!frame_ptr) { 216762306a36Sopenharmony_ci mutex_unlock(&opts->lock); 216862306a36Sopenharmony_ci kfree(h); 216962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci frame_ptr->frm = h; 217362306a36Sopenharmony_ci list_add_tail(&frame_ptr->entry, &fmt->frames); 217462306a36Sopenharmony_ci ++fmt->num_frames; 217562306a36Sopenharmony_ci mutex_unlock(&opts->lock); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci config_item_init_type_name(&h->item, name, &uvcg_frame_type); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci return &h->item; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_cistatic void uvcg_frame_drop(struct config_group *group, struct config_item *item) 218362306a36Sopenharmony_ci{ 218462306a36Sopenharmony_ci struct uvcg_format *fmt; 218562306a36Sopenharmony_ci struct f_uvc_opts *opts; 218662306a36Sopenharmony_ci struct config_item *opts_item; 218762306a36Sopenharmony_ci struct uvcg_frame *target_frm = NULL; 218862306a36Sopenharmony_ci struct uvcg_frame_ptr *frame_ptr, *tmp; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; 219162306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci mutex_lock(&opts->lock); 219462306a36Sopenharmony_ci target_frm = container_of(item, struct uvcg_frame, item); 219562306a36Sopenharmony_ci fmt = to_uvcg_format(&group->cg_item); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci list_for_each_entry_safe(frame_ptr, tmp, &fmt->frames, entry) 219862306a36Sopenharmony_ci if (frame_ptr->frm == target_frm) { 219962306a36Sopenharmony_ci list_del(&frame_ptr->entry); 220062306a36Sopenharmony_ci kfree(frame_ptr); 220162306a36Sopenharmony_ci --fmt->num_frames; 220262306a36Sopenharmony_ci break; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci mutex_unlock(&opts->lock); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci config_item_put(item); 220762306a36Sopenharmony_ci} 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_cistatic void uvcg_format_set_indices(struct config_group *fmt) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci struct config_item *ci; 221262306a36Sopenharmony_ci unsigned int i = 1; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci list_for_each_entry(ci, &fmt->cg_children, ci_entry) { 221562306a36Sopenharmony_ci struct uvcg_frame *frm; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci if (ci->ci_type != &uvcg_frame_type) 221862306a36Sopenharmony_ci continue; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci frm = to_uvcg_frame(ci); 222162306a36Sopenharmony_ci frm->frame.b_frame_index = i++; 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 222662306a36Sopenharmony_ci * streaming/uncompressed/<NAME> 222762306a36Sopenharmony_ci */ 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_uncompressed_group_ops = { 223062306a36Sopenharmony_ci .make_item = uvcg_frame_make, 223162306a36Sopenharmony_ci .drop_item = uvcg_frame_drop, 223262306a36Sopenharmony_ci}; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic ssize_t uvcg_uncompressed_guid_format_show(struct config_item *item, 223562306a36Sopenharmony_ci char *page) 223662306a36Sopenharmony_ci{ 223762306a36Sopenharmony_ci struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item); 223862306a36Sopenharmony_ci struct f_uvc_opts *opts; 223962306a36Sopenharmony_ci struct config_item *opts_item; 224062306a36Sopenharmony_ci struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; 224562306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci mutex_lock(&opts->lock); 224862306a36Sopenharmony_ci memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); 224962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci mutex_unlock(su_mutex); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci return sizeof(ch->desc.guidFormat); 225462306a36Sopenharmony_ci} 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cistatic ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item, 225762306a36Sopenharmony_ci const char *page, size_t len) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item); 226062306a36Sopenharmony_ci struct f_uvc_opts *opts; 226162306a36Sopenharmony_ci struct config_item *opts_item; 226262306a36Sopenharmony_ci struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; 226362306a36Sopenharmony_ci int ret; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; 226862306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci mutex_lock(&opts->lock); 227162306a36Sopenharmony_ci if (ch->fmt.linked || opts->refcnt) { 227262306a36Sopenharmony_ci ret = -EBUSY; 227362306a36Sopenharmony_ci goto end; 227462306a36Sopenharmony_ci } 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci memcpy(ch->desc.guidFormat, page, 227762306a36Sopenharmony_ci min(sizeof(ch->desc.guidFormat), len)); 227862306a36Sopenharmony_ci ret = sizeof(ch->desc.guidFormat); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ciend: 228162306a36Sopenharmony_ci mutex_unlock(&opts->lock); 228262306a36Sopenharmony_ci mutex_unlock(su_mutex); 228362306a36Sopenharmony_ci return ret; 228462306a36Sopenharmony_ci} 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ciUVC_ATTR(uvcg_uncompressed_, guid_format, guidFormat); 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, bits) \ 228962306a36Sopenharmony_cistatic ssize_t uvcg_uncompressed_##cname##_show( \ 229062306a36Sopenharmony_ci struct config_item *item, char *page) \ 229162306a36Sopenharmony_ci{ \ 229262306a36Sopenharmony_ci struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ 229362306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 229462306a36Sopenharmony_ci struct config_item *opts_item; \ 229562306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 229662306a36Sopenharmony_ci int result; \ 229762306a36Sopenharmony_ci \ 229862306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 229962306a36Sopenharmony_ci \ 230062306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 230162306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 230262306a36Sopenharmony_ci \ 230362306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 230462306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ 230562306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 230662306a36Sopenharmony_ci \ 230762306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 230862306a36Sopenharmony_ci return result; \ 230962306a36Sopenharmony_ci} \ 231062306a36Sopenharmony_ci \ 231162306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_uncompressed_, cname, aname); 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci#define UVCG_UNCOMPRESSED_ATTR(cname, aname, bits) \ 231462306a36Sopenharmony_cistatic ssize_t uvcg_uncompressed_##cname##_show( \ 231562306a36Sopenharmony_ci struct config_item *item, char *page) \ 231662306a36Sopenharmony_ci{ \ 231762306a36Sopenharmony_ci struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ 231862306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 231962306a36Sopenharmony_ci struct config_item *opts_item; \ 232062306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 232162306a36Sopenharmony_ci int result; \ 232262306a36Sopenharmony_ci \ 232362306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 232462306a36Sopenharmony_ci \ 232562306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 232662306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 232762306a36Sopenharmony_ci \ 232862306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 232962306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ 233062306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 233162306a36Sopenharmony_ci \ 233262306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 233362306a36Sopenharmony_ci return result; \ 233462306a36Sopenharmony_ci} \ 233562306a36Sopenharmony_ci \ 233662306a36Sopenharmony_cistatic ssize_t \ 233762306a36Sopenharmony_ciuvcg_uncompressed_##cname##_store(struct config_item *item, \ 233862306a36Sopenharmony_ci const char *page, size_t len) \ 233962306a36Sopenharmony_ci{ \ 234062306a36Sopenharmony_ci struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ 234162306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 234262306a36Sopenharmony_ci struct config_item *opts_item; \ 234362306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 234462306a36Sopenharmony_ci int ret; \ 234562306a36Sopenharmony_ci u8 num; \ 234662306a36Sopenharmony_ci \ 234762306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 234862306a36Sopenharmony_ci \ 234962306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 235062306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 235162306a36Sopenharmony_ci \ 235262306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 235362306a36Sopenharmony_ci if (u->fmt.linked || opts->refcnt) { \ 235462306a36Sopenharmony_ci ret = -EBUSY; \ 235562306a36Sopenharmony_ci goto end; \ 235662306a36Sopenharmony_ci } \ 235762306a36Sopenharmony_ci \ 235862306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); \ 235962306a36Sopenharmony_ci if (ret) \ 236062306a36Sopenharmony_ci goto end; \ 236162306a36Sopenharmony_ci \ 236262306a36Sopenharmony_ci /* index values in uvc are never 0 */ \ 236362306a36Sopenharmony_ci if (!num) { \ 236462306a36Sopenharmony_ci ret = -EINVAL; \ 236562306a36Sopenharmony_ci goto end; \ 236662306a36Sopenharmony_ci } \ 236762306a36Sopenharmony_ci \ 236862306a36Sopenharmony_ci u->desc.aname = num; \ 236962306a36Sopenharmony_ci ret = len; \ 237062306a36Sopenharmony_ciend: \ 237162306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 237262306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 237362306a36Sopenharmony_ci return ret; \ 237462306a36Sopenharmony_ci} \ 237562306a36Sopenharmony_ci \ 237662306a36Sopenharmony_ciUVC_ATTR(uvcg_uncompressed_, cname, aname); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR_RO(b_format_index, bFormatIndex, 8); 237962306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, 8); 238062306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); 238162306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); 238262306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); 238362306a36Sopenharmony_ciUVCG_UNCOMPRESSED_ATTR_RO(bm_interlace_flags, bmInterlaceFlags, 8); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci#undef UVCG_UNCOMPRESSED_ATTR 238662306a36Sopenharmony_ci#undef UVCG_UNCOMPRESSED_ATTR_RO 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_cistatic inline ssize_t 238962306a36Sopenharmony_ciuvcg_uncompressed_bma_controls_show(struct config_item *item, char *page) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item); 239262306a36Sopenharmony_ci return uvcg_format_bma_controls_show(&unc->fmt, page); 239362306a36Sopenharmony_ci} 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_cistatic inline ssize_t 239662306a36Sopenharmony_ciuvcg_uncompressed_bma_controls_store(struct config_item *item, 239762306a36Sopenharmony_ci const char *page, size_t len) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item); 240062306a36Sopenharmony_ci return uvcg_format_bma_controls_store(&unc->fmt, page, len); 240162306a36Sopenharmony_ci} 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ciUVC_ATTR(uvcg_uncompressed_, bma_controls, bmaControls); 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_uncompressed_attrs[] = { 240662306a36Sopenharmony_ci &uvcg_uncompressed_attr_b_format_index, 240762306a36Sopenharmony_ci &uvcg_uncompressed_attr_guid_format, 240862306a36Sopenharmony_ci &uvcg_uncompressed_attr_b_bits_per_pixel, 240962306a36Sopenharmony_ci &uvcg_uncompressed_attr_b_default_frame_index, 241062306a36Sopenharmony_ci &uvcg_uncompressed_attr_b_aspect_ratio_x, 241162306a36Sopenharmony_ci &uvcg_uncompressed_attr_b_aspect_ratio_y, 241262306a36Sopenharmony_ci &uvcg_uncompressed_attr_bm_interlace_flags, 241362306a36Sopenharmony_ci &uvcg_uncompressed_attr_bma_controls, 241462306a36Sopenharmony_ci NULL, 241562306a36Sopenharmony_ci}; 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_cistatic const struct config_item_type uvcg_uncompressed_type = { 241862306a36Sopenharmony_ci .ct_item_ops = &uvcg_format_item_operations, 241962306a36Sopenharmony_ci .ct_group_ops = &uvcg_uncompressed_group_ops, 242062306a36Sopenharmony_ci .ct_attrs = uvcg_uncompressed_attrs, 242162306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 242262306a36Sopenharmony_ci}; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_cistatic struct config_group *uvcg_uncompressed_make(struct config_group *group, 242562306a36Sopenharmony_ci const char *name) 242662306a36Sopenharmony_ci{ 242762306a36Sopenharmony_ci static char guid[] = { 242862306a36Sopenharmony_ci 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 242962306a36Sopenharmony_ci 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 243062306a36Sopenharmony_ci }; 243162306a36Sopenharmony_ci struct uvcg_color_matching *color_match; 243262306a36Sopenharmony_ci struct config_item *streaming; 243362306a36Sopenharmony_ci struct uvcg_uncompressed *h; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci streaming = group->cg_item.ci_parent; 243662306a36Sopenharmony_ci color_match = uvcg_format_get_default_color_match(streaming); 243762306a36Sopenharmony_ci if (!color_match) 243862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 244162306a36Sopenharmony_ci if (!h) 244262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE; 244562306a36Sopenharmony_ci h->desc.bDescriptorType = USB_DT_CS_INTERFACE; 244662306a36Sopenharmony_ci h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED; 244762306a36Sopenharmony_ci memcpy(h->desc.guidFormat, guid, sizeof(guid)); 244862306a36Sopenharmony_ci h->desc.bBitsPerPixel = 16; 244962306a36Sopenharmony_ci h->desc.bDefaultFrameIndex = 1; 245062306a36Sopenharmony_ci h->desc.bAspectRatioX = 0; 245162306a36Sopenharmony_ci h->desc.bAspectRatioY = 0; 245262306a36Sopenharmony_ci h->desc.bmInterlaceFlags = 0; 245362306a36Sopenharmony_ci h->desc.bCopyProtect = 0; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci INIT_LIST_HEAD(&h->fmt.frames); 245662306a36Sopenharmony_ci h->fmt.type = UVCG_UNCOMPRESSED; 245762306a36Sopenharmony_ci h->fmt.color_matching = color_match; 245862306a36Sopenharmony_ci color_match->refcnt++; 245962306a36Sopenharmony_ci config_group_init_type_name(&h->fmt.group, name, 246062306a36Sopenharmony_ci &uvcg_uncompressed_type); 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci return &h->fmt.group; 246362306a36Sopenharmony_ci} 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_uncompressed_grp_ops = { 246662306a36Sopenharmony_ci .make_group = uvcg_uncompressed_make, 246762306a36Sopenharmony_ci}; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { 247062306a36Sopenharmony_ci .type = { 247162306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 247262306a36Sopenharmony_ci .ct_group_ops = &uvcg_uncompressed_grp_ops, 247362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 247462306a36Sopenharmony_ci }, 247562306a36Sopenharmony_ci .name = "uncompressed", 247662306a36Sopenharmony_ci}; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 247962306a36Sopenharmony_ci * streaming/mjpeg/<NAME> 248062306a36Sopenharmony_ci */ 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_mjpeg_group_ops = { 248362306a36Sopenharmony_ci .make_item = uvcg_frame_make, 248462306a36Sopenharmony_ci .drop_item = uvcg_frame_drop, 248562306a36Sopenharmony_ci}; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci#define UVCG_MJPEG_ATTR_RO(cname, aname, bits) \ 248862306a36Sopenharmony_cistatic ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ 248962306a36Sopenharmony_ci{ \ 249062306a36Sopenharmony_ci struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ 249162306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 249262306a36Sopenharmony_ci struct config_item *opts_item; \ 249362306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 249462306a36Sopenharmony_ci int result; \ 249562306a36Sopenharmony_ci \ 249662306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 249762306a36Sopenharmony_ci \ 249862306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 249962306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 250062306a36Sopenharmony_ci \ 250162306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 250262306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ 250362306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 250462306a36Sopenharmony_ci \ 250562306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 250662306a36Sopenharmony_ci return result; \ 250762306a36Sopenharmony_ci} \ 250862306a36Sopenharmony_ci \ 250962306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_mjpeg_, cname, aname) 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci#define UVCG_MJPEG_ATTR(cname, aname, bits) \ 251262306a36Sopenharmony_cistatic ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ 251362306a36Sopenharmony_ci{ \ 251462306a36Sopenharmony_ci struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ 251562306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 251662306a36Sopenharmony_ci struct config_item *opts_item; \ 251762306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 251862306a36Sopenharmony_ci int result; \ 251962306a36Sopenharmony_ci \ 252062306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 252162306a36Sopenharmony_ci \ 252262306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 252362306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 252462306a36Sopenharmony_ci \ 252562306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 252662306a36Sopenharmony_ci result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ 252762306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 252862306a36Sopenharmony_ci \ 252962306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 253062306a36Sopenharmony_ci return result; \ 253162306a36Sopenharmony_ci} \ 253262306a36Sopenharmony_ci \ 253362306a36Sopenharmony_cistatic ssize_t \ 253462306a36Sopenharmony_ciuvcg_mjpeg_##cname##_store(struct config_item *item, \ 253562306a36Sopenharmony_ci const char *page, size_t len) \ 253662306a36Sopenharmony_ci{ \ 253762306a36Sopenharmony_ci struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ 253862306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 253962306a36Sopenharmony_ci struct config_item *opts_item; \ 254062306a36Sopenharmony_ci struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ 254162306a36Sopenharmony_ci int ret; \ 254262306a36Sopenharmony_ci u8 num; \ 254362306a36Sopenharmony_ci \ 254462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 254562306a36Sopenharmony_ci \ 254662306a36Sopenharmony_ci opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ 254762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 254862306a36Sopenharmony_ci \ 254962306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 255062306a36Sopenharmony_ci if (u->fmt.linked || opts->refcnt) { \ 255162306a36Sopenharmony_ci ret = -EBUSY; \ 255262306a36Sopenharmony_ci goto end; \ 255362306a36Sopenharmony_ci } \ 255462306a36Sopenharmony_ci \ 255562306a36Sopenharmony_ci ret = kstrtou8(page, 0, &num); \ 255662306a36Sopenharmony_ci if (ret) \ 255762306a36Sopenharmony_ci goto end; \ 255862306a36Sopenharmony_ci \ 255962306a36Sopenharmony_ci /* index values in uvc are never 0 */ \ 256062306a36Sopenharmony_ci if (!num) { \ 256162306a36Sopenharmony_ci ret = -EINVAL; \ 256262306a36Sopenharmony_ci goto end; \ 256362306a36Sopenharmony_ci } \ 256462306a36Sopenharmony_ci \ 256562306a36Sopenharmony_ci u->desc.aname = num; \ 256662306a36Sopenharmony_ci ret = len; \ 256762306a36Sopenharmony_ciend: \ 256862306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 256962306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 257062306a36Sopenharmony_ci return ret; \ 257162306a36Sopenharmony_ci} \ 257262306a36Sopenharmony_ci \ 257362306a36Sopenharmony_ciUVC_ATTR(uvcg_mjpeg_, cname, aname) 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ciUVCG_MJPEG_ATTR_RO(b_format_index, bFormatIndex, 8); 257662306a36Sopenharmony_ciUVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); 257762306a36Sopenharmony_ciUVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, 8); 257862306a36Sopenharmony_ciUVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); 257962306a36Sopenharmony_ciUVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); 258062306a36Sopenharmony_ciUVCG_MJPEG_ATTR_RO(bm_interlace_flags, bmInterlaceFlags, 8); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci#undef UVCG_MJPEG_ATTR 258362306a36Sopenharmony_ci#undef UVCG_MJPEG_ATTR_RO 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_cistatic inline ssize_t 258662306a36Sopenharmony_ciuvcg_mjpeg_bma_controls_show(struct config_item *item, char *page) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); 258962306a36Sopenharmony_ci return uvcg_format_bma_controls_show(&u->fmt, page); 259062306a36Sopenharmony_ci} 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_cistatic inline ssize_t 259362306a36Sopenharmony_ciuvcg_mjpeg_bma_controls_store(struct config_item *item, 259462306a36Sopenharmony_ci const char *page, size_t len) 259562306a36Sopenharmony_ci{ 259662306a36Sopenharmony_ci struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); 259762306a36Sopenharmony_ci return uvcg_format_bma_controls_store(&u->fmt, page, len); 259862306a36Sopenharmony_ci} 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ciUVC_ATTR(uvcg_mjpeg_, bma_controls, bmaControls); 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_mjpeg_attrs[] = { 260362306a36Sopenharmony_ci &uvcg_mjpeg_attr_b_format_index, 260462306a36Sopenharmony_ci &uvcg_mjpeg_attr_b_default_frame_index, 260562306a36Sopenharmony_ci &uvcg_mjpeg_attr_bm_flags, 260662306a36Sopenharmony_ci &uvcg_mjpeg_attr_b_aspect_ratio_x, 260762306a36Sopenharmony_ci &uvcg_mjpeg_attr_b_aspect_ratio_y, 260862306a36Sopenharmony_ci &uvcg_mjpeg_attr_bm_interlace_flags, 260962306a36Sopenharmony_ci &uvcg_mjpeg_attr_bma_controls, 261062306a36Sopenharmony_ci NULL, 261162306a36Sopenharmony_ci}; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_cistatic const struct config_item_type uvcg_mjpeg_type = { 261462306a36Sopenharmony_ci .ct_item_ops = &uvcg_format_item_operations, 261562306a36Sopenharmony_ci .ct_group_ops = &uvcg_mjpeg_group_ops, 261662306a36Sopenharmony_ci .ct_attrs = uvcg_mjpeg_attrs, 261762306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 261862306a36Sopenharmony_ci}; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_cistatic struct config_group *uvcg_mjpeg_make(struct config_group *group, 262162306a36Sopenharmony_ci const char *name) 262262306a36Sopenharmony_ci{ 262362306a36Sopenharmony_ci struct uvcg_color_matching *color_match; 262462306a36Sopenharmony_ci struct config_item *streaming; 262562306a36Sopenharmony_ci struct uvcg_mjpeg *h; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci streaming = group->cg_item.ci_parent; 262862306a36Sopenharmony_ci color_match = uvcg_format_get_default_color_match(streaming); 262962306a36Sopenharmony_ci if (!color_match) 263062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci h = kzalloc(sizeof(*h), GFP_KERNEL); 263362306a36Sopenharmony_ci if (!h) 263462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE; 263762306a36Sopenharmony_ci h->desc.bDescriptorType = USB_DT_CS_INTERFACE; 263862306a36Sopenharmony_ci h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG; 263962306a36Sopenharmony_ci h->desc.bDefaultFrameIndex = 1; 264062306a36Sopenharmony_ci h->desc.bAspectRatioX = 0; 264162306a36Sopenharmony_ci h->desc.bAspectRatioY = 0; 264262306a36Sopenharmony_ci h->desc.bmInterlaceFlags = 0; 264362306a36Sopenharmony_ci h->desc.bCopyProtect = 0; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci INIT_LIST_HEAD(&h->fmt.frames); 264662306a36Sopenharmony_ci h->fmt.type = UVCG_MJPEG; 264762306a36Sopenharmony_ci h->fmt.color_matching = color_match; 264862306a36Sopenharmony_ci color_match->refcnt++; 264962306a36Sopenharmony_ci config_group_init_type_name(&h->fmt.group, name, 265062306a36Sopenharmony_ci &uvcg_mjpeg_type); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci return &h->fmt.group; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_mjpeg_grp_ops = { 265662306a36Sopenharmony_ci .make_group = uvcg_mjpeg_make, 265762306a36Sopenharmony_ci}; 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { 266062306a36Sopenharmony_ci .type = { 266162306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 266262306a36Sopenharmony_ci .ct_group_ops = &uvcg_mjpeg_grp_ops, 266362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 266462306a36Sopenharmony_ci }, 266562306a36Sopenharmony_ci .name = "mjpeg", 266662306a36Sopenharmony_ci}; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 266962306a36Sopenharmony_ci * streaming/color_matching/default 267062306a36Sopenharmony_ci */ 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci#define UVCG_COLOR_MATCHING_ATTR(cname, aname, bits) \ 267362306a36Sopenharmony_cistatic ssize_t uvcg_color_matching_##cname##_show( \ 267462306a36Sopenharmony_ci struct config_item *item, char *page) \ 267562306a36Sopenharmony_ci{ \ 267662306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 267762306a36Sopenharmony_ci struct uvcg_color_matching *color_match = \ 267862306a36Sopenharmony_ci to_uvcg_color_matching(group); \ 267962306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 268062306a36Sopenharmony_ci struct config_item *opts_item; \ 268162306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 268262306a36Sopenharmony_ci int result; \ 268362306a36Sopenharmony_ci \ 268462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 268562306a36Sopenharmony_ci \ 268662306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \ 268762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 268862306a36Sopenharmony_ci \ 268962306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 269062306a36Sopenharmony_ci result = sprintf(page, "%u\n", \ 269162306a36Sopenharmony_ci le##bits##_to_cpu(color_match->desc.aname)); \ 269262306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 269362306a36Sopenharmony_ci \ 269462306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 269562306a36Sopenharmony_ci return result; \ 269662306a36Sopenharmony_ci} \ 269762306a36Sopenharmony_ci \ 269862306a36Sopenharmony_cistatic ssize_t uvcg_color_matching_##cname##_store( \ 269962306a36Sopenharmony_ci struct config_item *item, const char *page, size_t len) \ 270062306a36Sopenharmony_ci{ \ 270162306a36Sopenharmony_ci struct config_group *group = to_config_group(item); \ 270262306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; \ 270362306a36Sopenharmony_ci struct uvcg_color_matching *color_match = \ 270462306a36Sopenharmony_ci to_uvcg_color_matching(group); \ 270562306a36Sopenharmony_ci struct f_uvc_opts *opts; \ 270662306a36Sopenharmony_ci struct config_item *opts_item; \ 270762306a36Sopenharmony_ci int ret; \ 270862306a36Sopenharmony_ci u##bits num; \ 270962306a36Sopenharmony_ci \ 271062306a36Sopenharmony_ci ret = kstrtou##bits(page, 0, &num); \ 271162306a36Sopenharmony_ci if (ret) \ 271262306a36Sopenharmony_ci return ret; \ 271362306a36Sopenharmony_ci \ 271462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ 271562306a36Sopenharmony_ci \ 271662306a36Sopenharmony_ci if (color_match->refcnt) { \ 271762306a36Sopenharmony_ci ret = -EBUSY; \ 271862306a36Sopenharmony_ci goto unlock_su; \ 271962306a36Sopenharmony_ci } \ 272062306a36Sopenharmony_ci \ 272162306a36Sopenharmony_ci opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \ 272262306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); \ 272362306a36Sopenharmony_ci \ 272462306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 272562306a36Sopenharmony_ci \ 272662306a36Sopenharmony_ci color_match->desc.aname = num; \ 272762306a36Sopenharmony_ci ret = len; \ 272862306a36Sopenharmony_ci \ 272962306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 273062306a36Sopenharmony_ciunlock_su: \ 273162306a36Sopenharmony_ci mutex_unlock(su_mutex); \ 273262306a36Sopenharmony_ci \ 273362306a36Sopenharmony_ci return ret; \ 273462306a36Sopenharmony_ci} \ 273562306a36Sopenharmony_ciUVC_ATTR(uvcg_color_matching_, cname, aname) 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ciUVCG_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, 8); 273862306a36Sopenharmony_ciUVCG_COLOR_MATCHING_ATTR(b_transfer_characteristics, bTransferCharacteristics, 8); 273962306a36Sopenharmony_ciUVCG_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, 8); 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci#undef UVCG_COLOR_MATCHING_ATTR 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_color_matching_attrs[] = { 274462306a36Sopenharmony_ci &uvcg_color_matching_attr_b_color_primaries, 274562306a36Sopenharmony_ci &uvcg_color_matching_attr_b_transfer_characteristics, 274662306a36Sopenharmony_ci &uvcg_color_matching_attr_b_matrix_coefficients, 274762306a36Sopenharmony_ci NULL, 274862306a36Sopenharmony_ci}; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_cistatic void uvcg_color_matching_release(struct config_item *item) 275162306a36Sopenharmony_ci{ 275262306a36Sopenharmony_ci struct uvcg_color_matching *color_match = 275362306a36Sopenharmony_ci to_uvcg_color_matching(to_config_group(item)); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci kfree(color_match); 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_color_matching_item_ops = { 275962306a36Sopenharmony_ci .release = uvcg_color_matching_release, 276062306a36Sopenharmony_ci}; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_cistatic const struct config_item_type uvcg_color_matching_type = { 276362306a36Sopenharmony_ci .ct_item_ops = &uvcg_color_matching_item_ops, 276462306a36Sopenharmony_ci .ct_attrs = uvcg_color_matching_attrs, 276562306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 276662306a36Sopenharmony_ci}; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 276962306a36Sopenharmony_ci * streaming/color_matching 277062306a36Sopenharmony_ci */ 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_cistatic struct config_group *uvcg_color_matching_make(struct config_group *group, 277362306a36Sopenharmony_ci const char *name) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci struct uvcg_color_matching *color_match; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci color_match = kzalloc(sizeof(*color_match), GFP_KERNEL); 277862306a36Sopenharmony_ci if (!color_match) 277962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci color_match->desc.bLength = UVC_DT_COLOR_MATCHING_SIZE; 278262306a36Sopenharmony_ci color_match->desc.bDescriptorType = USB_DT_CS_INTERFACE; 278362306a36Sopenharmony_ci color_match->desc.bDescriptorSubType = UVC_VS_COLORFORMAT; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci config_group_init_type_name(&color_match->group, name, 278662306a36Sopenharmony_ci &uvcg_color_matching_type); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci return &color_match->group; 278962306a36Sopenharmony_ci} 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic struct configfs_group_operations uvcg_color_matching_grp_group_ops = { 279262306a36Sopenharmony_ci .make_group = uvcg_color_matching_make, 279362306a36Sopenharmony_ci}; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_cistatic int uvcg_color_matching_create_children(struct config_group *parent) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci struct uvcg_color_matching *color_match; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci color_match = kzalloc(sizeof(*color_match), GFP_KERNEL); 280062306a36Sopenharmony_ci if (!color_match) 280162306a36Sopenharmony_ci return -ENOMEM; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci color_match->desc.bLength = UVC_DT_COLOR_MATCHING_SIZE; 280462306a36Sopenharmony_ci color_match->desc.bDescriptorType = USB_DT_CS_INTERFACE; 280562306a36Sopenharmony_ci color_match->desc.bDescriptorSubType = UVC_VS_COLORFORMAT; 280662306a36Sopenharmony_ci color_match->desc.bColorPrimaries = UVC_COLOR_PRIMARIES_BT_709_SRGB; 280762306a36Sopenharmony_ci color_match->desc.bTransferCharacteristics = UVC_TRANSFER_CHARACTERISTICS_BT_709; 280862306a36Sopenharmony_ci color_match->desc.bMatrixCoefficients = UVC_MATRIX_COEFFICIENTS_SMPTE_170M; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci config_group_init_type_name(&color_match->group, "default", 281162306a36Sopenharmony_ci &uvcg_color_matching_type); 281262306a36Sopenharmony_ci configfs_add_default_group(&color_match->group, parent); 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci return 0; 281562306a36Sopenharmony_ci} 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_color_matching_grp_type = { 281862306a36Sopenharmony_ci .type = { 281962306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 282062306a36Sopenharmony_ci .ct_group_ops = &uvcg_color_matching_grp_group_ops, 282162306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 282262306a36Sopenharmony_ci }, 282362306a36Sopenharmony_ci .name = "color_matching", 282462306a36Sopenharmony_ci .create_children = uvcg_color_matching_create_children, 282562306a36Sopenharmony_ci}; 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 282862306a36Sopenharmony_ci * streaming/class/{fs|hs|ss} 282962306a36Sopenharmony_ci */ 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_cistruct uvcg_streaming_class_group { 283262306a36Sopenharmony_ci struct config_group group; 283362306a36Sopenharmony_ci const char *name; 283462306a36Sopenharmony_ci}; 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_cistatic inline struct uvc_descriptor_header 283762306a36Sopenharmony_ci***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o) 283862306a36Sopenharmony_ci{ 283962306a36Sopenharmony_ci struct uvcg_streaming_class_group *group = 284062306a36Sopenharmony_ci container_of(i, struct uvcg_streaming_class_group, 284162306a36Sopenharmony_ci group.cg_item); 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci if (!strcmp(group->name, "fs")) 284462306a36Sopenharmony_ci return &o->uvc_fs_streaming_cls; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci if (!strcmp(group->name, "hs")) 284762306a36Sopenharmony_ci return &o->uvc_hs_streaming_cls; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci if (!strcmp(group->name, "ss")) 285062306a36Sopenharmony_ci return &o->uvc_ss_streaming_cls; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci return NULL; 285362306a36Sopenharmony_ci} 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cienum uvcg_strm_type { 285662306a36Sopenharmony_ci UVCG_HEADER = 0, 285762306a36Sopenharmony_ci UVCG_FORMAT, 285862306a36Sopenharmony_ci UVCG_FRAME, 285962306a36Sopenharmony_ci UVCG_COLOR_MATCHING, 286062306a36Sopenharmony_ci}; 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci/* 286362306a36Sopenharmony_ci * Iterate over a hierarchy of streaming descriptors' config items. 286462306a36Sopenharmony_ci * The items are created by the user with configfs. 286562306a36Sopenharmony_ci * 286662306a36Sopenharmony_ci * It "processes" the header pointed to by @priv1, then for each format 286762306a36Sopenharmony_ci * that follows the header "processes" the format itself and then for 286862306a36Sopenharmony_ci * each frame inside a format "processes" the frame. 286962306a36Sopenharmony_ci * 287062306a36Sopenharmony_ci * As a "processing" function the @fun is used. 287162306a36Sopenharmony_ci * 287262306a36Sopenharmony_ci * __uvcg_iter_strm_cls() is used in two context: first, to calculate 287362306a36Sopenharmony_ci * the amount of memory needed for an array of streaming descriptors 287462306a36Sopenharmony_ci * and second, to actually fill the array. 287562306a36Sopenharmony_ci * 287662306a36Sopenharmony_ci * @h: streaming header pointer 287762306a36Sopenharmony_ci * @priv2: an "inout" parameter (the caller might want to see the changes to it) 287862306a36Sopenharmony_ci * @priv3: an "inout" parameter (the caller might want to see the changes to it) 287962306a36Sopenharmony_ci * @fun: callback function for processing each level of the hierarchy 288062306a36Sopenharmony_ci */ 288162306a36Sopenharmony_cistatic int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, 288262306a36Sopenharmony_ci void *priv2, void *priv3, 288362306a36Sopenharmony_ci int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) 288462306a36Sopenharmony_ci{ 288562306a36Sopenharmony_ci struct uvcg_format_ptr *f; 288662306a36Sopenharmony_ci struct config_group *grp; 288762306a36Sopenharmony_ci struct config_item *item; 288862306a36Sopenharmony_ci struct uvcg_frame *frm; 288962306a36Sopenharmony_ci int ret, i, j; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci if (!fun) 289262306a36Sopenharmony_ci return -EINVAL; 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci i = j = 0; 289562306a36Sopenharmony_ci ret = fun(h, priv2, priv3, 0, UVCG_HEADER); 289662306a36Sopenharmony_ci if (ret) 289762306a36Sopenharmony_ci return ret; 289862306a36Sopenharmony_ci list_for_each_entry(f, &h->formats, entry) { 289962306a36Sopenharmony_ci ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT); 290062306a36Sopenharmony_ci if (ret) 290162306a36Sopenharmony_ci return ret; 290262306a36Sopenharmony_ci grp = &f->fmt->group; 290362306a36Sopenharmony_ci list_for_each_entry(item, &grp->cg_children, ci_entry) { 290462306a36Sopenharmony_ci frm = to_uvcg_frame(item); 290562306a36Sopenharmony_ci ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); 290662306a36Sopenharmony_ci if (ret) 290762306a36Sopenharmony_ci return ret; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci ret = fun(f->fmt->color_matching, priv2, priv3, 0, 291162306a36Sopenharmony_ci UVCG_COLOR_MATCHING); 291262306a36Sopenharmony_ci if (ret) 291362306a36Sopenharmony_ci return ret; 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci return ret; 291762306a36Sopenharmony_ci} 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci/* 292062306a36Sopenharmony_ci * Count how many bytes are needed for an array of streaming descriptors. 292162306a36Sopenharmony_ci * 292262306a36Sopenharmony_ci * @priv1: pointer to a header, format or frame 292362306a36Sopenharmony_ci * @priv2: inout parameter, accumulated size of the array 292462306a36Sopenharmony_ci * @priv3: inout parameter, accumulated number of the array elements 292562306a36Sopenharmony_ci * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls 292662306a36Sopenharmony_ci */ 292762306a36Sopenharmony_cistatic int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, 292862306a36Sopenharmony_ci enum uvcg_strm_type type) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci size_t *size = priv2; 293162306a36Sopenharmony_ci size_t *count = priv3; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci switch (type) { 293462306a36Sopenharmony_ci case UVCG_HEADER: { 293562306a36Sopenharmony_ci struct uvcg_streaming_header *h = priv1; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci *size += sizeof(h->desc); 293862306a36Sopenharmony_ci /* bmaControls */ 293962306a36Sopenharmony_ci *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE; 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ci break; 294262306a36Sopenharmony_ci case UVCG_FORMAT: { 294362306a36Sopenharmony_ci struct uvcg_format *fmt = priv1; 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci if (fmt->type == UVCG_UNCOMPRESSED) { 294662306a36Sopenharmony_ci struct uvcg_uncompressed *u = 294762306a36Sopenharmony_ci container_of(fmt, struct uvcg_uncompressed, 294862306a36Sopenharmony_ci fmt); 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci *size += sizeof(u->desc); 295162306a36Sopenharmony_ci } else if (fmt->type == UVCG_MJPEG) { 295262306a36Sopenharmony_ci struct uvcg_mjpeg *m = 295362306a36Sopenharmony_ci container_of(fmt, struct uvcg_mjpeg, fmt); 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci *size += sizeof(m->desc); 295662306a36Sopenharmony_ci } else { 295762306a36Sopenharmony_ci return -EINVAL; 295862306a36Sopenharmony_ci } 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci break; 296162306a36Sopenharmony_ci case UVCG_FRAME: { 296262306a36Sopenharmony_ci struct uvcg_frame *frm = priv1; 296362306a36Sopenharmony_ci int sz = sizeof(frm->dw_frame_interval); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci *size += sizeof(frm->frame); 296662306a36Sopenharmony_ci *size += frm->frame.b_frame_interval_type * sz; 296762306a36Sopenharmony_ci } 296862306a36Sopenharmony_ci break; 296962306a36Sopenharmony_ci case UVCG_COLOR_MATCHING: { 297062306a36Sopenharmony_ci struct uvcg_color_matching *color_match = priv1; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci *size += sizeof(color_match->desc); 297362306a36Sopenharmony_ci } 297462306a36Sopenharmony_ci break; 297562306a36Sopenharmony_ci } 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci ++*count; 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci return 0; 298062306a36Sopenharmony_ci} 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci/* 298362306a36Sopenharmony_ci * Fill an array of streaming descriptors. 298462306a36Sopenharmony_ci * 298562306a36Sopenharmony_ci * @priv1: pointer to a header, format or frame 298662306a36Sopenharmony_ci * @priv2: inout parameter, pointer into a block of memory 298762306a36Sopenharmony_ci * @priv3: inout parameter, pointer to a 2-dimensional array 298862306a36Sopenharmony_ci */ 298962306a36Sopenharmony_cistatic int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, 299062306a36Sopenharmony_ci enum uvcg_strm_type type) 299162306a36Sopenharmony_ci{ 299262306a36Sopenharmony_ci void **dest = priv2; 299362306a36Sopenharmony_ci struct uvc_descriptor_header ***array = priv3; 299462306a36Sopenharmony_ci size_t sz; 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci **array = *dest; 299762306a36Sopenharmony_ci ++*array; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci switch (type) { 300062306a36Sopenharmony_ci case UVCG_HEADER: { 300162306a36Sopenharmony_ci struct uvc_input_header_descriptor *ihdr = *dest; 300262306a36Sopenharmony_ci struct uvcg_streaming_header *h = priv1; 300362306a36Sopenharmony_ci struct uvcg_format_ptr *f; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci memcpy(*dest, &h->desc, sizeof(h->desc)); 300662306a36Sopenharmony_ci *dest += sizeof(h->desc); 300762306a36Sopenharmony_ci sz = UVCG_STREAMING_CONTROL_SIZE; 300862306a36Sopenharmony_ci list_for_each_entry(f, &h->formats, entry) { 300962306a36Sopenharmony_ci memcpy(*dest, f->fmt->bmaControls, sz); 301062306a36Sopenharmony_ci *dest += sz; 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz; 301362306a36Sopenharmony_ci ihdr->bNumFormats = h->num_fmt; 301462306a36Sopenharmony_ci } 301562306a36Sopenharmony_ci break; 301662306a36Sopenharmony_ci case UVCG_FORMAT: { 301762306a36Sopenharmony_ci struct uvcg_format *fmt = priv1; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci if (fmt->type == UVCG_UNCOMPRESSED) { 302062306a36Sopenharmony_ci struct uvcg_uncompressed *u = 302162306a36Sopenharmony_ci container_of(fmt, struct uvcg_uncompressed, 302262306a36Sopenharmony_ci fmt); 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci u->desc.bFormatIndex = n + 1; 302562306a36Sopenharmony_ci u->desc.bNumFrameDescriptors = fmt->num_frames; 302662306a36Sopenharmony_ci memcpy(*dest, &u->desc, sizeof(u->desc)); 302762306a36Sopenharmony_ci *dest += sizeof(u->desc); 302862306a36Sopenharmony_ci } else if (fmt->type == UVCG_MJPEG) { 302962306a36Sopenharmony_ci struct uvcg_mjpeg *m = 303062306a36Sopenharmony_ci container_of(fmt, struct uvcg_mjpeg, fmt); 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci m->desc.bFormatIndex = n + 1; 303362306a36Sopenharmony_ci m->desc.bNumFrameDescriptors = fmt->num_frames; 303462306a36Sopenharmony_ci memcpy(*dest, &m->desc, sizeof(m->desc)); 303562306a36Sopenharmony_ci *dest += sizeof(m->desc); 303662306a36Sopenharmony_ci } else { 303762306a36Sopenharmony_ci return -EINVAL; 303862306a36Sopenharmony_ci } 303962306a36Sopenharmony_ci } 304062306a36Sopenharmony_ci break; 304162306a36Sopenharmony_ci case UVCG_FRAME: { 304262306a36Sopenharmony_ci struct uvcg_frame *frm = priv1; 304362306a36Sopenharmony_ci struct uvc_descriptor_header *h = *dest; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci sz = sizeof(frm->frame); 304662306a36Sopenharmony_ci memcpy(*dest, &frm->frame, sz); 304762306a36Sopenharmony_ci *dest += sz; 304862306a36Sopenharmony_ci sz = frm->frame.b_frame_interval_type * 304962306a36Sopenharmony_ci sizeof(*frm->dw_frame_interval); 305062306a36Sopenharmony_ci memcpy(*dest, frm->dw_frame_interval, sz); 305162306a36Sopenharmony_ci *dest += sz; 305262306a36Sopenharmony_ci if (frm->fmt_type == UVCG_UNCOMPRESSED) 305362306a36Sopenharmony_ci h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE( 305462306a36Sopenharmony_ci frm->frame.b_frame_interval_type); 305562306a36Sopenharmony_ci else if (frm->fmt_type == UVCG_MJPEG) 305662306a36Sopenharmony_ci h->bLength = UVC_DT_FRAME_MJPEG_SIZE( 305762306a36Sopenharmony_ci frm->frame.b_frame_interval_type); 305862306a36Sopenharmony_ci } 305962306a36Sopenharmony_ci break; 306062306a36Sopenharmony_ci case UVCG_COLOR_MATCHING: { 306162306a36Sopenharmony_ci struct uvcg_color_matching *color_match = priv1; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci memcpy(*dest, &color_match->desc, sizeof(color_match->desc)); 306462306a36Sopenharmony_ci *dest += sizeof(color_match->desc); 306562306a36Sopenharmony_ci } 306662306a36Sopenharmony_ci break; 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci return 0; 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_cistatic int uvcg_streaming_class_allow_link(struct config_item *src, 307362306a36Sopenharmony_ci struct config_item *target) 307462306a36Sopenharmony_ci{ 307562306a36Sopenharmony_ci struct config_item *streaming, *header; 307662306a36Sopenharmony_ci struct f_uvc_opts *opts; 307762306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 307862306a36Sopenharmony_ci struct uvc_descriptor_header ***class_array, **cl_arr; 307962306a36Sopenharmony_ci struct uvcg_streaming_header *target_hdr; 308062306a36Sopenharmony_ci void *data, *data_save; 308162306a36Sopenharmony_ci size_t size = 0, count = 0; 308262306a36Sopenharmony_ci int ret = -EINVAL; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci streaming = src->ci_parent->ci_parent; 308762306a36Sopenharmony_ci header = config_group_find_item(to_config_group(streaming), "header"); 308862306a36Sopenharmony_ci if (!header || target->ci_parent != header) 308962306a36Sopenharmony_ci goto out; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci opts = to_f_uvc_opts(streaming->ci_parent); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci mutex_lock(&opts->lock); 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci class_array = __uvcg_get_stream_class_arr(src, opts); 309662306a36Sopenharmony_ci if (!class_array || *class_array || opts->refcnt) { 309762306a36Sopenharmony_ci ret = -EBUSY; 309862306a36Sopenharmony_ci goto unlock; 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci target_hdr = to_uvcg_streaming_header(target); 310262306a36Sopenharmony_ci ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm); 310362306a36Sopenharmony_ci if (ret) 310462306a36Sopenharmony_ci goto unlock; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci count += 1; /* NULL */ 310762306a36Sopenharmony_ci *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL); 310862306a36Sopenharmony_ci if (!*class_array) { 310962306a36Sopenharmony_ci ret = -ENOMEM; 311062306a36Sopenharmony_ci goto unlock; 311162306a36Sopenharmony_ci } 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci data = data_save = kzalloc(size, GFP_KERNEL); 311462306a36Sopenharmony_ci if (!data) { 311562306a36Sopenharmony_ci kfree(*class_array); 311662306a36Sopenharmony_ci *class_array = NULL; 311762306a36Sopenharmony_ci ret = -ENOMEM; 311862306a36Sopenharmony_ci goto unlock; 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci cl_arr = *class_array; 312162306a36Sopenharmony_ci ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr, 312262306a36Sopenharmony_ci __uvcg_fill_strm); 312362306a36Sopenharmony_ci if (ret) { 312462306a36Sopenharmony_ci kfree(*class_array); 312562306a36Sopenharmony_ci *class_array = NULL; 312662306a36Sopenharmony_ci /* 312762306a36Sopenharmony_ci * __uvcg_fill_strm() called from __uvcg_iter_stream_cls() 312862306a36Sopenharmony_ci * might have advanced the "data", so use a backup copy 312962306a36Sopenharmony_ci */ 313062306a36Sopenharmony_ci kfree(data_save); 313162306a36Sopenharmony_ci goto unlock; 313262306a36Sopenharmony_ci } 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci ++target_hdr->linked; 313562306a36Sopenharmony_ci ret = 0; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ciunlock: 313862306a36Sopenharmony_ci mutex_unlock(&opts->lock); 313962306a36Sopenharmony_ciout: 314062306a36Sopenharmony_ci config_item_put(header); 314162306a36Sopenharmony_ci mutex_unlock(su_mutex); 314262306a36Sopenharmony_ci return ret; 314362306a36Sopenharmony_ci} 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_cistatic void uvcg_streaming_class_drop_link(struct config_item *src, 314662306a36Sopenharmony_ci struct config_item *target) 314762306a36Sopenharmony_ci{ 314862306a36Sopenharmony_ci struct config_item *streaming, *header; 314962306a36Sopenharmony_ci struct f_uvc_opts *opts; 315062306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 315162306a36Sopenharmony_ci struct uvc_descriptor_header ***class_array; 315262306a36Sopenharmony_ci struct uvcg_streaming_header *target_hdr; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci streaming = src->ci_parent->ci_parent; 315762306a36Sopenharmony_ci header = config_group_find_item(to_config_group(streaming), "header"); 315862306a36Sopenharmony_ci if (!header || target->ci_parent != header) 315962306a36Sopenharmony_ci goto out; 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci opts = to_f_uvc_opts(streaming->ci_parent); 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci mutex_lock(&opts->lock); 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci class_array = __uvcg_get_stream_class_arr(src, opts); 316662306a36Sopenharmony_ci if (!class_array || !*class_array) 316762306a36Sopenharmony_ci goto unlock; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci if (opts->refcnt) 317062306a36Sopenharmony_ci goto unlock; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci target_hdr = to_uvcg_streaming_header(target); 317362306a36Sopenharmony_ci --target_hdr->linked; 317462306a36Sopenharmony_ci kfree(**class_array); 317562306a36Sopenharmony_ci kfree(*class_array); 317662306a36Sopenharmony_ci *class_array = NULL; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ciunlock: 317962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 318062306a36Sopenharmony_ciout: 318162306a36Sopenharmony_ci config_item_put(header); 318262306a36Sopenharmony_ci mutex_unlock(su_mutex); 318362306a36Sopenharmony_ci} 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_cistatic struct configfs_item_operations uvcg_streaming_class_item_ops = { 318662306a36Sopenharmony_ci .release = uvcg_config_item_release, 318762306a36Sopenharmony_ci .allow_link = uvcg_streaming_class_allow_link, 318862306a36Sopenharmony_ci .drop_link = uvcg_streaming_class_drop_link, 318962306a36Sopenharmony_ci}; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_cistatic const struct config_item_type uvcg_streaming_class_type = { 319262306a36Sopenharmony_ci .ct_item_ops = &uvcg_streaming_class_item_ops, 319362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 319462306a36Sopenharmony_ci}; 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 319762306a36Sopenharmony_ci * streaming/class 319862306a36Sopenharmony_ci */ 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_cistatic int uvcg_streaming_class_create_children(struct config_group *parent) 320162306a36Sopenharmony_ci{ 320262306a36Sopenharmony_ci static const char * const names[] = { "fs", "hs", "ss" }; 320362306a36Sopenharmony_ci unsigned int i; 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(names); ++i) { 320662306a36Sopenharmony_ci struct uvcg_streaming_class_group *group; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci group = kzalloc(sizeof(*group), GFP_KERNEL); 320962306a36Sopenharmony_ci if (!group) 321062306a36Sopenharmony_ci return -ENOMEM; 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci group->name = names[i]; 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci config_group_init_type_name(&group->group, group->name, 321562306a36Sopenharmony_ci &uvcg_streaming_class_type); 321662306a36Sopenharmony_ci configfs_add_default_group(&group->group, parent); 321762306a36Sopenharmony_ci } 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci return 0; 322062306a36Sopenharmony_ci} 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_streaming_class_grp_type = { 322362306a36Sopenharmony_ci .type = { 322462306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 322562306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 322662306a36Sopenharmony_ci }, 322762306a36Sopenharmony_ci .name = "class", 322862306a36Sopenharmony_ci .create_children = uvcg_streaming_class_create_children, 322962306a36Sopenharmony_ci}; 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 323262306a36Sopenharmony_ci * streaming 323362306a36Sopenharmony_ci */ 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_cistatic ssize_t uvcg_default_streaming_b_interface_number_show( 323662306a36Sopenharmony_ci struct config_item *item, char *page) 323762306a36Sopenharmony_ci{ 323862306a36Sopenharmony_ci struct config_group *group = to_config_group(item); 323962306a36Sopenharmony_ci struct mutex *su_mutex = &group->cg_subsys->su_mutex; 324062306a36Sopenharmony_ci struct config_item *opts_item; 324162306a36Sopenharmony_ci struct f_uvc_opts *opts; 324262306a36Sopenharmony_ci int result = 0; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci opts_item = item->ci_parent; 324762306a36Sopenharmony_ci opts = to_f_uvc_opts(opts_item); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci mutex_lock(&opts->lock); 325062306a36Sopenharmony_ci result += sprintf(page, "%u\n", opts->streaming_interface); 325162306a36Sopenharmony_ci mutex_unlock(&opts->lock); 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci mutex_unlock(su_mutex); 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci return result; 325662306a36Sopenharmony_ci} 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ciUVC_ATTR_RO(uvcg_default_streaming_, b_interface_number, bInterfaceNumber); 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_cistatic struct configfs_attribute *uvcg_default_streaming_attrs[] = { 326162306a36Sopenharmony_ci &uvcg_default_streaming_attr_b_interface_number, 326262306a36Sopenharmony_ci NULL, 326362306a36Sopenharmony_ci}; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvcg_streaming_grp_type = { 326662306a36Sopenharmony_ci .type = { 326762306a36Sopenharmony_ci .ct_item_ops = &uvcg_config_item_ops, 326862306a36Sopenharmony_ci .ct_attrs = uvcg_default_streaming_attrs, 326962306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 327062306a36Sopenharmony_ci }, 327162306a36Sopenharmony_ci .name = "streaming", 327262306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 327362306a36Sopenharmony_ci &uvcg_streaming_header_grp_type, 327462306a36Sopenharmony_ci &uvcg_uncompressed_grp_type, 327562306a36Sopenharmony_ci &uvcg_mjpeg_grp_type, 327662306a36Sopenharmony_ci &uvcg_color_matching_grp_type, 327762306a36Sopenharmony_ci &uvcg_streaming_class_grp_type, 327862306a36Sopenharmony_ci NULL, 327962306a36Sopenharmony_ci }, 328062306a36Sopenharmony_ci}; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 328362306a36Sopenharmony_ci * UVC function 328462306a36Sopenharmony_ci */ 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_cistatic void uvc_func_item_release(struct config_item *item) 328762306a36Sopenharmony_ci{ 328862306a36Sopenharmony_ci struct f_uvc_opts *opts = to_f_uvc_opts(item); 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci uvcg_config_remove_children(to_config_group(item)); 329162306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 329262306a36Sopenharmony_ci} 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_cistatic int uvc_func_allow_link(struct config_item *src, struct config_item *tgt) 329562306a36Sopenharmony_ci{ 329662306a36Sopenharmony_ci struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; 329762306a36Sopenharmony_ci struct gadget_string *string; 329862306a36Sopenharmony_ci struct config_item *strings; 329962306a36Sopenharmony_ci struct f_uvc_opts *opts; 330062306a36Sopenharmony_ci int ret = 0; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci mutex_lock(su_mutex); /* for navigating configfs hierarchy */ 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci /* Validate that the target is an entry in strings/<langid> */ 330562306a36Sopenharmony_ci strings = config_group_find_item(to_config_group(src->ci_parent->ci_parent), 330662306a36Sopenharmony_ci "strings"); 330762306a36Sopenharmony_ci if (!strings || tgt->ci_parent->ci_parent != strings) { 330862306a36Sopenharmony_ci ret = -EINVAL; 330962306a36Sopenharmony_ci goto put_strings; 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci string = to_gadget_string(tgt); 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci opts = to_f_uvc_opts(src); 331562306a36Sopenharmony_ci mutex_lock(&opts->lock); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci if (!strcmp(tgt->ci_name, "iad_desc")) 331862306a36Sopenharmony_ci opts->iad_index = string->usb_string.id; 331962306a36Sopenharmony_ci else if (!strcmp(tgt->ci_name, "vs0_desc")) 332062306a36Sopenharmony_ci opts->vs0_index = string->usb_string.id; 332162306a36Sopenharmony_ci else if (!strcmp(tgt->ci_name, "vs1_desc")) 332262306a36Sopenharmony_ci opts->vs1_index = string->usb_string.id; 332362306a36Sopenharmony_ci else 332462306a36Sopenharmony_ci ret = -EINVAL; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci mutex_unlock(&opts->lock); 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ciput_strings: 332962306a36Sopenharmony_ci config_item_put(strings); 333062306a36Sopenharmony_ci mutex_unlock(su_mutex); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci return ret; 333362306a36Sopenharmony_ci} 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_cistatic void uvc_func_drop_link(struct config_item *src, struct config_item *tgt) 333662306a36Sopenharmony_ci{ 333762306a36Sopenharmony_ci struct f_uvc_opts *opts; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci opts = to_f_uvc_opts(src); 334062306a36Sopenharmony_ci mutex_lock(&opts->lock); 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci if (!strcmp(tgt->ci_name, "iad_desc")) 334362306a36Sopenharmony_ci opts->iad_index = 0; 334462306a36Sopenharmony_ci else if (!strcmp(tgt->ci_name, "vs0_desc")) 334562306a36Sopenharmony_ci opts->vs0_index = 0; 334662306a36Sopenharmony_ci else if (!strcmp(tgt->ci_name, "vs1_desc")) 334762306a36Sopenharmony_ci opts->vs1_index = 0; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci mutex_unlock(&opts->lock); 335062306a36Sopenharmony_ci} 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_cistatic struct configfs_item_operations uvc_func_item_ops = { 335362306a36Sopenharmony_ci .release = uvc_func_item_release, 335462306a36Sopenharmony_ci .allow_link = uvc_func_allow_link, 335562306a36Sopenharmony_ci .drop_link = uvc_func_drop_link, 335662306a36Sopenharmony_ci}; 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci#define UVCG_OPTS_ATTR(cname, aname, limit) \ 335962306a36Sopenharmony_cistatic ssize_t f_uvc_opts_##cname##_show( \ 336062306a36Sopenharmony_ci struct config_item *item, char *page) \ 336162306a36Sopenharmony_ci{ \ 336262306a36Sopenharmony_ci struct f_uvc_opts *opts = to_f_uvc_opts(item); \ 336362306a36Sopenharmony_ci int result; \ 336462306a36Sopenharmony_ci \ 336562306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 336662306a36Sopenharmony_ci result = sprintf(page, "%u\n", opts->cname); \ 336762306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 336862306a36Sopenharmony_ci \ 336962306a36Sopenharmony_ci return result; \ 337062306a36Sopenharmony_ci} \ 337162306a36Sopenharmony_ci \ 337262306a36Sopenharmony_cistatic ssize_t \ 337362306a36Sopenharmony_cif_uvc_opts_##cname##_store(struct config_item *item, \ 337462306a36Sopenharmony_ci const char *page, size_t len) \ 337562306a36Sopenharmony_ci{ \ 337662306a36Sopenharmony_ci struct f_uvc_opts *opts = to_f_uvc_opts(item); \ 337762306a36Sopenharmony_ci unsigned int num; \ 337862306a36Sopenharmony_ci int ret; \ 337962306a36Sopenharmony_ci \ 338062306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 338162306a36Sopenharmony_ci if (opts->refcnt) { \ 338262306a36Sopenharmony_ci ret = -EBUSY; \ 338362306a36Sopenharmony_ci goto end; \ 338462306a36Sopenharmony_ci } \ 338562306a36Sopenharmony_ci \ 338662306a36Sopenharmony_ci ret = kstrtouint(page, 0, &num); \ 338762306a36Sopenharmony_ci if (ret) \ 338862306a36Sopenharmony_ci goto end; \ 338962306a36Sopenharmony_ci \ 339062306a36Sopenharmony_ci if (num > limit) { \ 339162306a36Sopenharmony_ci ret = -EINVAL; \ 339262306a36Sopenharmony_ci goto end; \ 339362306a36Sopenharmony_ci } \ 339462306a36Sopenharmony_ci opts->cname = num; \ 339562306a36Sopenharmony_ci ret = len; \ 339662306a36Sopenharmony_ciend: \ 339762306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 339862306a36Sopenharmony_ci return ret; \ 339962306a36Sopenharmony_ci} \ 340062306a36Sopenharmony_ci \ 340162306a36Sopenharmony_ciUVC_ATTR(f_uvc_opts_, cname, cname) 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ciUVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16); 340462306a36Sopenharmony_ciUVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072); 340562306a36Sopenharmony_ciUVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci#undef UVCG_OPTS_ATTR 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci#define UVCG_OPTS_STRING_ATTR(cname, aname) \ 341062306a36Sopenharmony_cistatic ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\ 341162306a36Sopenharmony_ci char *page) \ 341262306a36Sopenharmony_ci{ \ 341362306a36Sopenharmony_ci struct f_uvc_opts *opts = to_f_uvc_opts(item); \ 341462306a36Sopenharmony_ci int result; \ 341562306a36Sopenharmony_ci \ 341662306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 341762306a36Sopenharmony_ci result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\ 341862306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 341962306a36Sopenharmony_ci \ 342062306a36Sopenharmony_ci return result; \ 342162306a36Sopenharmony_ci} \ 342262306a36Sopenharmony_ci \ 342362306a36Sopenharmony_cistatic ssize_t f_uvc_opts_string_##cname##_store(struct config_item *item,\ 342462306a36Sopenharmony_ci const char *page, size_t len) \ 342562306a36Sopenharmony_ci{ \ 342662306a36Sopenharmony_ci struct f_uvc_opts *opts = to_f_uvc_opts(item); \ 342762306a36Sopenharmony_ci int size = min(sizeof(opts->aname), len + 1); \ 342862306a36Sopenharmony_ci int ret = 0; \ 342962306a36Sopenharmony_ci \ 343062306a36Sopenharmony_ci mutex_lock(&opts->lock); \ 343162306a36Sopenharmony_ci if (opts->refcnt) { \ 343262306a36Sopenharmony_ci ret = -EBUSY; \ 343362306a36Sopenharmony_ci goto end; \ 343462306a36Sopenharmony_ci } \ 343562306a36Sopenharmony_ci \ 343662306a36Sopenharmony_ci ret = strscpy(opts->aname, page, size); \ 343762306a36Sopenharmony_ci if (ret == -E2BIG) \ 343862306a36Sopenharmony_ci ret = size - 1; \ 343962306a36Sopenharmony_ci \ 344062306a36Sopenharmony_ciend: \ 344162306a36Sopenharmony_ci mutex_unlock(&opts->lock); \ 344262306a36Sopenharmony_ci return ret; \ 344362306a36Sopenharmony_ci} \ 344462306a36Sopenharmony_ci \ 344562306a36Sopenharmony_ciUVC_ATTR(f_uvc_opts_string_, cname, aname) 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ciUVCG_OPTS_STRING_ATTR(function_name, function_name); 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ci#undef UVCG_OPTS_STRING_ATTR 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_cistatic struct configfs_attribute *uvc_attrs[] = { 345262306a36Sopenharmony_ci &f_uvc_opts_attr_streaming_interval, 345362306a36Sopenharmony_ci &f_uvc_opts_attr_streaming_maxpacket, 345462306a36Sopenharmony_ci &f_uvc_opts_attr_streaming_maxburst, 345562306a36Sopenharmony_ci &f_uvc_opts_string_attr_function_name, 345662306a36Sopenharmony_ci NULL, 345762306a36Sopenharmony_ci}; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_cistatic const struct uvcg_config_group_type uvc_func_type = { 346062306a36Sopenharmony_ci .type = { 346162306a36Sopenharmony_ci .ct_item_ops = &uvc_func_item_ops, 346262306a36Sopenharmony_ci .ct_attrs = uvc_attrs, 346362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 346462306a36Sopenharmony_ci }, 346562306a36Sopenharmony_ci .name = "", 346662306a36Sopenharmony_ci .children = (const struct uvcg_config_group_type*[]) { 346762306a36Sopenharmony_ci &uvcg_control_grp_type, 346862306a36Sopenharmony_ci &uvcg_streaming_grp_type, 346962306a36Sopenharmony_ci NULL, 347062306a36Sopenharmony_ci }, 347162306a36Sopenharmony_ci}; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ciint uvcg_attach_configfs(struct f_uvc_opts *opts) 347462306a36Sopenharmony_ci{ 347562306a36Sopenharmony_ci int ret; 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name, 347862306a36Sopenharmony_ci &uvc_func_type.type); 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci ret = uvcg_config_create_children(&opts->func_inst.group, 348162306a36Sopenharmony_ci &uvc_func_type); 348262306a36Sopenharmony_ci if (ret < 0) 348362306a36Sopenharmony_ci config_group_put(&opts->func_inst.group); 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci return ret; 348662306a36Sopenharmony_ci} 3487