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,
|