mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-11 21:14:07 +08:00
media: em28xx: fix handler for vidioc_s_input()
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 commit195a4ef627
("V4L/DVB (6585): Convert em28xx to video_ioctl2") this was not implemented right. Fixes:195a4ef627
("V4L/DVB (6585): Convert em28xx to video_ioctl2") Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
parent
5c4c4505b7
commit
258c430456
@ -3039,6 +3039,9 @@ static int em28xx_hint_board(struct em28xx *dev)
|
|||||||
|
|
||||||
static void em28xx_card_setup(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 the device can be a webcam, seek for a sensor.
|
||||||
* If sensor is not found, then it isn't a webcam.
|
* 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 */
|
/* Allow override tuner type by a module parameter */
|
||||||
if (tuner >= 0)
|
if (tuner >= 0)
|
||||||
dev->tuner_type = tuner;
|
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)
|
void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
|
||||||
|
@ -1666,6 +1666,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct em28xx *dev = video_drvdata(file);
|
struct em28xx *dev = video_drvdata(file);
|
||||||
unsigned int n;
|
unsigned int n;
|
||||||
|
int j;
|
||||||
|
|
||||||
n = i->index;
|
n = i->index;
|
||||||
if (n >= MAX_EM28XX_INPUT)
|
if (n >= MAX_EM28XX_INPUT)
|
||||||
@ -1685,6 +1686,12 @@ static int vidioc_enum_input(struct file *file, void *priv,
|
|||||||
if (dev->is_webcam)
|
if (dev->is_webcam)
|
||||||
i->capabilities = 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1710,11 +1717,24 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
|
|||||||
return 0;
|
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:
|
case EM28XX_AMUX_VIDEO:
|
||||||
strscpy(a->name, "Television", sizeof(a->name));
|
strscpy(a->name, "Television", sizeof(a->name));
|
||||||
break;
|
break;
|
||||||
@ -1739,32 +1759,79 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
|
|||||||
case EM28XX_AMUX_PCM_OUT:
|
case EM28XX_AMUX_PCM_OUT:
|
||||||
strscpy(a->name, "PCM", sizeof(a->name));
|
strscpy(a->name, "PCM", sizeof(a->name));
|
||||||
break;
|
break;
|
||||||
|
case EM28XX_AMUX_UNUSED:
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
a->index = index;
|
||||||
a->index = dev->ctl_ainput;
|
|
||||||
a->capability = V4L2_AUDCAP_STEREO;
|
a->capability = V4L2_AUDCAP_STEREO;
|
||||||
|
|
||||||
|
em28xx_videodbg("%s: audio input index %d is '%s'\n",
|
||||||
|
s, a->index, a->name);
|
||||||
|
|
||||||
return 0;
|
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,
|
static int vidioc_s_audio(struct file *file, void *priv,
|
||||||
const struct v4l2_audio *a)
|
const struct v4l2_audio *a)
|
||||||
{
|
{
|
||||||
struct em28xx *dev = video_drvdata(file);
|
struct em28xx *dev = video_drvdata(file);
|
||||||
|
int idx, i;
|
||||||
|
|
||||||
if (a->index >= MAX_EM28XX_INPUT)
|
if (a->index >= MAX_EM28XX_INPUT)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!INPUT(a->index)->type)
|
|
||||||
|
idx = dev->amux_map[a->index];
|
||||||
|
|
||||||
|
if (idx == EM28XX_AMUX_UNUSED)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
dev->ctl_ainput = INPUT(a->index)->amux;
|
dev->ctl_ainput = idx;
|
||||||
dev->ctl_aoutput = INPUT(a->index)->aout;
|
|
||||||
|
/*
|
||||||
|
* 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)
|
if (!dev->ctl_aoutput)
|
||||||
dev->ctl_aoutput = EM28XX_AOUT_MASTER;
|
dev->ctl_aoutput = EM28XX_AOUT_MASTER;
|
||||||
|
|
||||||
|
em28xx_videodbg("%s: set audio input to %d\n", __func__,
|
||||||
|
dev->ctl_ainput);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2302,6 +2369,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
|
|||||||
.vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
|
.vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
|
||||||
.vidioc_s_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_enum_framesizes = vidioc_enum_framesizes,
|
||||||
|
.vidioc_enumaudio = vidioc_enumaudio,
|
||||||
.vidioc_g_audio = vidioc_g_audio,
|
.vidioc_g_audio = vidioc_g_audio,
|
||||||
.vidioc_s_audio = vidioc_s_audio,
|
.vidioc_s_audio = vidioc_s_audio,
|
||||||
|
|
||||||
|
@ -335,6 +335,9 @@ enum em28xx_usb_audio_type {
|
|||||||
/**
|
/**
|
||||||
* em28xx_amux - describes the type of audio input used by em28xx
|
* 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:
|
* @EM28XX_AMUX_VIDEO:
|
||||||
* On devices without AC97, this is the only value that it is currently
|
* On devices without AC97, this is the only value that it is currently
|
||||||
* allowed.
|
* allowed.
|
||||||
@ -369,7 +372,8 @@ enum em28xx_usb_audio_type {
|
|||||||
* same time, via the alsa mux.
|
* same time, via the alsa mux.
|
||||||
*/
|
*/
|
||||||
enum em28xx_amux {
|
enum em28xx_amux {
|
||||||
EM28XX_AMUX_VIDEO,
|
EM28XX_AMUX_UNUSED = -1,
|
||||||
|
EM28XX_AMUX_VIDEO = 0,
|
||||||
EM28XX_AMUX_LINE_IN,
|
EM28XX_AMUX_LINE_IN,
|
||||||
|
|
||||||
/* Some less-common mixer setups */
|
/* Some less-common mixer setups */
|
||||||
@ -692,6 +696,8 @@ struct em28xx {
|
|||||||
unsigned int ctl_input; // selected input
|
unsigned int ctl_input; // selected input
|
||||||
unsigned int ctl_ainput;// selected audio input
|
unsigned int ctl_ainput;// selected audio input
|
||||||
unsigned int ctl_aoutput;// selected audio output
|
unsigned int ctl_aoutput;// selected audio output
|
||||||
|
enum em28xx_amux amux_map[MAX_EM28XX_INPUT];
|
||||||
|
|
||||||
int mute;
|
int mute;
|
||||||
int volume;
|
int volume;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user