aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/v4l2-core/v4l2-ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-ioctl.c')
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c219
1 files changed, 141 insertions, 78 deletions
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index aa6e7c788db..f81bda1a48e 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -35,6 +35,8 @@
memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+#define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
+
struct std_descr {
v4l2_std_id std;
const char *descr;
@@ -167,9 +169,11 @@ static void v4l_print_querycap(const void *arg, bool write_only)
{
const struct v4l2_capability *p = arg;
- pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
+ pr_cont("driver=%.*s, card=%.*s, bus=%.*s, version=0x%08x, "
"capabilities=0x%08x, device_caps=0x%08x\n",
- p->driver, p->card, p->bus_info,
+ (int)sizeof(p->driver), p->driver,
+ (int)sizeof(p->card), p->card,
+ (int)sizeof(p->bus_info), p->bus_info,
p->version, p->capabilities, p->device_caps);
}
@@ -177,20 +181,21 @@ static void v4l_print_enuminput(const void *arg, bool write_only)
{
const struct v4l2_input *p = arg;
- pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
+ pr_cont("index=%u, name=%.*s, type=%u, audioset=0x%x, tuner=%u, "
"std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
- p->index, p->name, p->type, p->audioset, p->tuner,
- (unsigned long long)p->std, p->status, p->capabilities);
+ p->index, (int)sizeof(p->name), p->name, p->type, p->audioset,
+ p->tuner, (unsigned long long)p->std, p->status,
+ p->capabilities);
}
static void v4l_print_enumoutput(const void *arg, bool write_only)
{
const struct v4l2_output *p = arg;
- pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
+ pr_cont("index=%u, name=%.*s, type=%u, audioset=0x%x, "
"modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
- p->index, p->name, p->type, p->audioset, p->modulator,
- (unsigned long long)p->std, p->capabilities);
+ p->index, (int)sizeof(p->name), p->name, p->type, p->audioset,
+ p->modulator, (unsigned long long)p->std, p->capabilities);
}
static void v4l_print_audio(const void *arg, bool write_only)
@@ -200,8 +205,9 @@ static void v4l_print_audio(const void *arg, bool write_only)
if (write_only)
pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
else
- pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
- p->index, p->name, p->capability, p->mode);
+ pr_cont("index=%u, name=%.*s, capability=0x%x, mode=0x%x\n",
+ p->index, (int)sizeof(p->name), p->name,
+ p->capability, p->mode);
}
static void v4l_print_audioout(const void *arg, bool write_only)
@@ -211,21 +217,22 @@ static void v4l_print_audioout(const void *arg, bool write_only)
if (write_only)
pr_cont("index=%u\n", p->index);
else
- pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
- p->index, p->name, p->capability, p->mode);
+ pr_cont("index=%u, name=%.*s, capability=0x%x, mode=0x%x\n",
+ p->index, (int)sizeof(p->name), p->name,
+ p->capability, p->mode);
}
static void v4l_print_fmtdesc(const void *arg, bool write_only)
{
const struct v4l2_fmtdesc *p = arg;
- pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
+ pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%.*s'\n",
p->index, prt_names(p->type, v4l2_type_names),
p->flags, (p->pixelformat & 0xff),
(p->pixelformat >> 8) & 0xff,
(p->pixelformat >> 16) & 0xff,
(p->pixelformat >> 24) & 0xff,
- p->description);
+ (int)sizeof(p->description), p->description);
}
static void v4l_print_format(const void *arg, bool write_only)
@@ -348,9 +355,9 @@ static void v4l_print_modulator(const void *arg, bool write_only)
if (write_only)
pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
else
- pr_cont("index=%u, name=%s, capability=0x%x, "
+ pr_cont("index=%u, name=%.*s, capability=0x%x, "
"rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
- p->index, p->name, p->capability,
+ p->index, (int)sizeof(p->name), p->name, p->capability,
p->rangelow, p->rangehigh, p->txsubchans);
}
@@ -361,10 +368,10 @@ static void v4l_print_tuner(const void *arg, bool write_only)
if (write_only)
pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
else
- pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
+ pr_cont("index=%u, name=%.*s, type=%u, capability=0x%x, "
"rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
"rxsubchans=0x%x, audmode=%u\n",
- p->index, p->name, p->type,
+ p->index, (int)sizeof(p->name), p->name, p->type,
p->capability, p->rangelow,
p->rangehigh, p->signal, p->afc,
p->rxsubchans, p->audmode);
@@ -382,9 +389,9 @@ static void v4l_print_standard(const void *arg, bool write_only)
{
const struct v4l2_standard *p = arg;
- pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
+ pr_cont("index=%u, id=0x%Lx, name=%.*s, fps=%u/%u, "
"framelines=%u\n", p->index,
- (unsigned long long)p->id, p->name,
+ (unsigned long long)p->id, (int)sizeof(p->name), p->name,
p->frameperiod.numerator,
p->frameperiod.denominator,
p->framelines);
@@ -504,9 +511,9 @@ static void v4l_print_queryctrl(const void *arg, bool write_only)
{
const struct v4l2_queryctrl *p = arg;
- pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+ pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%d/%d, "
"step=%d, default=%d, flags=0x%08x\n",
- p->id, p->type, p->name,
+ p->id, p->type, (int)sizeof(p->name), p->name,
p->minimum, p->maximum,
p->step, p->default_value, p->flags);
}
@@ -623,39 +630,39 @@ static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
pr_cont("type=%u, ", p->match.type);
if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%s, ", p->match.name);
+ pr_cont("name=%.*s, ",
+ (int)sizeof(p->match.name), p->match.name);
else
pr_cont("addr=%u, ", p->match.addr);
pr_cont("chip_ident=%u, revision=0x%x\n",
p->ident, p->revision);
}
-static void v4l_print_dbg_register(const void *arg, bool write_only)
+static void v4l_print_dbg_chip_info(const void *arg, bool write_only)
{
- const struct v4l2_dbg_register *p = arg;
+ const struct v4l2_dbg_chip_info *p = arg;
pr_cont("type=%u, ", p->match.type);
if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%s, ", p->match.name);
+ pr_cont("name=%.*s, ",
+ (int)sizeof(p->match.name), p->match.name);
else
pr_cont("addr=%u, ", p->match.addr);
- pr_cont("reg=0x%llx, val=0x%llx\n",
- p->reg, p->val);
+ pr_cont("name=%.*s\n", (int)sizeof(p->name), p->name);
}
-static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
-{
- const struct v4l2_dv_enum_preset *p = arg;
-
- pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
- p->index, p->preset, p->name, p->width, p->height);
-}
-
-static void v4l_print_dv_preset(const void *arg, bool write_only)
+static void v4l_print_dbg_register(const void *arg, bool write_only)
{
- const struct v4l2_dv_preset *p = arg;
+ const struct v4l2_dbg_register *p = arg;
- pr_cont("preset=%u\n", p->preset);
+ pr_cont("type=%u, ", p->match.type);
+ if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ pr_cont("name=%.*s, ",
+ (int)sizeof(p->match.name), p->match.name);
+ else
+ pr_cont("addr=%u, ", p->match.addr);
+ pr_cont("reg=0x%llx, val=0x%llx\n",
+ p->reg, p->val);
}
static void v4l_print_dv_timings(const void *arg, bool write_only)
@@ -997,20 +1004,17 @@ static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_input *p = arg;
/*
- * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS &
+ * We set the flags for CAP_DV_TIMINGS &
* CAP_STD here based on ioctl handler provided by the
* driver. If the driver doesn't support these
* for a specific input, it must override these flags.
*/
- if (ops->vidioc_s_std)
+ if (is_valid_ioctl(vfd, VIDIOC_S_STD))
p->capabilities |= V4L2_IN_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_IN_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_IN_CAP_DV_TIMINGS;
return ops->vidioc_enum_input(file, fh, p);
}
@@ -1018,20 +1022,17 @@ static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
+ struct video_device *vfd = video_devdata(file);
struct v4l2_output *p = arg;
/*
- * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS &
+ * We set the flags for CAP_DV_TIMINGS &
* CAP_STD here based on ioctl handler provided by the
* driver. If the driver doesn't support these
* for a specific output, it must override these flags.
*/
- if (ops->vidioc_s_std)
+ if (is_valid_ioctl(vfd, VIDIOC_S_STD))
p->capabilities |= V4L2_OUT_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_OUT_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_OUT_CAP_DV_TIMINGS;
return ops->vidioc_enum_output(file, fh, p);
}
@@ -1316,7 +1317,7 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct video_device *vfd = video_devdata(file);
- struct v4l2_frequency *p = arg;
+ const struct v4l2_frequency *p = arg;
enum v4l2_tuner_type type;
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
@@ -1383,15 +1384,15 @@ static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct video_device *vfd = video_devdata(file);
- v4l2_std_id *id = arg, norm;
+ v4l2_std_id id = *(v4l2_std_id *)arg, norm;
int ret;
- norm = (*id) & vfd->tvnorms;
+ norm = id & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
return -EINVAL;
/* Calls the specific handler */
- ret = ops->vidioc_s_std(file, fh, &norm);
+ ret = ops->vidioc_s_std(file, fh, norm);
/* Updates standard information */
if (ret >= 0)
@@ -1513,7 +1514,7 @@ static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
p->parm.capture.readbuffers = 2;
- if (ops->vidioc_g_std)
+ if (is_valid_ioctl(vfd, VIDIOC_G_STD) && ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, &std);
if (ret == 0)
v4l2_video_std_frame_period(std,
@@ -1792,10 +1793,23 @@ static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
{
#ifdef CONFIG_VIDEO_ADV_DEBUG
struct v4l2_dbg_register *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_subdev *sd;
+ int idx = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- return ops->vidioc_g_register(file, fh, p);
+ if (p->match.type == V4L2_CHIP_MATCH_SUBDEV) {
+ if (vfd->v4l2_dev == NULL)
+ return -EINVAL;
+ v4l2_device_for_each_subdev(sd, vfd->v4l2_dev)
+ if (p->match.addr == idx++)
+ return v4l2_subdev_call(sd, core, g_register, p);
+ return -EINVAL;
+ }
+ if (ops->vidioc_g_register)
+ return ops->vidioc_g_register(file, fh, p);
+ return -EINVAL;
#else
return -ENOTTY;
#endif
@@ -1805,11 +1819,24 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register *p = arg;
+ const struct v4l2_dbg_register *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_subdev *sd;
+ int idx = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- return ops->vidioc_s_register(file, fh, p);
+ if (p->match.type == V4L2_CHIP_MATCH_SUBDEV) {
+ if (vfd->v4l2_dev == NULL)
+ return -EINVAL;
+ v4l2_device_for_each_subdev(sd, vfd->v4l2_dev)
+ if (p->match.addr == idx++)
+ return v4l2_subdev_call(sd, core, s_register, p);
+ return -EINVAL;
+ }
+ if (ops->vidioc_s_register)
+ return ops->vidioc_s_register(file, fh, p);
+ return -EINVAL;
#else
return -ENOTTY;
#endif
@@ -1822,9 +1849,59 @@ static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
p->ident = V4L2_IDENT_NONE;
p->revision = 0;
+ if (p->match.type == V4L2_CHIP_MATCH_SUBDEV)
+ return -EINVAL;
return ops->vidioc_g_chip_ident(file, fh, p);
}
+static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_dbg_chip_info *p = arg;
+ struct v4l2_subdev *sd;
+ int idx = 0;
+
+ switch (p->match.type) {
+ case V4L2_CHIP_MATCH_BRIDGE:
+ if (ops->vidioc_s_register)
+ p->flags |= V4L2_CHIP_FL_WRITABLE;
+ if (ops->vidioc_g_register)
+ p->flags |= V4L2_CHIP_FL_READABLE;
+ if (vfd->v4l2_dev)
+ strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
+ else if (vfd->parent)
+ strlcpy(p->name, vfd->parent->driver->name, sizeof(p->name));
+ else
+ strlcpy(p->name, "bridge", sizeof(p->name));
+ if (ops->vidioc_g_chip_info)
+ return ops->vidioc_g_chip_info(file, fh, arg);
+ if (p->match.addr)
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CHIP_MATCH_SUBDEV:
+ if (vfd->v4l2_dev == NULL)
+ break;
+ v4l2_device_for_each_subdev(sd, vfd->v4l2_dev) {
+ if (p->match.addr != idx++)
+ continue;
+ if (sd->ops->core && sd->ops->core->s_register)
+ p->flags |= V4L2_CHIP_FL_WRITABLE;
+ if (sd->ops->core && sd->ops->core->g_register)
+ p->flags |= V4L2_CHIP_FL_READABLE;
+ strlcpy(p->name, sd->name, sizeof(p->name));
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+#else
+ return -ENOTTY;
+#endif
+}
+
static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
@@ -1873,7 +1950,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
return -EINVAL;
if (ops->vidioc_enum_freq_bands)
return ops->vidioc_enum_freq_bands(file, fh, p);
- if (ops->vidioc_g_tuner) {
+ if (is_valid_ioctl(vfd, VIDIOC_G_TUNER)) {
struct v4l2_tuner t = {
.index = p->tuner,
.type = type,
@@ -1891,7 +1968,7 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
return 0;
}
- if (ops->vidioc_g_modulator) {
+ if (is_valid_ioctl(vfd, VIDIOC_G_MODULATOR)) {
struct v4l2_modulator m = {
.index = p->tuner,
};
@@ -2028,10 +2105,6 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
- IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
- IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
@@ -2043,6 +2116,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -2147,11 +2221,6 @@ static long __video_do_ioctl(struct file *file,
}
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
- if (write_only && debug > V4L2_DEBUG_IOCTL) {
- v4l_printk_ioctl(video_device_node_name(vfd), cmd);
- pr_cont(": ");
- info->debug(arg, write_only);
- }
if (info->flags & INFO_FL_STD) {
typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
const void *p = vfd->ioctl_ops;
@@ -2170,16 +2239,10 @@ static long __video_do_ioctl(struct file *file,
done:
if (debug) {
- if (write_only && debug > V4L2_DEBUG_IOCTL) {
- if (ret < 0)
- printk(KERN_DEBUG "%s: error %ld\n",
- video_device_node_name(vfd), ret);
- return ret;
- }
v4l_printk_ioctl(video_device_node_name(vfd), cmd);
if (ret < 0)
- pr_cont(": error %ld\n", ret);
- else if (debug == V4L2_DEBUG_IOCTL)
+ pr_cont(": error %ld", ret);
+ if (debug == V4L2_DEBUG_IOCTL)
pr_cont("\n");
else if (_IOC_DIR(cmd) == _IOC_NONE)
info->debug(arg, write_only);