--- 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/ */ 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 */ +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,