summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/hda_verbs.h2
-rw-r--r--sound/pci/ca0106/ca0106.h18
-rw-r--r--sound/pci/ca0106/ca0106_main.c70
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c16
-rw-r--r--sound/pci/hda/hda_intel.c21
-rw-r--r--sound/pci/hda/patch_hdmi.c1
-rw-r--r--sound/usb/mixer_s1810c.c2
-rw-r--r--sound/usb/mixer_scarlett_gen2.c6
-rw-r--r--tools/testing/selftests/alsa/mixer-test.c412
9 files changed, 467 insertions, 81 deletions
diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h
index e36b77531c5c..006d358acce2 100644
--- a/include/sound/hda_verbs.h
+++ b/include/sound/hda_verbs.h
@@ -461,7 +461,7 @@ enum {
#define AC_DE_ELDV (1<<1)
#define AC_DE_IA (1<<2)
-/* device device types (0x0-0xf) */
+/* device types (0x0-0xf) */
enum {
AC_JACK_LINE_OUT,
AC_JACK_SPEAKER,
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index f246e6094289..991b1c5d41d5 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -59,15 +59,15 @@
/* PCI function 0 registers, address = <val> + PCIBASE0 */
/************************************************************************************************/
-#define PTR 0x00 /* Indexed register set pointer register */
+#define CA0106_PTR 0x00 /* Indexed register set pointer register */
/* NOTE: The CHANNELNUM and ADDRESS words can */
/* be modified independently of each other. */
/* CNL[1:0], ADDR[27:16] */
-#define DATA 0x04 /* Indexed register set data register */
+#define CA0106_DATA 0x04 /* Indexed register set data register */
/* DATA[31:0] */
-#define IPR 0x08 /* Global interrupt pending register */
+#define CA0106_IPR 0x08 /* Global interrupt pending register */
/* Clear pending interrupts by writing a 1 to */
/* the relevant bits and zero to the other bits */
#define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
@@ -88,7 +88,7 @@
#define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define IPR_PCI 0x00000001 /* PCI Bus error */
-#define INTE 0x0c /* Interrupt enable register */
+#define CA0106_INTE 0x0c /* Interrupt enable register */
#define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
#define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */
@@ -108,8 +108,8 @@
#define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define INTE_PCI 0x00000001 /* PCI Bus error */
-#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
-#define HCFG 0x14 /* Hardware config register */
+#define CA0106_UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
+#define CA0106_HCFG 0x14 /* Hardware config register */
/* 0x1000 causes AC3 to fails. It adds a dither bit. */
#define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */
@@ -133,7 +133,7 @@
#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
/* Should be set to 1 when the EMU10K1 is */
/* completely initialized. */
-#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
+#define CA0106_GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
/* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
/* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
/* SB Live 24bit:
@@ -152,9 +152,9 @@
* GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
* GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
*/
-#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
+#define CA0106_AC97DATA 0x1c /* AC97 register set data register (16 bit) */
-#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+#define CA0106_AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
/********************************************************************************************************/
/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 36fb150b72fb..8577f9fa5ea6 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -338,8 +338,8 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
+ outl(regptr, emu->port + CA0106_PTR);
+ val = inl(emu->port + CA0106_DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
@@ -355,8 +355,8 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
regptr = (reg << 16) | chn;
spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
+ outl(regptr, emu->port + CA0106_PTR);
+ outl(data, emu->port + CA0106_DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -455,8 +455,8 @@ static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
unsigned int intr_enable;
spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) | intrenb;
- outl(intr_enable, emu->port + INTE);
+ intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -466,8 +466,8 @@ static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb
unsigned int intr_enable;
spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) & ~intrenb;
- outl(intr_enable, emu->port + INTE);
+ intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -786,9 +786,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
reg40 = (reg40 & ~reg40_mask) | reg40_set;
snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
@@ -888,9 +888,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
reg71 = (reg71 & ~reg71_mask) | reg71_set;
snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
@@ -1142,8 +1142,8 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
unsigned short val;
spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ val = inw(emu->port + CA0106_AC97DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
@@ -1155,8 +1155,8 @@ static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
unsigned long flags;
spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- outw(val, emu->port + AC97DATA);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ outw(val, emu->port + CA0106_AC97DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -1200,7 +1200,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
unsigned int stat76;
struct snd_ca0106_channel *pchannel;
- status = inl(chip->port + IPR);
+ status = inl(chip->port + CA0106_IPR);
if (! status)
return IRQ_NONE;
@@ -1255,7 +1255,7 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
}
// acknowledge the interrupt if necessary
- outl(status, chip->port+IPR);
+ outl(status, chip->port + CA0106_IPR);
return IRQ_HANDLED;
}
@@ -1383,7 +1383,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
int ch;
unsigned int def_bits;
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
/*
* Init to 0x02109204 :
@@ -1420,8 +1420,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
/* Write 0x8000 to AC97_REC_GAIN to mute it. */
- outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
- outw(0x8000, chip->port + AC97DATA);
+ outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS);
+ outw(0x8000, chip->port + CA0106_AC97DATA);
#if 0 /* FIXME: what are these? */
snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
@@ -1495,30 +1495,30 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else if (chip->details->gpio_type == 1) {
/* The SB0410 and SB0413 use GPIO differently. */
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else {
- outl(0x0, chip->port+GPIO);
- outl(0x005f03a3, chip->port+GPIO); /* Analog */
- /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
+ outl(0x0, chip->port + CA0106_GPIO);
+ outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */
+ /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */
}
snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
- /* outl(0x00001409, chip->port+HCFG); */
- /* outl(0x00000009, chip->port+HCFG); */
+ /* outl(0x00001409, chip->port + CA0106_HCFG); */
+ /* outl(0x00000009, chip->port + CA0106_HCFG); */
/* AC97 2.0, Enable outputs. */
- outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
+ outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG);
if (chip->details->i2c_adc == 1) {
/* The SB0410 and SB0413 use I2C to control ADC. */
@@ -1560,12 +1560,12 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip)
{
/* disable interrupts */
snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
udelay(1000);
/* disable audio */
/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
- outl(0, chip->port + HCFG);
+ outl(0, chip->port + CA0106_HCFG);
/* FIXME: We need to stop and DMA transfers here.
* But as I am not sure how yet, we cannot from the dma pages.
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index c852c6a75b91..05f56015ddd8 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -70,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) & ~0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) & ~0x101;
+ outl(val, emu->port + CA0106_GPIO);
} else {
/* Analog */
@@ -79,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) | 0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) | 0x101;
+ outl(val, emu->port + CA0106_GPIO);
}
}
@@ -119,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
if (emu->capture_mic_line_in) {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
tmp = tmp | 0x400;
- outl(tmp, emu->port+GPIO);
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
} else {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
- outl(tmp, emu->port+GPIO);
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
}
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4b0338c4c543..a3c4d6e293e1 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2064,14 +2064,16 @@ static const struct hda_controller_ops pci_hda_ops = {
.position_check = azx_position_check,
};
+static DECLARE_BITMAP(probed_devs, SNDRV_CARDS);
+
static int azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
- static int dev;
struct snd_card *card;
struct hda_intel *hda;
struct azx *chip;
bool schedule_probe;
+ int dev;
int err;
if (pci_match_id(driver_denylist, pci)) {
@@ -2079,10 +2081,11 @@ static int azx_probe(struct pci_dev *pci,
return -ENODEV;
}
+ dev = find_first_zero_bit(probed_devs, SNDRV_CARDS);
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
- dev++;
+ set_bit(dev, probed_devs);
return -ENOENT;
}
@@ -2149,7 +2152,7 @@ static int azx_probe(struct pci_dev *pci,
if (schedule_probe)
schedule_delayed_work(&hda->probe_work, 0);
- dev++;
+ set_bit(dev, probed_devs);
if (chip->disabled)
complete_all(&hda->probe_wait);
return 0;
@@ -2372,6 +2375,7 @@ static void azx_remove(struct pci_dev *pci)
cancel_delayed_work_sync(&hda->probe_work);
device_lock(&pci->dev);
+ clear_bit(chip->dev_index, probed_devs);
pci_set_drvdata(pci, NULL);
snd_card_free(card);
}
@@ -2506,6 +2510,17 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
{ PCI_DEVICE(0x8086, 0x4b58),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ /* Raptor Lake */
+ { PCI_DEVICE(0x8086, 0x7a50),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51ca),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cb),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51ce),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+ { PCI_DEVICE(0x8086, 0x51cf),
+ .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
/* Broxton-P(Apollolake) */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 92df4f243ec6..64fe025fda86 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -4390,6 +4390,7 @@ HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi),
HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi),
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c
index 0255089c9efb..fac4bbc6b275 100644
--- a/sound/usb/mixer_s1810c.c
+++ b/sound/usb/mixer_s1810c.c
@@ -221,7 +221,7 @@ static int snd_s1810c_init_mixer_maps(struct snd_usb_audio *chip)
e = 0xbc;
for (n = 0; n < 2; n++) {
off = n * 18;
- for (b = off, c = 0; b < 18 + off; b++) {
+ for (b = off; b < 18 + off; b++) {
/* This channel to all outputs ? */
for (c = 0; c <= 8; c++) {
snd_s1810c_send_ctl_packet(dev, a, b, c, 0, e);
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c
index 53ebabf42472..7ff8a4817c67 100644
--- a/sound/usb/mixer_scarlett_gen2.c
+++ b/sound/usb/mixer_scarlett_gen2.c
@@ -1061,9 +1061,9 @@ static int scarlett2_usb(
{
struct scarlett2_data *private = mixer->private_data;
struct usb_device *dev = mixer->chip->dev;
- u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size;
- u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size;
struct scarlett2_usb_packet *req, *resp = NULL;
+ size_t req_buf_size = struct_size(req, data, req_size);
+ size_t resp_buf_size = struct_size(resp, data, resp_size);
int err;
req = kmalloc(req_buf_size, GFP_KERNEL);
@@ -1111,7 +1111,7 @@ static int scarlett2_usb(
usb_audio_err(
mixer->chip,
"Scarlett Gen 2/3 USB response result cmd %x was %d "
- "expected %d\n",
+ "expected %zu\n",
cmd, err, resp_buf_size);
err = -EINVAL;
goto unlock;
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index 17f158d7a767..eb2213540fe3 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -3,7 +3,7 @@
// kselftest for the ALSA mixer API
//
// Original author: Mark Brown <broonie@kernel.org>
-// Copyright (c) 2021 Arm Limited
+// Copyright (c) 2021-2 Arm Limited
// This test will iterate over all cards detected in the system, exercising
// every mixer control it can find. This may conflict with other system
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <limits.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
@@ -26,11 +27,12 @@
#include "../kselftest.h"
-#define TESTS_PER_CONTROL 3
+#define TESTS_PER_CONTROL 6
struct card_data {
snd_ctl_t *handle;
int card;
+ struct pollfd pollfd;
int num_ctls;
snd_ctl_elem_list_t *ctls;
struct card_data *next;
@@ -42,6 +44,8 @@ struct ctl_data {
snd_ctl_elem_info_t *info;
snd_ctl_elem_value_t *def_val;
int elem;
+ int event_missing;
+ int event_spurious;
struct card_data *card;
struct ctl_data *next;
};
@@ -67,7 +71,8 @@ struct ctl_data *ctl_list = NULL;
#endif
#ifndef LIB_HAS_LOAD_STRING
-int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
+static int snd_config_load_string(snd_config_t **config, const char *s,
+ size_t size)
{
snd_input_t *input;
snd_config_t *dst;
@@ -95,7 +100,7 @@ int snd_config_load_string(snd_config_t **config, const char *s, size_t size)
}
#endif
-void find_controls(void)
+static void find_controls(void)
{
char name[32];
int card, ctl, err;
@@ -148,6 +153,7 @@ void find_controls(void)
if (!ctl_data)
ksft_exit_fail_msg("Out of memory\n");
+ memset(ctl_data, 0, sizeof(*ctl_data));
ctl_data->card = card_data;
ctl_data->elem = ctl;
ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls,
@@ -183,6 +189,26 @@ void find_controls(void)
ctl_list = ctl_data;
}
+ /* Set up for events */
+ err = snd_ctl_subscribe_events(card_data->handle, true);
+ if (err < 0) {
+ ksft_exit_fail_msg("snd_ctl_subscribe_events() failed for card %d: %d\n",
+ card, err);
+ }
+
+ err = snd_ctl_poll_descriptors_count(card_data->handle);
+ if (err != 1) {
+ ksft_exit_fail_msg("Unexpected descriptor count %d for card %d\n",
+ err, card);
+ }
+
+ err = snd_ctl_poll_descriptors(card_data->handle,
+ &card_data->pollfd, 1);
+ if (err != 1) {
+ ksft_exit_fail_msg("snd_ctl_poll_descriptors() failed for %d\n",
+ card, err);
+ }
+
next_card:
if (snd_card_next(&card) < 0) {
ksft_print_msg("snd_card_next");
@@ -193,8 +219,82 @@ void find_controls(void)
snd_config_delete(config);
}
-bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
- int index)
+/*
+ * Block for up to timeout ms for an event, returns a negative value
+ * on error, 0 for no event and 1 for an event.
+ */
+static int wait_for_event(struct ctl_data *ctl, int timeout)
+{
+ unsigned short revents;
+ snd_ctl_event_t *event;
+ int count, err;
+ unsigned int mask = 0;
+ unsigned int ev_id;
+
+ snd_ctl_event_alloca(&event);
+
+ do {
+ err = poll(&(ctl->card->pollfd), 1, timeout);
+ if (err < 0) {
+ ksft_print_msg("poll() failed for %s: %s (%d)\n",
+ ctl->name, strerror(errno), errno);
+ return -1;
+ }
+ /* Timeout */
+ if (err == 0)
+ return 0;
+
+ err = snd_ctl_poll_descriptors_revents(ctl->card->handle,
+ &(ctl->card->pollfd),
+ 1, &revents);
+ if (err < 0) {
+ ksft_print_msg("snd_ctl_poll_descriptors_revents() failed for %s: %d\n",
+ ctl->name, err);
+ return err;
+ }
+ if (revents & POLLERR) {
+ ksft_print_msg("snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n",
+ ctl->name);
+ return -1;
+ }
+ /* No read events */
+ if (!(revents & POLLIN)) {
+ ksft_print_msg("No POLLIN\n");
+ continue;
+ }
+
+ err = snd_ctl_read(ctl->card->handle, event);
+ if (err < 0) {
+ ksft_print_msg("snd_ctl_read() failed for %s: %d\n",
+ ctl->name, err);
+ return err;
+ }
+
+ if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
+ continue;
+
+ /* The ID returned from the event is 1 less than numid */
+ mask = snd_ctl_event_elem_get_mask(event);
+ ev_id = snd_ctl_event_elem_get_numid(event);
+ if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) {
+ ksft_print_msg("Event for unexpected ctl %s\n",
+ snd_ctl_event_elem_get_name(event));
+ continue;
+ }
+
+ if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) {
+ ksft_print_msg("Removal event for %s\n",
+ ctl->name);
+ return -1;
+ }
+ } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE);
+
+ return 1;
+}
+
+static bool ctl_value_index_valid(struct ctl_data *ctl,
+ snd_ctl_elem_value_t *val,
+ int index)
{
long int_val;
long long int64_val;
@@ -305,7 +405,7 @@ bool ctl_value_index_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val,
* Check that the provided value meets the constraints for the
* provided control.
*/
-bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
+static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
{
int i;
bool valid = true;
@@ -321,7 +421,7 @@ bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val)
* Check that we can read the default value and it is valid. Write
* tests use the read value to restore the default.
*/
-void test_ctl_get_value(struct ctl_data *ctl)
+static void test_ctl_get_value(struct ctl_data *ctl)
{
int err;
@@ -356,9 +456,9 @@ out:
ctl->card->card, ctl->elem);
}
-bool show_mismatch(struct ctl_data *ctl, int index,
- snd_ctl_elem_value_t *read_val,
- snd_ctl_elem_value_t *expected_val)
+static bool show_mismatch(struct ctl_data *ctl, int index,
+ snd_ctl_elem_value_t *read_val,
+ snd_ctl_elem_value_t *expected_val)
{
long long expected_int, read_int;
@@ -421,13 +521,14 @@ bool show_mismatch(struct ctl_data *ctl, int index,
* the write to fail, for verifying that invalid writes don't corrupt
* anything.
*/
-int write_and_verify(struct ctl_data *ctl,
- snd_ctl_elem_value_t *write_val,
- snd_ctl_elem_value_t *expected_val)
+static int write_and_verify(struct ctl_data *ctl,
+ snd_ctl_elem_value_t *write_val,
+ snd_ctl_elem_value_t *expected_val)
{
int err, i;
bool error_expected, mismatch_shown;
- snd_ctl_elem_value_t *read_val, *w_val;
+ snd_ctl_elem_value_t *initial_val, *read_val, *w_val;
+ snd_ctl_elem_value_alloca(&initial_val);
snd_ctl_elem_value_alloca(&read_val);
snd_ctl_elem_value_alloca(&w_val);
@@ -445,6 +546,18 @@ int write_and_verify(struct ctl_data *ctl,
snd_ctl_elem_value_copy(expected_val, write_val);
}
+ /* Store the value before we write */
+ if (snd_ctl_elem_info_is_readable(ctl->info)) {
+ snd_ctl_elem_value_set_id(initial_val, ctl->id);
+
+ err = snd_ctl_elem_read(ctl->card->handle, initial_val);
+ if (err < 0) {
+ ksft_print_msg("snd_ctl_elem_read() failed: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ }
+
/*
* Do the write, if we have an expected value ignore the error
* and carry on to validate the expected value.
@@ -470,6 +583,30 @@ int write_and_verify(struct ctl_data *ctl,
}
/*
+ * Check for an event if the value changed, or confirm that
+ * there was none if it didn't. We rely on the kernel
+ * generating the notification before it returns from the
+ * write, this is currently true, should that ever change this
+ * will most likely break and need updating.
+ */
+ if (!snd_ctl_elem_info_is_volatile(ctl->info)) {
+ err = wait_for_event(ctl, 0);
+ if (snd_ctl_elem_value_compare(initial_val, read_val)) {
+ if (err < 1) {
+ ksft_print_msg("No event generated for %s\n",
+ ctl->name);
+ ctl->event_missing++;
+ }
+ } else {
+ if (err != 0) {
+ ksft_print_msg("Spurious event generated for %s\n",
+ ctl->name);
+ ctl->event_spurious++;
+ }
+ }
+ }
+
+ /*
* Use the libray to compare values, if there's a mismatch
* carry on and try to provide a more useful diagnostic than
* just "mismatch".
@@ -493,7 +630,7 @@ int write_and_verify(struct ctl_data *ctl,
* Make sure we can write the default value back to the control, this
* should validate that at least some write works.
*/
-void test_ctl_write_default(struct ctl_data *ctl)
+static void test_ctl_write_default(struct ctl_data *ctl)
{
int err;
@@ -526,7 +663,7 @@ void test_ctl_write_default(struct ctl_data *ctl)
ctl->card->card, ctl->elem);
}
-bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
+static bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
{
int err, i, j;
bool fail = false;
@@ -547,7 +684,7 @@ bool test_ctl_write_valid_boolean(struct ctl_data *ctl)
return !fail;
}
-bool test_ctl_write_valid_integer(struct ctl_data *ctl)
+static bool test_ctl_write_valid_integer(struct ctl_data *ctl)
{
int err;
int i;
@@ -577,7 +714,7 @@ bool test_ctl_write_valid_integer(struct ctl_data *ctl)
return !fail;
}
-bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
+static bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
{
int err, i;
long long j, step;
@@ -605,7 +742,7 @@ bool test_ctl_write_valid_integer64(struct ctl_data *ctl)
return !fail;
}
-bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
+static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
{
int err, i, j;
bool fail = false;
@@ -626,7 +763,7 @@ bool test_ctl_write_valid_enumerated(struct ctl_data *ctl)
return !fail;
}
-void test_ctl_write_valid(struct ctl_data *ctl)
+static void test_ctl_write_valid(struct ctl_data *ctl)
{
bool pass;
int err;
@@ -679,6 +816,236 @@ void test_ctl_write_valid(struct ctl_data *ctl)
ctl->card->card, ctl->elem);
}
+static bool test_ctl_write_invalid_value(struct ctl_data *ctl,
+ snd_ctl_elem_value_t *val)
+{
+ int err;
+ long val_read;
+
+ /* Ideally this will fail... */
+ err = snd_ctl_elem_write(ctl->card->handle, val);
+ if (err < 0)
+ return false;
+
+ /* ...but some devices will clamp to an in range value */
+ err = snd_ctl_elem_read(ctl->card->handle, val);
+ if (err < 0) {
+ ksft_print_msg("%s failed to read: %s\n",
+ ctl->name, snd_strerror(err));
+ return true;
+ }
+
+ return !ctl_value_valid(ctl, val);
+}
+
+static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl)
+{
+ int err, i;
+ long val_read;
+ bool fail = false;
+ snd_ctl_elem_value_t *val;
+ snd_ctl_elem_value_alloca(&val);
+
+ for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_boolean(val, i, 2);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+ }
+
+ return !fail;
+}
+
+static bool test_ctl_write_invalid_integer(struct ctl_data *ctl)
+{
+ int i;
+ bool fail = false;
+ snd_ctl_elem_value_t *val;
+ snd_ctl_elem_value_alloca(&val);
+
+ for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+ if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) {
+ /* Just under range */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer(val, i,
+ snd_ctl_elem_info_get_min(ctl->info) - 1);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ /* Minimum representable value */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer(val, i, LONG_MIN);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+ }
+
+ if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) {
+ /* Just over range */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer(val, i,
+ snd_ctl_elem_info_get_max(ctl->info) + 1);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ /* Maximum representable value */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer(val, i, LONG_MAX);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+ }
+ }
+
+ return !fail;
+}
+
+static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl)
+{
+ int i;
+ bool fail = false;
+ snd_ctl_elem_value_t *val;
+ snd_ctl_elem_value_alloca(&val);
+
+ for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+ if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) {
+ /* Just under range */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer64(val, i,
+ snd_ctl_elem_info_get_min64(ctl->info) - 1);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ /* Minimum representable value */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+ }
+
+ if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) {
+ /* Just over range */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer64(val, i,
+ snd_ctl_elem_info_get_max64(ctl->info) + 1);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ /* Maximum representable value */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+ }
+ }
+
+ return !fail;
+}
+
+static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl)
+{
+ int err, i;
+ unsigned int val_read;
+ bool fail = false;
+ snd_ctl_elem_value_t *val;
+ snd_ctl_elem_value_alloca(&val);
+
+ snd_ctl_elem_value_set_id(val, ctl->id);
+
+ for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) {
+ /* One beyond maximum */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_enumerated(val, i,
+ snd_ctl_elem_info_get_items(ctl->info));
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ /* Maximum representable value */
+ snd_ctl_elem_value_copy(val, ctl->def_val);
+ snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX);
+
+ if (test_ctl_write_invalid_value(ctl, val))
+ fail = true;
+
+ }
+
+ return !fail;
+}
+
+
+static void test_ctl_write_invalid(struct ctl_data *ctl)
+{
+ bool pass;
+ int err;
+
+ /* If the control is turned off let's be polite */
+ if (snd_ctl_elem_info_is_inactive(ctl->info)) {
+ ksft_print_msg("%s is inactive\n", ctl->name);
+ ksft_test_result_skip("write_invalid.%d.%d\n",
+ ctl->card->card, ctl->elem);
+ return;
+ }
+
+ if (!snd_ctl_elem_info_is_writable(ctl->info)) {
+ ksft_print_msg("%s is not writeable\n", ctl->name);
+ ksft_test_result_skip("write_invalid.%d.%d\n",
+ ctl->card->card, ctl->elem);
+ return;
+ }
+
+ switch (snd_ctl_elem_info_get_type(ctl->info)) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ pass = test_ctl_write_invalid_boolean(ctl);
+ break;
+
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ pass = test_ctl_write_invalid_integer(ctl);
+ break;
+
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ pass = test_ctl_write_invalid_integer64(ctl);
+ break;
+
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ pass = test_ctl_write_invalid_enumerated(ctl);
+ break;
+
+ default:
+ /* No tests for this yet */
+ ksft_test_result_skip("write_invalid.%d.%d\n",
+ ctl->card->card, ctl->elem);
+ return;
+ }
+
+ /* Restore the default value to minimise disruption */
+ err = write_and_verify(ctl, ctl->def_val, NULL);
+ if (err < 0)
+ pass = false;
+
+ ksft_test_result(pass, "write_invalid.%d.%d\n",
+ ctl->card->card, ctl->elem);
+}
+
+static void test_ctl_event_missing(struct ctl_data *ctl)
+{
+ ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n",
+ ctl->card->card, ctl->elem);
+}
+
+static void test_ctl_event_spurious(struct ctl_data *ctl)
+{
+ ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n",
+ ctl->card->card, ctl->elem);
+}
+
int main(void)
{
struct ctl_data *ctl;
@@ -697,6 +1064,9 @@ int main(void)
test_ctl_get_value(ctl);
test_ctl_write_default(ctl);
test_ctl_write_valid(ctl);
+ test_ctl_write_invalid(ctl);
+ test_ctl_event_missing(ctl);
+ test_ctl_event_spurious(ctl);
}
ksft_exit_pass();