/*
* uvc_configfs.c
*
* Configfs support for the uvc function.
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "u_uvc.h"
#include "uvc_configfs.h"
#define UVCG_STREAMING_CONTROL_SIZE 1
#define UVC_ATTR(prefix, cname, aname) \
static struct configfs_attribute prefix##attr_##cname = { \
.ca_name = __stringify(aname), \
.ca_mode = S_IRUGO | S_IWUGO, \
.ca_owner = THIS_MODULE, \
.show = prefix##cname##_show, \
.store = prefix##cname##_store, \
}
#define UVC_ATTR_RO(prefix, cname, aname) \
static struct configfs_attribute prefix##attr_##cname = { \
.ca_name = __stringify(aname), \
.ca_mode = S_IRUGO, \
.ca_owner = THIS_MODULE, \
.show = prefix##cname##_show, \
}
static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
/* control/header/<NAME> */
DECLARE_UVC_HEADER_DESCRIPTOR(1);
struct uvcg_control_header {
struct config_item item;
struct UVC_HEADER_DESCRIPTOR(1) desc;
unsigned linked;
};
static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
{
return container_of(item, struct uvcg_control_header, item);
}
#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
static ssize_t uvcg_control_header_##cname##_show( \
struct config_item *item, char *page) \
{ \
struct uvcg_control_header *ch = to_uvcg_control_header(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
int result; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%d\n", conv(ch->desc.aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
return result; \
} \
\
static ssize_t \
uvcg_control_header_##cname##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct uvcg_control_header *ch = to_uvcg_control_header(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
int ret; \
uxx num; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
if (ch->linked || opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = str2u(page, 0, &num); \
if (ret) \
goto end; \
\
if (num > limit) { \
ret = -EINVAL; \
goto end; \
} \
ch->desc.aname = vnoc(num); \
ret = len; \
end: \
mutex_unlock(&opts->lock); \
mutex_unlock(su_mutex); \
return ret; \
} \
\
UVC_ATTR(uvcg_control_header_, cname, aname)
UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
0xffff);
UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
u32, cpu_to_le32, 0x7fffffff);
#undef UVCG_CTRL_HDR_ATTR
static struct configfs_attribute *uvcg_control_header_attrs[] = {
&uvcg_control_header_attr_bcd_uvc,
&uvcg_control_header_attr_dw_clock_frequency,
NULL,
};
static struct config_item_type uvcg_control_header_type = {
.ct_attrs = uvcg_control_header_attrs,
.ct_owner = THIS_MODULE,
};
static struct config_item *uvcg_control_header_make(struct config_group *group,
const char *name)
{
struct uvcg_control_header *h;
h = kzalloc(sizeof(*h), GFP_KERNEL);
if (!h)
return ERR_PTR(-ENOMEM);
h->desc.bLength = UVC_DT_HEADER_SIZE(1);
h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
h->desc.bDescriptorSubType = UVC_VC_HEADER;
h->desc.bcdUVC = cpu_to_le16(0x0100);
h->desc.dwClockFrequency = cpu_to_le32(48000000);
config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
return &h->item;
}
static void uvcg_control_header_drop(struct config_group *group,
struct config_item *item)
{
struct uvcg_control_header *h = to_uvcg_control_header(item);
kfree(h);
}
/* control/header */
static struct uvcg_control_header_grp {
struct config_group group;
} uvcg_control_header_grp;
static struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
.drop_item = uvcg_control_header_drop,
};
static struct config_item_type uvcg_control_header_grp_type = {
.ct_group_ops = &uvcg_control_header_grp_ops,
.ct_owner = THIS_MODULE,
};
/* control/processing/default */
static struct uvcg_default_processing {
struct config_group group;
} uvcg_default_processing;
static inline struct uvcg_default_processing
*to_uvcg_default_processing(struct config_item *item)
{
return container_of(to_config_group(item),
struct uvcg_default_processing, group);
}
#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \
static ssize_t uvcg_default_processing_##cname##_show( \
struct config_item *item, char *page) \
{ \
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 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); \
pd = &opts->uvc_processing; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%d\n", conv(pd->aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
return result; \
} \
\
UVC_ATTR_RO(uvcg_default_processing_, cname, aname)
#define identity_conv(x) (x)
UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
#undef identity_conv
#undef UVCG_DEFAULT_PROCESSING_ATTR
static ssize_t uvcg_default_processing_bm_controls_show(
struct config_item *item, char *page)
{
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 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);
pd = &opts->uvc_processing;
mutex_lock(&opts->lock);
for (result = 0, i = 0; i < pd->bControlSize; ++i) {
result += sprintf(pg, "%d\n", pd->bmControls[i]);
pg = page + result;
}
mutex_unlock(&opts->lock);
mutex_unlock(su_mutex);
return result;
}
UVC_ATTR_RO(uvcg_default_processing_, bm_controls, bmControls);
static struct configfs_attribute *uvcg_default_processing_attrs[] = {
&uvcg_default_processing_attr_b_unit_id,
&uvcg_default_processing_attr_b_source_id,
&uvcg_default_processing_attr_w_max_multiplier,
&uvcg_default_processing_attr_bm_controls,
&uvcg_default_processing_attr_i_processing,
NULL,
};
static struct config_item_type uvcg_default_processing_type = {
.ct_attrs = uvcg_default_processing_attrs,
.ct_owner = THIS_MODULE,
};
/* struct uvcg_processing {}; */
/* control/processi