summaryrefslogtreecommitdiff
path: root/drivers/media/usb/em28xx/em28xx-video.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-video.c')
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c124
1 files changed, 97 insertions, 27 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 68571bf36d28..f43717ea831d 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1093,6 +1093,8 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
em28xx_videodbg("%s\n", __func__);
+ dev->v4l2->field_count = 0;
+
/*
* Make sure streaming is not already in progress for this type
* of filehandle (e.g. video, vbi)
@@ -1471,9 +1473,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
if (!fmt) {
- em28xx_videodbg("Fourcc format (%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
+ fmt = &format[0];
+ em28xx_videodbg("Fourcc format (%08x) invalid. Using default (%08x).\n",
+ f->fmt.pix.pixelformat, fmt->fourcc);
}
if (dev->board.is_em2800) {
@@ -1666,6 +1668,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
{
struct em28xx *dev = video_drvdata(file);
unsigned int n;
+ int j;
n = i->index;
if (n >= MAX_EM28XX_INPUT)
@@ -1675,7 +1678,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, iname[INPUT(n)->type]);
+ strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));
if (INPUT(n)->type == EM28XX_VMUX_TELEVISION)
i->type = V4L2_INPUT_TYPE_TUNER;
@@ -1685,6 +1688,12 @@ static int vidioc_enum_input(struct file *file, void *priv,
if (dev->is_webcam)
i->capabilities = 0;
+ /* Dynamically generates an audioset bitmask */
+ i->audioset = 0;
+ for (j = 0; j < MAX_EM28XX_INPUT; j++)
+ if (dev->amux_map[j] != EM28XX_AMUX_UNUSED)
+ i->audioset |= 1 << j;
+
return 0;
}
@@ -1710,61 +1719,121 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+static int em28xx_fill_audio_input(struct em28xx *dev,
+ const char *s,
+ struct v4l2_audio *a,
+ unsigned int index)
{
- struct em28xx *dev = video_drvdata(file);
+ unsigned int idx = dev->amux_map[index];
+
+ /*
+ * With msp3400, almost all mappings use the default (amux = 0).
+ * The only one may use a different value is WinTV USB2, where it
+ * can also be SCART1 input.
+ * As it is very doubtful that we would see new boards with msp3400,
+ * let's just reuse the existing switch.
+ */
+ if (dev->has_msp34xx && idx != EM28XX_AMUX_UNUSED)
+ idx = EM28XX_AMUX_LINE_IN;
- switch (a->index) {
+ switch (idx) {
case EM28XX_AMUX_VIDEO:
- strcpy(a->name, "Television");
+ strscpy(a->name, "Television", sizeof(a->name));
break;
case EM28XX_AMUX_LINE_IN:
- strcpy(a->name, "Line In");
+ strscpy(a->name, "Line In", sizeof(a->name));
break;
case EM28XX_AMUX_VIDEO2:
- strcpy(a->name, "Television alt");
+ strscpy(a->name, "Television alt", sizeof(a->name));
break;
case EM28XX_AMUX_PHONE:
- strcpy(a->name, "Phone");
+ strscpy(a->name, "Phone", sizeof(a->name));
break;
case EM28XX_AMUX_MIC:
- strcpy(a->name, "Mic");
+ strscpy(a->name, "Mic", sizeof(a->name));
break;
case EM28XX_AMUX_CD:
- strcpy(a->name, "CD");
+ strscpy(a->name, "CD", sizeof(a->name));
break;
case EM28XX_AMUX_AUX:
- strcpy(a->name, "Aux");
+ strscpy(a->name, "Aux", sizeof(a->name));
break;
case EM28XX_AMUX_PCM_OUT:
- strcpy(a->name, "PCM");
+ strscpy(a->name, "PCM", sizeof(a->name));
break;
+ case EM28XX_AMUX_UNUSED:
default:
return -EINVAL;
}
-
- a->index = dev->ctl_ainput;
+ a->index = index;
a->capability = V4L2_AUDCAP_STEREO;
+ em28xx_videodbg("%s: audio input index %d is '%s'\n",
+ s, a->index, a->name);
+
return 0;
}
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ struct em28xx *dev = video_drvdata(file);
+
+ if (a->index >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+
+ return em28xx_fill_audio_input(dev, __func__, a, a->index);
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx *dev = video_drvdata(file);
+ int i;
+
+ for (i = 0; i < MAX_EM28XX_INPUT; i++)
+ if (dev->ctl_ainput == dev->amux_map[i])
+ return em28xx_fill_audio_input(dev, __func__, a, i);
+
+ /* Should never happen! */
+ return -EINVAL;
+}
+
static int vidioc_s_audio(struct file *file, void *priv,
const struct v4l2_audio *a)
{
struct em28xx *dev = video_drvdata(file);
+ int idx, i;
if (a->index >= MAX_EM28XX_INPUT)
return -EINVAL;
- if (!INPUT(a->index)->type)
+
+ idx = dev->amux_map[a->index];
+
+ if (idx == EM28XX_AMUX_UNUSED)
return -EINVAL;
- dev->ctl_ainput = INPUT(a->index)->amux;
- dev->ctl_aoutput = INPUT(a->index)->aout;
+ dev->ctl_ainput = idx;
+
+ /*
+ * FIXME: This is wrong, as different inputs at em28xx_cards
+ * may have different audio outputs. So, the right thing
+ * to do is to implement VIDIOC_G_AUDOUT/VIDIOC_S_AUDOUT.
+ * With the current board definitions, this would work fine,
+ * as, currently, all boards fit.
+ */
+ for (i = 0; i < MAX_EM28XX_INPUT; i++)
+ if (idx == dev->amux_map[i])
+ break;
+ if (i == MAX_EM28XX_INPUT)
+ return -EINVAL;
+
+ dev->ctl_aoutput = INPUT(i)->aout;
if (!dev->ctl_aoutput)
dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+ em28xx_videodbg("%s: set audio input to %d\n", __func__,
+ dev->ctl_ainput);
+
return 0;
}
@@ -1776,7 +1845,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
if (t->index != 0)
return -EINVAL;
- strcpy(t->name, "Tuner");
+ strscpy(t->name, "Tuner", sizeof(t->name));
v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
return 0;
@@ -1833,9 +1902,9 @@ static int vidioc_g_chip_info(struct file *file, void *priv,
if (chip->match.addr > 1)
return -EINVAL;
if (chip->match.addr == 1)
- strlcpy(chip->name, "ac97", sizeof(chip->name));
+ strscpy(chip->name, "ac97", sizeof(chip->name));
else
- strlcpy(chip->name,
+ strscpy(chip->name,
dev->v4l2->v4l2_dev.name, sizeof(chip->name));
return 0;
}
@@ -1920,8 +1989,8 @@ static int vidioc_querycap(struct file *file, void *priv,
struct em28xx_v4l2 *v4l2 = dev->v4l2;
struct usb_device *udev = interface_to_usbdev(dev->intf);
- strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
- strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strscpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strscpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info));
if (vdev->vfl_type == VFL_TYPE_GRABBER)
@@ -1954,7 +2023,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
if (unlikely(f->index >= ARRAY_SIZE(format)))
return -EINVAL;
- strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ strscpy(f->description, format[f->index].name, sizeof(f->description));
f->pixelformat = format[f->index].fourcc;
return 0;
@@ -2045,7 +2114,7 @@ static int radio_g_tuner(struct file *file, void *priv,
if (unlikely(t->index > 0))
return -EINVAL;
- strcpy(t->name, "Radio");
+ strscpy(t->name, "Radio", sizeof(t->name));
v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
@@ -2302,6 +2371,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_enumaudio = vidioc_enumaudio,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,