mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			843 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			843 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Diff
		
	
	
| --- linux-4.9.37/drivers/usb/gadget/function/uvc_configfs.c	2017-07-12 16:42:41.000000000 +0300
 | |
| +++ linux-4.9.y/drivers/usb/gadget/function/uvc_configfs.c	2021-06-07 13:01:34.000000000 +0300
 | |
| @@ -254,7 +254,49 @@
 | |
|  	return result;
 | |
|  }
 | |
|  
 | |
| -UVC_ATTR_RO(uvcg_default_processing_, bm_controls, bmControls);
 | |
| +static ssize_t uvcg_default_processing_bm_controls_store(
 | |
| +	struct config_item *item, const char *page, size_t len)
 | |
| +{
 | |
| +	struct uvcg_default_processing *dp = to_uvcg_default_processing(item);
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
 | |
| +	struct uvc_processing_unit_descriptor *pd;
 | |
| +	int ret, i;
 | |
| +	const char *pg = page;
 | |
| +	/* sign, base 2 representation, newline, terminator */
 | |
| +	char buf[1 + sizeof(u8) * 8 + 1 + 1];
 | |
| +	int idx;
 | |
| +
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
 | |
| +
 | |
| +	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +	pd = &opts->uvc_processing;
 | |
| +
 | |
| +	idx = 0;
 | |
| +	while (pg - page < len) {
 | |
| +		i = 0;
 | |
| +		while (i < sizeof(buf) && (pg - page < len) &&
 | |
| +				*pg != '\0' && *pg != '\n')
 | |
| +			buf[i++] = *pg++;
 | |
| +		while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
 | |
| +			++pg;
 | |
| +		buf[i] = '\0';
 | |
| +		ret = kstrtou8(buf, 0, &pd->bmControls[idx++]);
 | |
| +		if (ret < 0)
 | |
| +			goto end;
 | |
| +		if (idx >= pd->bControlSize)
 | |
| +			break;
 | |
| +	}
 | |
| +	ret = len;
 | |
| +end:
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +	mutex_unlock(su_mutex);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +UVC_ATTR(uvcg_default_processing_, bm_controls, bmControls);
 | |
|  
 | |
|  static struct configfs_attribute *uvcg_default_processing_attrs[] = {
 | |
|  	&uvcg_default_processing_attr_b_unit_id,
 | |
| @@ -281,6 +323,110 @@
 | |
|  	.ct_owner = THIS_MODULE,
 | |
|  };
 | |
|  
 | |
| +/* control/extension/default */
 | |
| +static struct uvcg_default_extension {
 | |
| +	struct config_group	group;
 | |
| +} uvcg_default_extension;
 | |
| +
 | |
| +static inline struct uvcg_default_extension
 | |
| +*to_uvcg_default_extension(struct config_item *item)
 | |
| +{
 | |
| +	return container_of(to_config_group(item),
 | |
| +			    struct uvcg_default_extension, group);
 | |
| +}
 | |
| +
 | |
| +#define UVCG_DEFAULT_EXTENSION_ATTR(cname, aname, conv)		\
 | |
| +static ssize_t uvcg_default_extension_##cname##_show(			\
 | |
| +	struct config_item *item, char *page)				\
 | |
| +{									\
 | |
| +	struct uvcg_default_extension *dp = to_uvcg_default_extension(item); \
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;	\
 | |
| +	struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) *ed;			\
 | |
| +	int result;							\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;	\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +	ed = &opts->uvc_extension;					\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	result = sprintf(page, "%d\n", conv(ed->aname));		\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +									\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return result;							\
 | |
| +}									\
 | |
| +									\
 | |
| +UVC_ATTR_RO(uvcg_default_extension_, cname, aname)
 | |
| +
 | |
| +#define identity_conv(x) (x)
 | |
| +
 | |
| +UVCG_DEFAULT_EXTENSION_ATTR(b_unit_id, bUnitID, identity_conv);
 | |
| +UVCG_DEFAULT_EXTENSION_ATTR(b_num_input_pins, bNrInPins, identity_conv);
 | |
| +UVCG_DEFAULT_EXTENSION_ATTR(i_extension, iExtension, identity_conv);
 | |
| +
 | |
| +#undef identity_conv
 | |
| +
 | |
| +#undef UVCG_DEFAULT_EXTENSION_ATTR
 | |
| +
 | |
| +static ssize_t uvcg_default_extension_bm_controls_show(
 | |
| +	struct config_item *item, char *page)
 | |
| +{
 | |
| +	struct uvcg_default_extension *dp = to_uvcg_default_extension(item);
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
 | |
| +	struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) *ed;
 | |
| +	int result, i;
 | |
| +	char *pg = page;
 | |
| +
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
 | |
| +
 | |
| +	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +	ed = &opts->uvc_extension;
 | |
| +
 | |
| +	mutex_lock(&opts->lock);
 | |
| +	for (result = 0, i = 0; i < ed->bControlSize; ++i) {
 | |
| +		result += sprintf(pg, "%d\n", ed->bmControls[i]);
 | |
| +		pg = page + result;
 | |
| +	}
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +
 | |
| +	mutex_unlock(su_mutex);
 | |
| +
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +UVC_ATTR_RO(uvcg_default_extension_, bm_controls, bmControls);
 | |
| +
 | |
| +static struct configfs_attribute *uvcg_default_extension_attrs[] = {
 | |
| +	&uvcg_default_extension_attr_b_unit_id,
 | |
| +	&uvcg_default_extension_attr_b_num_input_pins,
 | |
| +	&uvcg_default_extension_attr_bm_controls,
 | |
| +	&uvcg_default_extension_attr_i_extension,
 | |
| +	NULL,
 | |
| +};
 | |
| +
 | |
| +static struct config_item_type uvcg_default_extension_type = {
 | |
| +	.ct_attrs	= uvcg_default_extension_attrs,
 | |
| +	.ct_owner	= THIS_MODULE,
 | |
| +};
 | |
| +
 | |
| +/* struct uvcg_extension {}; */
 | |
| +
 | |
| +/* control/extension */
 | |
| +static struct uvcg_extension_grp {
 | |
| +	struct config_group	group;
 | |
| +} uvcg_extension_grp;
 | |
| +
 | |
| +static struct config_item_type uvcg_extension_grp_type = {
 | |
| +	.ct_owner = THIS_MODULE,
 | |
| +};
 | |
| +
 | |
|  /* control/terminal/camera/default */
 | |
|  static struct uvcg_default_camera {
 | |
|  	struct config_group	group;
 | |
| @@ -368,7 +514,50 @@
 | |
|  	return result;
 | |
|  }
 | |
|  
 | |
| -UVC_ATTR_RO(uvcg_default_camera_, bm_controls, bmControls);
 | |
| +static ssize_t uvcg_default_camera_bm_controls_store(
 | |
| +	struct config_item *item, const char *page, size_t len)
 | |
| +{
 | |
| +	struct uvcg_default_camera *dc = to_uvcg_default_camera(item);
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
 | |
| +	struct uvc_camera_terminal_descriptor *cd;
 | |
| +	int ret, i;
 | |
| +	const char *pg = page;
 | |
| +	/* sign, base 2 representation, newline, terminator */
 | |
| +	char buf[1 + sizeof(u8) * 8 + 1 + 1];
 | |
| +	int idx;
 | |
| +
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
 | |
| +
 | |
| +	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
 | |
| +			ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +	cd = &opts->uvc_camera_terminal;
 | |
| +
 | |
| +	idx = 0;
 | |
| +	while (pg - page < len) {
 | |
| +		i = 0;
 | |
| +		while (i < sizeof(buf) && (pg - page < len) &&
 | |
| +				*pg != '\0' && *pg != '\n')
 | |
| +			buf[i++] = *pg++;
 | |
| +		while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
 | |
| +			++pg;
 | |
| +		buf[i] = '\0';
 | |
| +		ret = kstrtou8(buf, 0, &cd->bmControls[idx++]);
 | |
| +		if (ret < 0)
 | |
| +			goto end;
 | |
| +		if (idx >= cd->bControlSize)
 | |
| +			break;
 | |
| +	}
 | |
| +	ret = len;
 | |
| +end:
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +	mutex_unlock(su_mutex);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +UVC_ATTR(uvcg_default_camera_, bm_controls, bmControls);
 | |
|  
 | |
|  static struct configfs_attribute *uvcg_default_camera_attrs[] = {
 | |
|  	&uvcg_default_camera_attr_b_terminal_id,
 | |
| @@ -626,14 +815,21 @@
 | |
|  	struct config_group	group;
 | |
|  } uvcg_mjpeg_grp;
 | |
|  
 | |
| +/* streaming/frame_based */
 | |
| +static struct uvcg_frame_based_format_grp {
 | |
| +	struct config_group	group;
 | |
| +} uvcg_frame_based_format_grp;
 | |
| +
 | |
|  static struct config_item *fmt_parent[] = {
 | |
|  	&uvcg_uncompressed_grp.group.cg_item,
 | |
|  	&uvcg_mjpeg_grp.group.cg_item,
 | |
| +	&uvcg_frame_based_format_grp.group.cg_item,
 | |
|  };
 | |
|  
 | |
|  enum uvcg_format_type {
 | |
|  	UVCG_UNCOMPRESSED = 0,
 | |
|  	UVCG_MJPEG,
 | |
| +	UVCG_FRAME_FRAME_BASED,
 | |
|  };
 | |
|  
 | |
|  struct uvcg_format {
 | |
| @@ -1203,6 +1399,7 @@
 | |
|  		return ERR_PTR(-EINVAL);
 | |
|  	}
 | |
|  	++fmt->num_frames;
 | |
| +	h->frame.b_frame_index	= fmt->num_frames;
 | |
|  	mutex_unlock(&opts->lock);
 | |
|  
 | |
|  	config_item_init_type_name(&h->item, name, &uvcg_frame_type);
 | |
| @@ -1227,6 +1424,263 @@
 | |
|  	mutex_unlock(&opts->lock);
 | |
|  }
 | |
|  
 | |
| +struct uvcg_frame_based_frame {
 | |
| +	struct {
 | |
| +		u8	b_length;
 | |
| +		u8	b_descriptor_type;
 | |
| +		u8	b_descriptor_subtype;
 | |
| +		u8	b_frame_index;
 | |
| +		u8	bm_capabilities;
 | |
| +		u16	w_width;
 | |
| +		u16	w_height;
 | |
| +		u32	dw_min_bit_rate;
 | |
| +		u32	dw_max_bit_rate;
 | |
| +		u32	dw_default_frame_interval;
 | |
| +		u8	b_frame_interval_type;
 | |
| +		u32  dw_bytes_per_line;
 | |
| +	} __attribute__((packed)) frame;
 | |
| +	u32 *dw_frame_interval;
 | |
| +	enum uvcg_format_type	fmt_type;
 | |
| +	struct config_item	item;
 | |
| +};
 | |
| +
 | |
| +static struct uvcg_frame_based_frame *to_uvcg_frame_based_frame(struct config_item *item)
 | |
| +{
 | |
| +	return container_of(item, struct uvcg_frame_based_frame, item);
 | |
| +}
 | |
| +
 | |
| +#define UVCG_FRAME_BASED_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
 | |
| +static ssize_t uvcg_frame_based_frame_##cname##_show(struct config_item *item, char *page)\
 | |
| +{									\
 | |
| +	struct uvcg_frame_based_frame *f = to_uvcg_frame_based_frame(item);			\
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
 | |
| +	int result;							\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname));	\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +									\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return result;							\
 | |
| +}									\
 | |
| +									\
 | |
| +static ssize_t  uvcg_frame_based_frame_##cname##_store(struct config_item *item,	\
 | |
| +					   const char *page, size_t len)\
 | |
| +{									\
 | |
| +	struct uvcg_frame_based_frame *f = to_uvcg_frame_based_frame(item);			\
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct uvcg_format *fmt;					\
 | |
| +	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
 | |
| +	int ret;							\
 | |
| +	u##bits num;							\
 | |
| +									\
 | |
| +	ret = kstrtou##bits(page, 0, &num);				\
 | |
| +	if (ret)							\
 | |
| +		return ret;						\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +	fmt = to_uvcg_format(f->item.ci_parent);			\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	if (fmt->linked || opts->refcnt) {				\
 | |
| +		ret = -EBUSY;						\
 | |
| +		goto end;						\
 | |
| +	}								\
 | |
| +									\
 | |
| +	f->frame.cname = to_little_endian(num);				\
 | |
| +	ret = len;							\
 | |
| +end:									\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return ret;							\
 | |
| +}									\
 | |
| +									\
 | |
| +UVC_ATTR(uvcg_frame_based_frame_, cname, aname);
 | |
| +
 | |
| +#define noop_conversion(x) (x)
 | |
| +
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
 | |
| +		noop_conversion, 8);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
 | |
| +		le32_to_cpu, cpu_to_le32, 32);
 | |
| +UVCG_FRAME_BASED_FRAME_ATTR(dw_bytes_per_line, dwBytesPerLine,
 | |
| +		le32_to_cpu, cpu_to_le32, 32);
 | |
| +
 | |
| +#undef noop_conversion
 | |
| +
 | |
| +#undef UVCG_FRAME_BASED_FRAME_ATTR
 | |
| +
 | |
| +static ssize_t uvcg_frame_based_frame_dw_frame_interval_show(struct config_item *item,
 | |
| +						 char *page)
 | |
| +{
 | |
| +	struct uvcg_frame_based_frame *frm = to_uvcg_frame_based_frame(item);
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +	struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
 | |
| +	int result, i;
 | |
| +	char *pg = page;
 | |
| +
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
 | |
| +
 | |
| +	opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +
 | |
| +	mutex_lock(&opts->lock);
 | |
| +	for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
 | |
| +		result += sprintf(pg, "%d\n",
 | |
| +				  le32_to_cpu(frm->dw_frame_interval[i]));
 | |
| +		pg = page + result;
 | |
| +	}
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +
 | |
| +	mutex_unlock(su_mutex);
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +static ssize_t uvcg_frame_based_frame_dw_frame_interval_store(struct config_item *item,
 | |
| +						  const char *page, size_t len)
 | |
| +{
 | |
| +	struct uvcg_frame_based_frame *ch = to_uvcg_frame_based_frame(item);
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +	struct uvcg_format *fmt;
 | |
| +	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
 | |
| +	int ret = 0, n = 0;
 | |
| +	u32 *frm_intrv, *tmp;
 | |
| +
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
 | |
| +
 | |
| +	opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +	fmt = to_uvcg_format(ch->item.ci_parent);
 | |
| +
 | |
| +	mutex_lock(&opts->lock);
 | |
| +	if (fmt->linked || opts->refcnt) {
 | |
| +		ret = -EBUSY;
 | |
| +		goto end;
 | |
| +	}
 | |
| +
 | |
| +	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
 | |
| +	if (ret)
 | |
| +		goto end;
 | |
| +
 | |
| +	tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
 | |
| +	if (!frm_intrv) {
 | |
| +		ret = -ENOMEM;
 | |
| +		goto end;
 | |
| +	}
 | |
| +
 | |
| +	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
 | |
| +	if (ret) {
 | |
| +		kfree(frm_intrv);
 | |
| +		goto end;
 | |
| +	}
 | |
| +
 | |
| +	kfree(ch->dw_frame_interval);
 | |
| +	ch->dw_frame_interval = frm_intrv;
 | |
| +	ch->frame.b_frame_interval_type = n;
 | |
| +	ret = len;
 | |
| +
 | |
| +end:
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +	mutex_unlock(su_mutex);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +UVC_ATTR(uvcg_frame_based_frame_, dw_frame_interval, dwFrameInterval);
 | |
| +
 | |
| +static struct configfs_attribute *uvcg_frame_based_frame_attrs[] = {
 | |
| +	&uvcg_frame_based_frame_attr_bm_capabilities,
 | |
| +	&uvcg_frame_based_frame_attr_w_width,
 | |
| +	&uvcg_frame_based_frame_attr_w_height,
 | |
| +	&uvcg_frame_based_frame_attr_dw_min_bit_rate,
 | |
| +	&uvcg_frame_based_frame_attr_dw_max_bit_rate,
 | |
| +	&uvcg_frame_based_frame_attr_dw_default_frame_interval,
 | |
| +	&uvcg_frame_based_frame_attr_dw_frame_interval,
 | |
| +	&uvcg_frame_based_frame_attr_dw_bytes_per_line,
 | |
| +	NULL,
 | |
| +};
 | |
| +
 | |
| +static struct config_item_type uvcg_frame_based_frame_type = {
 | |
| +	.ct_attrs	= uvcg_frame_based_frame_attrs,
 | |
| +	.ct_owner	= THIS_MODULE,
 | |
| +};
 | |
| +
 | |
| +static struct config_item *uvcg_frame_based_frame_make(struct config_group *group,
 | |
| +					   const char *name)
 | |
| +{
 | |
| +	struct uvcg_frame_based_frame *h;
 | |
| +	struct uvcg_format *fmt;
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +
 | |
| +	h = kzalloc(sizeof(*h), GFP_KERNEL);
 | |
| +	if (!h)
 | |
| +		return ERR_PTR(-ENOMEM);
 | |
| +
 | |
| +	h->frame.b_descriptor_type		= USB_DT_CS_INTERFACE;
 | |
| +	h->frame.b_frame_index			= 1;
 | |
| +	h->frame.w_width			= cpu_to_le16(640);
 | |
| +	h->frame.w_height			= cpu_to_le16(360);
 | |
| +	h->frame.dw_min_bit_rate		= cpu_to_le32(18432000);
 | |
| +	h->frame.dw_max_bit_rate		= cpu_to_le32(55296000);
 | |
| +	h->frame.dw_default_frame_interval	= cpu_to_le32(333333);
 | |
| +	h->frame.dw_bytes_per_line	= cpu_to_le32(0);
 | |
| +
 | |
| +	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +
 | |
| +	mutex_lock(&opts->lock);
 | |
| +	fmt = to_uvcg_format(&group->cg_item);
 | |
| +	if (fmt->type == UVCG_FRAME_FRAME_BASED) {
 | |
| +		h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED;
 | |
| +		h->fmt_type = UVCG_FRAME_FRAME_BASED;
 | |
| +	} else {
 | |
| +		mutex_unlock(&opts->lock);
 | |
| +		kfree(h);
 | |
| +		return ERR_PTR(-EINVAL);
 | |
| +	}
 | |
| +	++fmt->num_frames;
 | |
| +	h->frame.b_frame_index	= fmt->num_frames;
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +
 | |
| +	config_item_init_type_name(&h->item, name, &uvcg_frame_based_frame_type);
 | |
| +
 | |
| +	return &h->item;
 | |
| +}
 | |
| +
 | |
| +static void uvcg_frame_based_frame_drop(struct config_group *group, struct config_item *item)
 | |
| +{
 | |
| +	struct uvcg_frame_based_frame *h = to_uvcg_frame_based_frame(item);
 | |
| +	struct uvcg_format *fmt;
 | |
| +	struct f_uvc_opts *opts;
 | |
| +	struct config_item *opts_item;
 | |
| +
 | |
| +	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
 | |
| +	opts = to_f_uvc_opts(opts_item);
 | |
| +
 | |
| +	mutex_lock(&opts->lock);
 | |
| +	fmt = to_uvcg_format(&group->cg_item);
 | |
| +	--fmt->num_frames;
 | |
| +	kfree(h);
 | |
| +	mutex_unlock(&opts->lock);
 | |
| +}
 | |
| +
 | |
|  /* streaming/uncompressed/<NAME> */
 | |
|  struct uvcg_uncompressed {
 | |
|  	struct uvcg_format		fmt;
 | |
| @@ -1438,10 +1892,17 @@
 | |
|  static struct config_group *uvcg_uncompressed_make(struct config_group *group,
 | |
|  						   const char *name)
 | |
|  {
 | |
| +#ifndef CONFIG_GOKE_MC
 | |
|  	static char guid[] = {
 | |
|  		'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
 | |
|  		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
 | |
|  	};
 | |
| +#else
 | |
| +	static char guid[] = {
 | |
| +		'N',  'V',  '2',  '1', 0x00, 0x00, 0x10, 0x00,
 | |
| +		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
 | |
| +	};
 | |
| +#endif
 | |
|  	struct uvcg_uncompressed *h;
 | |
|  
 | |
|  	h = kzalloc(sizeof(*h), GFP_KERNEL);
 | |
| @@ -1452,7 +1913,11 @@
 | |
|  	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
 | |
|  	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_UNCOMPRESSED;
 | |
|  	memcpy(h->desc.guidFormat, guid, sizeof(guid));
 | |
| +#ifndef CONFIG_GOKE_MC
 | |
|  	h->desc.bBitsPerPixel		= 16;
 | |
| +#else
 | |
| +	h->desc.bBitsPerPixel		= 12;
 | |
| +#endif
 | |
|  	h->desc.bDefaultFrameIndex	= 1;
 | |
|  	h->desc.bAspectRatioX		= 0;
 | |
|  	h->desc.bAspectRatioY		= 0;
 | |
| @@ -1678,6 +2143,205 @@
 | |
|  	.ct_owner	= THIS_MODULE,
 | |
|  };
 | |
|  
 | |
| +/* streaming/frame_based<NAME> */
 | |
| +struct uvcg_frame_based_format {
 | |
| +	struct uvcg_format		fmt;
 | |
| +	struct uvc_frame_based_format_desc		desc;
 | |
| +};
 | |
| +
 | |
| +static struct uvcg_frame_based_format *to_uvcg_frame_based_format(struct config_item *item)
 | |
| +{
 | |
| +	return container_of(
 | |
| +		container_of(to_config_group(item), struct uvcg_format, group),
 | |
| +		struct uvcg_frame_based_format, fmt);
 | |
| +}
 | |
| +
 | |
| +static struct configfs_group_operations uvcg_frame_based_format_group_ops = {
 | |
| +	.make_item		= uvcg_frame_based_frame_make,
 | |
| +	.drop_item		= uvcg_frame_based_frame_drop,
 | |
| +};
 | |
| +
 | |
| +#define UVCG_FRAME_BASED_FORMAT_ATTR_RO(cname, aname, conv)				\
 | |
| +static ssize_t uvcg_frame_based_format_##cname##_show(struct config_item *item, char *page)\
 | |
| +{									\
 | |
| +	struct uvcg_frame_based_format *u = to_uvcg_frame_based_format(item);			\
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
 | |
| +	int result;							\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +									\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return result;							\
 | |
| +}									\
 | |
| +									\
 | |
| +UVC_ATTR_RO(uvcg_frame_based_format_, cname, aname)
 | |
| +
 | |
| +#define UVCG_FRAME_BASED_FORMAT_ATTR(cname, aname, conv)				\
 | |
| +static ssize_t uvcg_frame_based_format_##cname##_show(struct config_item *item, char *page)\
 | |
| +{									\
 | |
| +	struct uvcg_frame_based_format *u = to_uvcg_frame_based_format(item);			\
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
 | |
| +	int result;							\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +									\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return result;							\
 | |
| +}									\
 | |
| +									\
 | |
| +static ssize_t								\
 | |
| +uvcg_frame_based_format_##cname##_store(struct config_item *item,			\
 | |
| +			   const char *page, size_t len)		\
 | |
| +{									\
 | |
| +	struct uvcg_frame_based_format *u = to_uvcg_frame_based_format(item);			\
 | |
| +	struct f_uvc_opts *opts;					\
 | |
| +	struct config_item *opts_item;					\
 | |
| +	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
 | |
| +	int ret;							\
 | |
| +	u8 num;								\
 | |
| +									\
 | |
| +	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
 | |
| +									\
 | |
| +	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
 | |
| +	opts = to_f_uvc_opts(opts_item);				\
 | |
| +									\
 | |
| +	mutex_lock(&opts->lock);					\
 | |
| +	if (u->fmt.linked || opts->refcnt) {				\
 | |
| +		ret = -EBUSY;						\
 | |
| +		goto end;						\
 | |
| +	}								\
 | |
| +									\
 | |
| +	ret = kstrtou8(page, 0, &num);					\
 | |
| +	if (ret)							\
 | |
| +		goto end;						\
 | |
| +									\
 | |
| +	if (num > 255) {						\
 | |
| +		ret = -EINVAL;						\
 | |
| +		goto end;						\
 | |
| +	}								\
 | |
| +	u->desc.aname = num;						\
 | |
| +	ret = len;							\
 | |
| +end:									\
 | |
| +	mutex_unlock(&opts->lock);					\
 | |
| +	mutex_unlock(su_mutex);						\
 | |
| +	return ret;							\
 | |
| +}									\
 | |
| +									\
 | |
| +UVC_ATTR(uvcg_frame_based_format_, cname, aname)
 | |
| +
 | |
| +#define identity_conv(x) (x)
 | |
| +
 | |
| +UVCG_FRAME_BASED_FORMAT_ATTR(b_default_frame_index, bDefaultFrameIndex,
 | |
| +		       identity_conv);
 | |
| +UVCG_FRAME_BASED_FORMAT_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
 | |
| +UVCG_FRAME_BASED_FORMAT_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
 | |
| +UVCG_FRAME_BASED_FORMAT_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
 | |
| +
 | |
| +#undef identity_conv
 | |
| +
 | |
| +#undef UVCG_FRAME_BASED_FORMAT_ATTR
 | |
| +#undef UVCG_FRAME_BASED_FORMAT_ATTR_RO
 | |
| +
 | |
| +static inline ssize_t
 | |
| +uvcg_frame_based_format_bma_controls_show(struct config_item *item, char *page)
 | |
| +{
 | |
| +	struct uvcg_frame_based_format *u = to_uvcg_frame_based_format(item);
 | |
| +	return uvcg_format_bma_controls_show(&u->fmt, page);
 | |
| +}
 | |
| +
 | |
| +static inline ssize_t
 | |
| +uvcg_frame_based_format_bma_controls_store(struct config_item *item,
 | |
| +				     const char *page, size_t len)
 | |
| +{
 | |
| +	struct uvcg_frame_based_format *u = to_uvcg_frame_based_format(item);
 | |
| +	return uvcg_format_bma_controls_store(&u->fmt, page, len);
 | |
| +}
 | |
| +
 | |
| +UVC_ATTR(uvcg_frame_based_format_, bma_controls, bmaControls);
 | |
| +
 | |
| +static struct configfs_attribute *uvcg_frame_based_format_attrs[] = {
 | |
| +	&uvcg_frame_based_format_attr_b_default_frame_index,
 | |
| +	&uvcg_frame_based_format_attr_b_aspect_ratio_x,
 | |
| +	&uvcg_frame_based_format_attr_b_aspect_ratio_y,
 | |
| +	&uvcg_frame_based_format_attr_bm_interface_flags,
 | |
| +	&uvcg_frame_based_format_attr_bma_controls,
 | |
| +	NULL,
 | |
| +};
 | |
| +
 | |
| +static struct config_item_type uvcg_frame_based_format_type = {
 | |
| +	.ct_group_ops	= &uvcg_frame_based_format_group_ops,
 | |
| +	.ct_attrs	= uvcg_frame_based_format_attrs,
 | |
| +	.ct_owner	= THIS_MODULE,
 | |
| +};
 | |
| +
 | |
| +static struct config_group *uvcg_frame_based_format_make(struct config_group *group,
 | |
| +						   const char *name)
 | |
| +{
 | |
| +	static char guid[] = { /*Declear frame frame based as H264*/
 | |
| +		'H',  '2',  '6',  '4', 0x00, 0x00, 0x10, 0x00,
 | |
| +		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
 | |
| +	};
 | |
| +	struct uvcg_frame_based_format *h;
 | |
| +
 | |
| +	h = kzalloc(sizeof(*h), GFP_KERNEL);
 | |
| +	if (!h)
 | |
| +		return ERR_PTR(-ENOMEM);
 | |
| +
 | |
| +	h->desc.bLength			= UVC_DT_FRAME_BASED_FORMAT_SIZE;
 | |
| +	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
 | |
| +	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_FRAME_BASED;
 | |
| +	memcpy(h->desc.guidFormat, guid, sizeof(guid));
 | |
| +	h->desc.bBitsPerPixel		= 16;
 | |
| +	h->desc.bDefaultFrameIndex	= 1;
 | |
| +	h->desc.bAspectRatioX	= 0;
 | |
| +	h->desc.bAspectRatioY	= 0;
 | |
| +	h->desc.bmInterfaceFlags	= 0;
 | |
| +	h->desc.bCopyProtect		= 0;
 | |
| +	h->desc.bVariableSize		= 1;
 | |
| +
 | |
| +	h->fmt.type = UVCG_FRAME_FRAME_BASED;
 | |
| +	config_group_init_type_name(&h->fmt.group, name,
 | |
| +				    &uvcg_frame_based_format_type);
 | |
| +
 | |
| +	return &h->fmt.group;
 | |
| +}
 | |
| +
 | |
| +static void uvcg_frame_based_format_drop(struct config_group *group,
 | |
| +			    struct config_item *item)
 | |
| +{
 | |
| +	struct uvcg_frame_based_format *h = to_uvcg_frame_based_format(item);
 | |
| +
 | |
| +	kfree(h);
 | |
| +}
 | |
| +
 | |
| +static struct configfs_group_operations uvcg_frame_based_format_grp_ops = {
 | |
| +	.make_group		= uvcg_frame_based_format_make,
 | |
| +	.drop_item		= uvcg_frame_based_format_drop,
 | |
| +};
 | |
| +
 | |
| +static struct config_item_type uvcg_frame_based_format_grp_type = {
 | |
| +	.ct_group_ops	= &uvcg_frame_based_format_grp_ops,
 | |
| +	.ct_owner	= THIS_MODULE,
 | |
| +};
 | |
| +
 | |
|  /* streaming/color_matching/default */
 | |
|  static struct uvcg_default_color_matching {
 | |
|  	struct config_group	group;
 | |
| @@ -1873,6 +2537,11 @@
 | |
|  				container_of(fmt, struct uvcg_mjpeg, fmt);
 | |
|  
 | |
|  			*size += sizeof(m->desc);
 | |
| +		} else if (fmt->type == UVCG_FRAME_FRAME_BASED) {
 | |
| +			struct uvcg_frame_based_format *h =
 | |
| +				container_of(fmt, struct uvcg_frame_based_format, fmt);
 | |
| +
 | |
| +			*size += sizeof(h->desc);
 | |
|  		} else {
 | |
|  			return -EINVAL;
 | |
|  		}
 | |
| @@ -1881,7 +2550,14 @@
 | |
|  	case UVCG_FRAME: {
 | |
|  		struct uvcg_frame *frm = priv1;
 | |
|  		int sz = sizeof(frm->dw_frame_interval);
 | |
| +		if (frm->frame.b_descriptor_subtype == UVC_VS_FRAME_FRAME_BASED) {
 | |
| +			struct uvcg_frame_based_frame *fb_frm = priv1;
 | |
| +			*size += sizeof(fb_frm->frame);
 | |
| +			*size += fb_frm->frame.b_frame_interval_type * sizeof(fb_frm->dw_frame_interval);
 | |
|  
 | |
| +			++*count;
 | |
| +			return 0;
 | |
| +		}
 | |
|  		*size += sizeof(frm->frame);
 | |
|  		*size += frm->frame.b_frame_interval_type * sz;
 | |
|  	}
 | |
| @@ -1949,6 +2625,15 @@
 | |
|  			*dest += sizeof(m->desc);
 | |
|  			mjp->bNumFrameDescriptors = fmt->num_frames;
 | |
|  			mjp->bFormatIndex = n + 1;
 | |
| +		} else if (fmt->type == UVCG_FRAME_FRAME_BASED) {
 | |
| +			struct uvc_frame_based_format_desc *ffb = *dest;
 | |
| +			struct uvcg_frame_based_format *h =
 | |
| +				container_of(fmt, struct uvcg_frame_based_format, fmt);
 | |
| +
 | |
| +			memcpy(*dest, &h->desc, sizeof(h->desc));
 | |
| +			*dest += sizeof(h->desc);
 | |
| +			ffb->bNumFrameDescriptors = fmt->num_frames;
 | |
| +			ffb->bFormatIndex = n + 1;
 | |
|  		} else {
 | |
|  			return -EINVAL;
 | |
|  		}
 | |
| @@ -1958,6 +2643,19 @@
 | |
|  		struct uvcg_frame *frm = priv1;
 | |
|  		struct uvc_descriptor_header *h = *dest;
 | |
|  
 | |
| +		if (frm->frame.b_descriptor_subtype == UVC_VS_FRAME_FRAME_BASED) {
 | |
| +			struct uvcg_frame_based_frame *fb_frm = priv1;
 | |
| +			sz = sizeof(fb_frm->frame);
 | |
| +			memcpy(*dest, &fb_frm->frame, sz);
 | |
| +			*dest += sz;
 | |
| +			sz = fb_frm->frame.b_frame_interval_type *
 | |
| +			sizeof(*fb_frm->dw_frame_interval);
 | |
| +			memcpy(*dest, fb_frm->dw_frame_interval, sz);
 | |
| +			*dest += sz;
 | |
| +			h->bLength = UVC_DT_FRAME_BASED_FRAME_SIZE(
 | |
| +				fb_frm->frame.b_frame_interval_type);
 | |
| +			return 0;
 | |
| +		}
 | |
|  		sz = sizeof(frm->frame);
 | |
|  		memcpy(*dest, &frm->frame, sz);
 | |
|  		*dest += sz;
 | |
| @@ -2224,6 +2922,13 @@
 | |
|  	configfs_add_default_group(&uvcg_default_processing.group,
 | |
|  			&uvcg_processing_grp.group);
 | |
|  
 | |
| +	config_group_init_type_name(&uvcg_default_extension.group,
 | |
| +					"default", &uvcg_default_extension_type);
 | |
| +	config_group_init_type_name(&uvcg_extension_grp.group,
 | |
| +					"extension", &uvcg_extension_grp_type);
 | |
| +	configfs_add_default_group(&uvcg_default_extension.group,
 | |
| +					&uvcg_extension_grp.group);
 | |
| +
 | |
|  	config_group_init_type_name(&uvcg_default_camera.group,
 | |
|  			"default", &uvcg_default_camera_type);
 | |
|  	config_group_init_type_name(&uvcg_camera_grp.group,
 | |
| @@ -2278,6 +2983,9 @@
 | |
|  	config_group_init_type_name(&uvcg_mjpeg_grp.group,
 | |
|  				    "mjpeg",
 | |
|  				    &uvcg_mjpeg_grp_type);
 | |
| +	config_group_init_type_name(&uvcg_frame_based_format_grp.group,
 | |
| +				    "framebased",
 | |
| +				    &uvcg_frame_based_format_grp_type);
 | |
|  	config_group_init_type_name(&uvcg_default_color_matching.group,
 | |
|  				    "default",
 | |
|  				    &uvcg_default_color_matching_type);
 | |
| @@ -2310,6 +3018,8 @@
 | |
|  			&uvcg_streaming_grp.group);
 | |
|  	configfs_add_default_group(&uvcg_mjpeg_grp.group,
 | |
|  			&uvcg_streaming_grp.group);
 | |
| +	configfs_add_default_group(&uvcg_frame_based_format_grp.group,
 | |
| +			&uvcg_streaming_grp.group);
 | |
|  	configfs_add_default_group(&uvcg_color_matching_grp.group,
 | |
|  			&uvcg_streaming_grp.group);
 | |
|  	configfs_add_default_group(&uvcg_streaming_class_grp.group,
 |