Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a79eb12 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab Committed by Greg Kroah-Hartman
Browse files

media: em28xx: fix handler for vidioc_s_input()



commit 258c430456ba5f0005043762e14fc3be35983aaf upstream.

The a->index is not the name of the internal amux entry,
but, instead a value from zero to the maximum number
of audio inputs.

As the actual available inputs depend on each board, build
it dynamically.

This is broken for a really long time. On a quick check,
since at least commit 195a4ef6 ("V4L/DVB (6585): Convert
em28xx to video_ioctl2") this was not implemented right.

Fixes: 195a4ef6 ("V4L/DVB (6585): Convert em28xx to video_ioctl2")

Cc: stable@vger.kernel.org
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3e2e4d05
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -3039,6 +3039,9 @@ static int em28xx_hint_board(struct em28xx *dev)

static void em28xx_card_setup(struct em28xx *dev)
{
	int i, j, idx;
	bool duplicate_entry;

	/*
	 * If the device can be a webcam, seek for a sensor.
	 * If sensor is not found, then it isn't a webcam.
@@ -3195,6 +3198,32 @@ static void em28xx_card_setup(struct em28xx *dev)
	/* Allow override tuner type by a module parameter */
	if (tuner >= 0)
		dev->tuner_type = tuner;

	/*
	 * Dynamically generate a list of valid audio inputs for this
	 * specific board, mapping them via enum em28xx_amux.
	 */

	idx = 0;
	for (i = 0; i < MAX_EM28XX_INPUT; i++) {
		if (!INPUT(i)->type)
			continue;

		/* Skip already mapped audio inputs */
		duplicate_entry = false;
		for (j = 0; j < idx; j++) {
			if (INPUT(i)->amux == dev->amux_map[j]) {
				duplicate_entry = true;
				break;
			}
		}
		if (duplicate_entry)
			continue;

		dev->amux_map[idx++] = INPUT(i)->amux;
	}
	for (; idx < MAX_EM28XX_INPUT; idx++)
		dev->amux_map[idx] = EM28XX_AMUX_UNUSED;
}

void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
+76 −8
Original line number Diff line number Diff line
@@ -1668,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)
@@ -1687,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;
}

@@ -1712,11 +1719,24 @@ 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];

	switch (a->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 (idx) {
	case EM28XX_AMUX_VIDEO:
		strcpy(a->name, "Television");
		break;
@@ -1741,32 +1761,79 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
	case EM28XX_AMUX_PCM_OUT:
		strcpy(a->name, "PCM");
		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 = 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_ainput = INPUT(a->index)->amux;
	dev->ctl_aoutput = INPUT(a->index)->aout;
	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;
}

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

+7 −1
Original line number Diff line number Diff line
@@ -335,6 +335,9 @@ enum em28xx_usb_audio_type {
/**
 * em28xx_amux - describes the type of audio input used by em28xx
 *
 * @EM28XX_AMUX_UNUSED:
 *	Used only on em28xx dev->map field, in order to mark an entry
 *	as unused.
 * @EM28XX_AMUX_VIDEO:
 *	On devices without AC97, this is the only value that it is currently
 *	allowed.
@@ -369,7 +372,8 @@ enum em28xx_usb_audio_type {
 * same time, via the alsa mux.
 */
enum em28xx_amux {
	EM28XX_AMUX_VIDEO,
	EM28XX_AMUX_UNUSED = -1,
	EM28XX_AMUX_VIDEO = 0,
	EM28XX_AMUX_LINE_IN,

	/* Some less-common mixer setups */
@@ -692,6 +696,8 @@ struct em28xx {
	unsigned int ctl_input;	// selected input
	unsigned int ctl_ainput;// selected audio input
	unsigned int ctl_aoutput;// selected audio output
	enum em28xx_amux amux_map[MAX_EM28XX_INPUT];

	int mute;
	int volume;