diff options
-rw-r--r-- | include/sound/hda_verbs.h | 2 | ||||
-rw-r--r-- | sound/pci/ca0106/ca0106.h | 18 | ||||
-rw-r--r-- | sound/pci/ca0106/ca0106_main.c | 70 | ||||
-rw-r--r-- | sound/pci/ca0106/ca0106_mixer.c | 16 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 21 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 1 | ||||
-rw-r--r-- | sound/usb/mixer_s1810c.c | 2 | ||||
-rw-r--r-- | sound/usb/mixer_scarlett_gen2.c | 6 | ||||
-rw-r--r-- | tools/testing/selftests/alsa/mixer-test.c | 412 |
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(); |