Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
September 2009
- 123 participants
- 225 discussions
01 Oct '09
From: Krzysztof Helt <krzysztof.h1(a)wp.pl>
The conversion solves the problem that firmware size was set to 64KB
while non PnP cards have 128KB firmware files.
An additional firmware initialization code has been moved from the OSS
driver.
Signed-off-by: Krzysztof Helt <krzysztof.h1(a)wp.pl>
---
This is the second version with fixes to issues pointed by Takashi and two mistakes in the original patch.
Documentation/sound/alsa/ALSA-Configuration.txt | 8 +-
include/sound/sscape_ioctl.h | 21 --
sound/isa/Kconfig | 8 +-
sound/isa/sscape.c | 328 ++++++++---------------
4 files changed, 116 insertions(+), 249 deletions(-)
delete mode 100644 include/sound/sscape_ioctl.h
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 1c8eb45..cf98525 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -1631,7 +1631,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-sscape
-----------------
- Module for ENSONIQ SoundScape PnP cards.
+ Module for ENSONIQ SoundScape cards.
port - Port # (PnP setup)
wss_port - WSS Port # (PnP setup)
@@ -1640,9 +1640,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
dma - DMA # (PnP setup)
dma2 - 2nd DMA # (PnP setup, -1 to disable)
- This module supports multiple cards. ISA PnP must be enabled.
- You need sscape_ctl tool in alsa-tools package for loading
- the microcode.
+ This module supports multiple cards.
+
+ The driver requires the firmware loader support on kernel.
Module snd-sun-amd7930 (on sparc only)
--------------------------------------
diff --git a/include/sound/sscape_ioctl.h b/include/sound/sscape_ioctl.h
deleted file mode 100644
index 0d88859..0000000
--- a/include/sound/sscape_ioctl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef SSCAPE_IOCTL_H
-#define SSCAPE_IOCTL_H
-
-
-struct sscape_bootblock
-{
- unsigned char code[256];
- unsigned version;
-};
-
-#define SSCAPE_MICROCODE_SIZE 65536
-
-struct sscape_microcode
-{
- unsigned char __user *code;
-};
-
-#define SND_SSCAPE_LOAD_BOOTB _IOWR('P', 100, struct sscape_bootblock)
-#define SND_SSCAPE_LOAD_MCODE _IOW ('P', 101, struct sscape_microcode)
-
-#endif
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index b90fc16..02fe81c 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -372,9 +372,9 @@ config SND_SGALAXY
config SND_SSCAPE
tristate "Ensoniq SoundScape driver"
- select SND_HWDEP
select SND_MPU401_UART
select SND_WSS_LIB
+ select FW_LOADER
help
Say Y here to include support for Ensoniq SoundScape
and Ensoniq OEM soundcards.
@@ -382,7 +382,11 @@ config SND_SSCAPE
The PCM audio is supported on SoundScape Classic, Elite, PnP
and VIVO cards. The supported OEM cards are SPEA Media FX and
Reveal SC-600.
- The MIDI support is very experimental.
+ The MIDI support is very experimental and requires binary
+ firmware files called "scope.cod" and "sndscape.co?" where the
+ ? is digit 0, 1, 2, 3 or 4. The firmware files can be found
+ in DOS or Windows driver packages. One has to put the firmware
+ files into the /lib/firmware directory.
To compile this driver as a module, choose M here: the module
will be called snd-sscape.
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index b11c35f..1ce465c 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,5 +1,5 @@
/*
- * Low-level ALSA driver for the ENSONIQ SoundScape PnP
+ * Low-level ALSA driver for the ENSONIQ SoundScape
* Copyright (c) by Chris Rankin
*
* This driver was written in part using information obtained from
@@ -25,22 +25,26 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/pnp.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <asm/dma.h>
#include <sound/core.h>
-#include <sound/hwdep.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
-#include <sound/sscape_ioctl.h>
-
MODULE_AUTHOR("Chris Rankin");
-MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");
+MODULE_DESCRIPTION("ENSONIQ SoundScape driver");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("sndscape.co0");
+MODULE_FIRMWARE("sndscape.co1");
+MODULE_FIRMWARE("sndscape.co2");
+MODULE_FIRMWARE("sndscape.co3");
+MODULE_FIRMWARE("sndscape.co4");
+MODULE_FIRMWARE("scope.cod");
static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;
static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;
@@ -142,14 +146,12 @@ struct soundscape {
struct resource *wss_res;
struct snd_wss *chip;
struct snd_mpu401 *mpu;
- struct snd_hwdep *hw;
/*
* The MIDI device won't work until we've loaded
* its firmware via a hardware-dependent device IOCTL
*/
spinlock_t fwlock;
- int hw_in_use;
unsigned long midi_usage;
unsigned char midi_vol;
};
@@ -167,12 +169,6 @@ static inline struct soundscape *get_mpu401_soundscape(struct snd_mpu401 * mpu)
return (struct soundscape *) (mpu->private_data);
}
-static inline struct soundscape *get_hwdep_soundscape(struct snd_hwdep * hw)
-{
- return (struct soundscape *) (hw->private_data);
-}
-
-
/*
* Allocates some kernel memory that we can use for DMA.
* I think this means that the memory has to map to
@@ -393,12 +389,12 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
- if ((x & 0xfe) == 0xfe)
+ if (x == 0xfe || x == 0xff)
return 1;
msleep(10);
@@ -420,10 +416,10 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
do {
unsigned long flags;
- unsigned char x;
+ int x;
spin_lock_irqsave(&s->lock, flags);
- x = inb(HOST_DATA_IO(s->io_base));
+ x = host_read_unsafe(s->io_base);
spin_unlock_irqrestore(&s->lock, flags);
if (x == 0xfe)
return 1;
@@ -438,14 +434,14 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
* Upload a byte-stream into the SoundScape using DMA channel A.
*/
static int upload_dma_data(struct soundscape *s,
- const unsigned char __user *data,
+ const unsigned char *data,
size_t size)
{
unsigned long flags;
struct snd_dma_buffer dma;
int ret;
- if (!get_dmabuf(&dma, PAGE_ALIGN(size)))
+ if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
spin_lock_irqsave(&s->lock, flags);
@@ -458,7 +454,6 @@ static int upload_dma_data(struct soundscape *s,
/*
* Enable the DMA channels and configure them ...
*/
- sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);
sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);
sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
@@ -468,35 +463,17 @@ static int upload_dma_data(struct soundscape *s,
sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);
/*
- * Upload the user's data (firmware?) to the SoundScape
+ * Upload the firmware to the SoundScape
* board through the DMA channel ...
*/
while (size != 0) {
unsigned long len;
- /*
- * Apparently, copying to/from userspace can sleep.
- * We are therefore forbidden from holding any
- * spinlocks while we copy ...
- */
- spin_unlock_irqrestore(&s->lock, flags);
-
- /*
- * Remember that the data that we want to DMA
- * comes from USERSPACE. We have already verified
- * the userspace pointer ...
- */
len = min(size, dma.bytes);
- len -= __copy_from_user(dma.area, data, len);
+ memcpy(dma.area, data, len);
data += len;
size -= len;
- /*
- * Grab that spinlock again, now that we've
- * finished copying!
- */
- spin_lock_irqsave(&s->lock, flags);
-
snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
@@ -512,6 +489,7 @@ static int upload_dma_data(struct soundscape *s,
} /* while */
set_host_mode_unsafe(s->io_base);
+ outb(0x0, s->io_base);
/*
* Boot the board ... (I think)
@@ -537,7 +515,7 @@ _release_dma:
/*
* NOTE!!! We are NOT holding any spinlocks at this point !!!
*/
- sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_ODIE ? 0x70 : 0x40));
+ sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
free_dmabuf(&dma);
return ret;
@@ -547,162 +525,69 @@ _release_dma:
* Upload the bootblock(?) into the SoundScape. The only
* purpose of this block of code seems to be to tell
* us which version of the microcode we should be using.
- *
- * NOTE: The boot-block data resides in USER-SPACE!!!
- * However, we have already verified its memory
- * addresses by the time we get here.
*/
-static int sscape_upload_bootblock(struct soundscape *sscape, struct sscape_bootblock __user *bb)
+static int sscape_upload_bootblock(struct snd_card *card)
{
+ struct soundscape *sscape = get_card_soundscape(card);
unsigned long flags;
+ const struct firmware *init_fw = NULL;
int data = 0;
int ret;
- ret = upload_dma_data(sscape, bb->code, sizeof(bb->code));
-
- spin_lock_irqsave(&sscape->lock, flags);
- if (ret == 0) {
- data = host_read_ctrl_unsafe(sscape->io_base, 100);
- }
- set_midi_mode_unsafe(sscape->io_base);
- spin_unlock_irqrestore(&sscape->lock, flags);
-
- if (ret == 0) {
- if (data < 0) {
- snd_printk(KERN_ERR "sscape: timeout reading firmware version\n");
- ret = -EAGAIN;
- }
- else if (__copy_to_user(&bb->version, &data, sizeof(bb->version))) {
- ret = -EFAULT;
- }
+ ret = request_firmware(&init_fw, "scope.cod", card->dev);
+ if (ret < 0) {
+ snd_printk(KERN_ERR "Error loading scope.cod");
+ return ret;
}
+ ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
- return ret;
-}
+ release_firmware(init_fw);
-/*
- * Upload the microcode into the SoundScape. The
- * microcode is 64K of data, and if we try to copy
- * it into a local variable then we will SMASH THE
- * KERNEL'S STACK! We therefore leave it in USER
- * SPACE, and save ourselves from copying it at all.
- */
-static int sscape_upload_microcode(struct soundscape *sscape,
- const struct sscape_microcode __user *mc)
-{
- unsigned long flags;
- char __user *code;
- int err;
-
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now.
- *
- * NOTE: This buffer is 64K long! That's WAY too big to
- * copy into a stack-temporary anyway.
- */
- if ( get_user(code, &mc->code) ||
- !access_ok(VERIFY_READ, code, SSCAPE_MICROCODE_SIZE) )
- return -EFAULT;
+ spin_lock_irqsave(&sscape->lock, flags);
+ if (ret == 0)
+ data = host_read_ctrl_unsafe(sscape->io_base, 100);
- if ((err = upload_dma_data(sscape, code, SSCAPE_MICROCODE_SIZE)) == 0) {
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded\n");
- }
+ if (data & 0x10)
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
- spin_lock_irqsave(&sscape->lock, flags);
- set_midi_mode_unsafe(sscape->io_base);
spin_unlock_irqrestore(&sscape->lock, flags);
- initialise_mpu401(sscape->mpu);
+ data &= 0xf;
+ if (ret == 0 && data > 7) {
+ snd_printk(KERN_ERR "timeout reading firmware version\n");
+ ret = -EAGAIN;
+ }
- return err;
+ return (ret == 0) ? data : ret;
}
/*
- * Hardware-specific device functions, to implement special
- * IOCTLs for the SoundScape card. This is how we upload
- * the microcode into the card, for example, and so we
- * must ensure that no two processes can open this device
- * simultaneously, and that we can't open it at all if
- * someone is using the MIDI device.
+ * Upload the microcode into the SoundScape.
*/
-static int sscape_hw_open(struct snd_hwdep * hw, struct file *file)
+static int sscape_upload_microcode(struct snd_card *card, int version)
{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
+ struct soundscape *sscape = get_card_soundscape(card);
+ const struct firmware *init_fw = NULL;
+ char name[14];
int err;
- spin_lock_irqsave(&sscape->fwlock, flags);
+ snprintf(name, sizeof(name), "sndscape.co%d", version);
- if ((sscape->midi_usage != 0) || sscape->hw_in_use) {
- err = -EBUSY;
- } else {
- sscape->hw_in_use = 1;
- err = 0;
+ err = request_firmware(&init_fw, name, card->dev);
+ if (err < 0) {
+ snd_printk(KERN_ERR "Error loading sndscape.co%d", version);
+ return err;
}
+ err = upload_dma_data(sscape, init_fw->data, init_fw->size);
+ if (err == 0)
+ snd_printk(KERN_INFO "MIDI firmware loaded %d KBs\n",
+ init_fw->size >> 10);
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return err;
-}
-
-static int sscape_hw_release(struct snd_hwdep * hw, struct file *file)
-{
- register struct soundscape *sscape = get_hwdep_soundscape(hw);
- unsigned long flags;
-
- spin_lock_irqsave(&sscape->fwlock, flags);
- sscape->hw_in_use = 0;
- spin_unlock_irqrestore(&sscape->fwlock, flags);
- return 0;
-}
-
-static int sscape_hw_ioctl(struct snd_hwdep * hw, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct soundscape *sscape = get_hwdep_soundscape(hw);
- int err = -EBUSY;
-
- switch (cmd) {
- case SND_SSCAPE_LOAD_BOOTB:
- {
- register struct sscape_bootblock __user *bb = (struct sscape_bootblock __user *) arg;
-
- /*
- * We are going to have to copy this data into a special
- * DMA-able buffer before we can upload it. We shall therefore
- * just check that the data pointer is valid for now ...
- */
- if ( !access_ok(VERIFY_READ, bb->code, sizeof(bb->code)) )
- return -EFAULT;
-
- /*
- * Now check that we can write the firmware version number too...
- */
- if ( !access_ok(VERIFY_WRITE, &bb->version, sizeof(bb->version)) )
- return -EFAULT;
-
- err = sscape_upload_bootblock(sscape, bb);
- }
- break;
-
- case SND_SSCAPE_LOAD_MCODE:
- {
- register const struct sscape_microcode __user *mc = (const struct sscape_microcode __user *) arg;
-
- err = sscape_upload_microcode(sscape, mc);
- }
- break;
-
- default:
- err = -EINVAL;
- break;
- } /* switch */
+ release_firmware(init_fw);
return err;
}
-
/*
* Mixer control for the SoundScape's MIDI device.
*/
@@ -920,7 +805,7 @@ static int mpu401_open(struct snd_mpu401 * mpu)
spin_lock_irqsave(&sscape->fwlock, flags);
- if (sscape->hw_in_use || (sscape->midi_usage == ULONG_MAX)) {
+ if (sscape->midi_usage == ULONG_MAX) {
err = -EBUSY;
} else {
++(sscape->midi_usage);
@@ -1053,13 +938,6 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
}
}
- strcpy(card->driver, "SoundScape");
- strcpy(card->shortname, pcm->name);
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
- pcm->name, chip->port, chip->irq,
- chip->dma1, chip->dma2);
-
sscape->chip = chip;
}
@@ -1162,29 +1040,6 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
return -ENXIO;
}
- if (sscape->type != SSCAPE_VIVO) {
- /*
- * Now create the hardware-specific device so that we can
- * load the microcode into the on-board processor.
- * We cannot use the MPU-401 MIDI system until this firmware
- * has been loaded into the card.
- */
- err = snd_hwdep_new(card, "MC68EC000", 0, &(sscape->hw));
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "firmware device\n");
- goto _release_dma;
- }
- strlcpy(sscape->hw->name, "SoundScape M68K",
- sizeof(sscape->hw->name));
- sscape->hw->name[sizeof(sscape->hw->name) - 1] = '\0';
- sscape->hw->iface = SNDRV_HWDEP_IFACE_SSCAPE;
- sscape->hw->ops.open = sscape_hw_open;
- sscape->hw->ops.release = sscape_hw_release;
- sscape->hw->ops.ioctl = sscape_hw_ioctl;
- sscape->hw->private_data = sscape;
- }
-
/*
* Tell the on-board devices where their resources are (I think -
* I can't be sure without a datasheet ... So many magic values!)
@@ -1222,28 +1077,56 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
wss_port[dev], irq[dev]);
goto _release_dma;
}
+ strcpy(card->driver, "SoundScape");
+ strcpy(card->shortname, name);
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
+ name, sscape->chip->port, sscape->chip->irq,
+ sscape->chip->dma1, sscape->chip->dma2);
+
#define MIDI_DEVNUM 0
if (sscape->type != SSCAPE_VIVO) {
- err = create_mpu401(card, MIDI_DEVNUM, port[dev], mpu_irq[dev]);
- if (err < 0) {
- printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%lx\n",
- port[dev]);
- goto _release_dma;
- }
+ err = sscape_upload_bootblock(card);
+ if (err >= 0)
+ err = sscape_upload_microcode(card, err);
- /*
- * Enable the master IRQ ...
- */
- sscape_write(sscape, GA_INTENA_REG, 0x80);
-
- /*
- * Initialize mixer
- */
- sscape->midi_vol = 0;
- host_write_ctrl_unsafe(sscape->io_base, CMD_SET_MIDI_VOL, 100);
- host_write_ctrl_unsafe(sscape->io_base, 0, 100);
- host_write_ctrl_unsafe(sscape->io_base, CMD_XXX_MIDI_VOL, 100);
+ if (err == 0) {
+ err = create_mpu401(card, MIDI_DEVNUM, port[dev],
+ mpu_irq[dev]);
+ if (err < 0) {
+ printk(KERN_ERR "sscape: Failed to create "
+ "MPU-401 device at 0x%lx\n",
+ port[dev]);
+ goto _release_dma;
+ }
+
+ /*
+ * Enable the master IRQ ...
+ */
+ sscape_write(sscape, GA_INTENA_REG, 0x80);
+
+ /*
+ * Initialize mixer
+ */
+ spin_lock_irqsave(&sscape->lock, flags);
+ sscape->midi_vol = 0;
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_XXX_MIDI_VOL, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ sscape->midi_vol, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ CMD_SET_EXTMIDI, 100);
+ host_write_ctrl_unsafe(sscape->io_base,
+ 0, 100);
+ host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
+
+ set_midi_mode_unsafe(sscape->io_base);
+ spin_unlock_irqrestore(&sscape->lock, flags);
+ }
}
/*
@@ -1301,11 +1184,12 @@ static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
sscape->type = SSCAPE;
dma[dev] &= 0x03;
+ snd_card_set_dev(card, pdev);
+
ret = create_sscape(dev, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, pdev);
if ((ret = snd_card_register(card)) < 0) {
printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
@@ -1426,12 +1310,12 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
wss_port[idx] = pnp_port_start(dev, 1);
dma2[idx] = pnp_dma(dev, 1);
}
+ snd_card_set_dev(card, &pcard->card->dev);
ret = create_sscape(idx, card);
if (ret < 0)
goto _release_card;
- snd_card_set_dev(card, &pcard->card->dev);
if ((ret = snd_card_register(card)) < 0) {
printk(KERN_ERR "sscape: Failed to register sound card\n");
goto _release_card;
--
1.6.0.3
----------------------------------------------------------------------
Nie dla nich ciepla praca za biurkiem.
Sprawdz >>> http://link.interia.pl/f2383
2
1
01 Oct '09
The DMA params for McASP with FIFO has been updated so that it works for
various FIFO levels. A member- 'fifo_level' has been added to the DMA
params data structure. The fifo_level can be adjusted by the tx[rx]_numevt
platform data. This is relevant only for DA8xx/OMAP-L1xx platforms. This
implementation has been tested for numevt values 1, 2, 4, 8.
Signed-off-by: Chaithrika U S <chaithrika(a)ti.com>
---
Applies to ALSA GIT tree on branch topic/asoc at
http://git.kernel.org/?p=linux/kernel/git/tiwai/sound-2.6.git;a=shortlog;
h=topic/asoc
sound/soc/davinci/davinci-i2s.c | 2 ++
sound/soc/davinci/davinci-mcasp.c | 17 +++++++----------
sound/soc/davinci/davinci-pcm.c | 21 ++++++++++++++++++---
sound/soc/davinci/davinci-pcm.h | 1 +
4 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 4ae7070..2ab8093 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -397,6 +397,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
}
dma_params->acnt = dma_params->data_type;
+ dma_params->fifo_level = 0;
+
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 5d1f98a..50ad051 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
int word_length;
- u8 numevt;
+ u8 fifo_level;
davinci_hw_common_param(dev, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- numevt = dev->txnumevt;
+ fifo_level = dev->txnumevt;
else
- numevt = dev->rxnumevt;
-
- if (!numevt)
- numevt = 1;
+ fifo_level = dev->rxnumevt;
if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (dev->version == MCASP_VERSION_2) {
- dma_params->data_type *= numevt;
- dma_params->acnt = 4 * numevt;
- } else
+ if (dev->version == MCASP_VERSION_2 && !fifo_level)
+ dma_params->acnt = 4;
+ else
dma_params->acnt = dma_params->data_type;
+ dma_params->fifo_level = fifo_level;
davinci_config_channel_size(dev, word_length);
return 0;
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 359e99e..1152d8b 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -66,38 +66,53 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
dma_addr_t dma_pos;
dma_addr_t src, dst;
unsigned short src_bidx, dst_bidx;
+ unsigned short src_cidx, dst_cidx;
unsigned int data_type;
unsigned short acnt;
unsigned int count;
+ unsigned int fifo_level;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
+ fifo_level = prtd->params->fifo_level;
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
+ if (fifo_level)
+ count /= fifo_level;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
dst_bidx = 0;
+ src_cidx = data_type * fifo_level;
+ dst_cidx = 0;
} else {
src = prtd->params->dma_addr;
dst = dma_pos;
src_bidx = 0;
dst_bidx = data_type;
+ src_cidx = 0;
+ dst_cidx = data_type * fifo_level;
}
acnt = prtd->params->acnt;
edma_set_src(lch, src, INCR, W8BIT);
edma_set_dest(lch, dst, INCR, W8BIT);
- edma_set_src_index(lch, src_bidx, 0);
- edma_set_dest_index(lch, dst_bidx, 0);
- edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+
+ edma_set_src_index(lch, src_bidx, src_cidx);
+ edma_set_dest_index(lch, dst_bidx, dst_cidx);
+
+ if (!fifo_level)
+ edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+ else
+ edma_set_transfer_params(lch, acnt, fifo_level, count,
+ fifo_level, ABSYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8746606..c8b0d2b 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -23,6 +23,7 @@ struct davinci_pcm_dma_params {
enum dma_event_q eventq_no; /* event queue number */
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
+ unsigned int fifo_level;
};
--
1.5.6
3
3
30 Sep '09
Hi,
I have installed 2.6.32-rc1 (well -rc2...)
And I notice that 'Input source' is missing.
/proc/asound/Intel/codec#0 attached.
Best regards,
Maxim Levitsky
2
7
It allows switching audio settings between scenarios or uses-cases like
listening to music and answering an incoming phone call. Made of control
aliasing for playback, capture master and switch as well as the option to
post- and prefix a sequence of control changes avoiding pops and other
unwanted noise. Some example programs will be available in alsa-utils.
CC: Ian Molton <ian(a)mnementh.co.uk>
CC: Graeme Gregory <gg(a)slimlogic.co.uk>
Signed-off-by: Liam Girdwood <lrg(a)slimlogic.co.uk>
Signed-off-by: Stefan Schmidt <stefan(a)slimlogic.co.uk>
---
include/Makefile.am | 2 +-
include/ascenario.h | 170 +++++++
src/Makefile.am | 2 +-
src/ascenario.c | 1360 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1532 insertions(+), 2 deletions(-)
create mode 100644 include/ascenario.h
create mode 100644 src/ascenario.c
diff --git a/include/Makefile.am b/include/Makefile.am
index a291503..572fbc9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -3,7 +3,7 @@ SUBDIRS = sound
sysincludedir = ${includedir}/sys
alsaincludedir = ${includedir}/alsa
-alsainclude_HEADERS = asoundlib.h asoundef.h \
+alsainclude_HEADERS = asoundlib.h asoundef.h ascenario.h \
version.h global.h input.h output.h error.h \
conf.h control.h iatomic.h
diff --git a/include/ascenario.h b/include/ascenario.h
new file mode 100644
index 0000000..869f2ea
--- /dev/null
+++ b/include/ascenario.h
@@ -0,0 +1,170 @@
+/*
+* ALSA Scenario header file
+*
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Copyright (C) 2008-2009 SlimLogic Ltd
+* Authors: Liam Girdwood <lrg(a)slimlogic.co.uk>
+* Stefan Schmidt <stefan(a)slimlogic.co.uk>
+*/
+
+/**
+ * Scenario IDs
+ *
+ * Standard Scenario IDs - Add new scenarios at the end.
+ */
+
+#define SND_SCN_PLAYBACK_SPEAKER "playback speaker"
+#define SND_SCN_PLAYBACK_HEADPHONES "playback headphone"
+#define SND_SCN_PLAYBACK_HEADSET "playback headset"
+#define SND_SCN_PLAYBACK_BLUETOOTH "playback bluetooth"
+#define SND_SCN_PLAYBACK_HANDSET "playback handset"
+#define SND_SCN_PLAYBACK_GSM "playback gsm"
+#define SND_SCN_PLAYBACK_LINE "playback line"
+
+#define SND_SCN_CAPTURE_MIC "capture mic"
+#define SND_SCN_CAPTURE_LINE "capture line"
+#define SND_SCN_CAPTURE_HEADSET "capture headset"
+#define SND_SCN_CAPTURE_HANDSET "capture handset"
+#define SND_SCN_CAPTURE_BLUETOOTH "capture bluetooth"
+#define SND_SCN_CAPTURE_GSM "capture gsm"
+
+#define SND_SCN_PHONECALL_GSM_HANDSET "phonecall gsm handset"
+#define SND_SCN_PHONECALL_BT_HANDSET "phonecall bt handset"
+#define SND_SCN_PHONECALL_IP_HANDSET "phonecall ip handset"
+#define SND_SCN_PHONECALL_GSM_HEADSET "phonecall gsm headset"
+#define SND_SCN_PHONECALL_BT_HEADSET "phonecall bt headset"
+#define SND_SCN_PHONECALL_IP_HEADSET "phonecall ip headset"
+
+/**
+ * QOS
+ *
+ * Defines Audio Quality of Service. Systems supporting different types of QoS
+ * often have lower power consumption on lower quality levels.
+ */
+#define SND_POWER_QOS_HIFI 0
+#define SND_POWER_QOS_VOICE 1
+#define SND_POWER_QOS_SYSTEM 2
+
+struct snd_scenario;
+
+/* TODO: add notification */
+
+/**
+ * snd_scenario_list - list supported scenarios
+ * @scn: scenario
+ * @list: list of supported scenario names.
+ *
+ * List supported scenarios for this sound card.
+ * Returns number of scenarios.
+ */
+int snd_scenario_list(struct snd_scenario *scn, const char **list[]);
+
+/**
+ * snd_scenario_set_scn - set scenario
+ * @scn: scenario
+ * @scenario: scenario name
+ *
+ * Set new scenario for sound card.
+ */
+int snd_scenario_set_scn(struct snd_scenario *scn, const char *scenario);
+
+/**
+ * snd_scenario_get_scn - get scenario
+ * @scn: scenario
+ *
+ * Get current sound card scenario.
+ */
+const char *snd_scenario_get_scn(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_playback_volume - get playback volume
+ * @scn: scenario
+ *
+ * Get the master playback volume control name for the current scenario.
+ */
+int snd_scenario_get_master_playback_volume(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_playback_switch - get playback switch
+ * @scn: scenario
+ *
+ * Get the master playback switch control name for the current scenario.
+ */
+ int snd_scenario_get_master_playback_switch(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_capture_volume - get capture volume
+ * @scn: scenario
+ *
+ * Get the master capture volume control name for the current scenario.
+ */
+int snd_scenario_get_master_capture_volume(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_capture_switch - get capture switch
+ * @scn: scenario
+ *
+ * Get the master capture switch control name for the current scenario.
+ */
+int snd_scenario_get_master_capture_switch(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_set_qos - set qos
+ * @qos: qos
+ *
+ * Set Quality of Service for this scenario.
+ */
+int snd_scenario_set_qos(struct snd_scenario *scn, int qos);
+
+/**
+ * snd_scenario_get_qos - get qos
+ * @scn: scenario
+ *
+ * Get Quality of Service for this scenario.
+ */
+int snd_scenario_get_qos(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_open - open scenario core
+ * @card_name: sound card name.
+ *
+ * Open scenario manager core for sound card.
+ */
+struct snd_scenario *snd_scenario_open(const char *card_name);
+
+/**
+ * snd_scenario_reload - reload and reparse scenario configuration
+ * @scn: scenario
+ *
+ * Reloads and reparses sound card scenario configuration.
+ */
+int snd_scenario_reload(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_close - close scenario core
+ * @scn: scenario
+ *
+ * Free scenario manager core for sound card.
+ */
+void snd_scenario_close(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_dump - dump
+ * @card_name: sound card name.
+ *
+ * Dump current sound card settings to stdout in scn format.
+ */
+int snd_scenario_dump(const char *card_name);
diff --git a/src/Makefile.am b/src/Makefile.am
index 3204fe4..be46cb3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,7 +14,7 @@ SYMFUNCS =
endif
lib_LTLIBRARIES = libasound.la
-libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c
+libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c ascenario.c
SUBDIRS=control
libasound_la_LIBADD = control/libcontrol.la
diff --git a/src/ascenario.c b/src/ascenario.c
new file mode 100644
index 0000000..99f239e
--- /dev/null
+++ b/src/ascenario.c
@@ -0,0 +1,1360 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2008-2009 SlimLogic Ltd
+ * Authors: Liam Girdwood <lrg(a)slimlogic.co.uk>
+ * Stefan Schmidt <stefan(a)slimlogic.co.uk>
+ */
+
+#define _GNU_SOURCE /* needed of O_NOATIME */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <alsa/asoundlib.h>
+
+#include "../include/ascenario.h"
+
+#define PRE_SEQ 0
+#define POST_SEQ 1
+#define MAX_SCN 32
+#define MAX_NAME 64
+#define MAX_FILE 256
+#define MAX_BUF 256
+#define ALSA_SCN_DIR "/etc/alsa/scenario"
+
+/*
+ * Stores all scenario settings for 1 kcontrol. Hence we have a
+ * control_settings for each kcontrol in card.
+ */
+struct control_settings {
+ char name[MAX_NAME];
+ int id;
+ snd_ctl_elem_type_t type;
+ int count; /* 1 = mono, 2 = stereo, etc */
+ unsigned short *value; /* kcontrol value 2D array */
+};
+
+/*
+ * If sleep is 0 the element contains the settings in control. Else sleep
+ * contains the sleep time in micro seconds.
+ */
+struct sequence_element {
+ unsigned int sleep; /* Sleep time in msecs if sleep element, else 0 */
+ struct control_settings *control;
+ struct sequence_element *next; /* Pointer to next list element */
+};
+
+/*
+ * Describes default mixers and qos for scenario.
+ * We have a scenario_info for each scenario loaded.
+ */
+struct scenario_info {
+ char *name;
+ char *file;
+ char *pre_sequence_file;
+ char *post_sequence_file;
+ short playback_volume_id;
+ short playback_switch_id;
+ short capture_volume_id;
+ short capture_switch_id;
+ int qos;
+};
+
+/* Describe a snd card and all its scenarios.
+ */
+struct snd_scenario {
+ char *card_name;
+ int current_scenario;
+ int num_scenarios; /* number of supported scenarios */
+ int num_kcontrols; /* number of kcontrols */
+ struct sequence_element *pre_seq_list; /* Linked list for pre sequence */
+ struct sequence_element *post_seq_list; /* Linked list for post sequence */
+ const char **list;
+ struct scenario_info *scenario; /* var len array of scenario info */
+ struct control_settings *control; /* var len array of controls */
+};
+
+static void scn_error(const char *fmt,...)
+{
+ va_list va;
+ va_start(va, fmt);
+ fprintf(stderr, "scenario: ");
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+}
+
+static void scn_stdout(const char *fmt,...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+}
+
+static inline void set_value(struct snd_scenario *scn,
+ struct control_settings *control, int count, unsigned short val)
+{
+ int offset = scn->current_scenario * control->count;
+ control->value[offset + count] = val;
+}
+
+static inline unsigned short get_value(struct snd_scenario *scn,
+ struct control_settings *control, int count)
+{
+ int offset = scn->current_scenario * control->count;
+ return control->value[offset + count];
+}
+
+static int dump_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
+{
+ int err, count, i;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_type_t type;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ scn_stdout("%s: failed to get ctl info\n");
+ return err;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ type = snd_ctl_elem_info_get_type(info);
+ count = snd_ctl_elem_info_get_count(info);
+ if (count == 0)
+ return 0;
+
+ scn_stdout("%u:'%s':%d:",
+ snd_ctl_elem_id_get_numid(id),
+ snd_ctl_elem_id_get_name(id), count);
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_boolean(control, i));
+ scn_stdout("%d", snd_ctl_elem_value_get_boolean(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_integer(control, i));
+ scn_stdout("%d", snd_ctl_elem_value_get_integer(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%ld,",
+ snd_ctl_elem_value_get_integer64(control, i));
+ scn_stdout("%ld",
+ snd_ctl_elem_value_get_integer64(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_enumerated(control, i));
+ scn_stdout("%d",
+ snd_ctl_elem_value_get_enumerated(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%2.2x,",
+ snd_ctl_elem_value_get_byte(control, i));
+ scn_stdout("%2.2x", snd_ctl_elem_value_get_byte(control, i));
+ break;
+ default:
+ break;
+ }
+ scn_stdout("\n");
+ return 0;
+}
+
+/*
+ * Add new kcontrol from sound card into memory database.
+ */
+static int add_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
+ struct control_settings *control_settings)
+{
+ int err;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ scn_stdout("%s: failed to get ctl info\n");
+ return err;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ strncpy(control_settings->name, snd_ctl_elem_id_get_name(id),
+ MAX_NAME);
+ control_settings->count = snd_ctl_elem_info_get_count(info);
+ control_settings->type = snd_ctl_elem_info_get_type(info);
+ control_settings->id = snd_ctl_elem_id_get_numid(id);
+ return 0;
+}
+
+static int parse_controls(struct snd_scenario *scn, FILE *f)
+{
+ struct control_settings *control;
+ char buf[MAX_BUF], name[MAX_NAME];
+ int id, count, line = 1, i;
+ char *name_start, *name_end, *tbuf;
+
+ while (fgets(buf, MAX_BUF, f) != NULL) {
+
+ /* get id */
+ tbuf = buf;
+ id = atoi(tbuf);
+ if (id == 0) {
+ scn_error("%s:id == 0 on line %d\n", __func__, line);
+ return -EINVAL;
+ }
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ if (id == scn->control[i].id) {
+ control = &scn->control[i];
+ goto get_name;
+ }
+ }
+ scn_error("%s:id not found at line %d\n", __func__, line);
+ return -EINVAL;
+get_name:
+ /* get name start */
+ while (*tbuf != 0 && *tbuf != '\'')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ name_start = ++tbuf;
+
+ /* get name end */
+ while (*tbuf != 0 && *tbuf != '\'')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ name_end = tbuf++;
+
+ /* copy name */
+ if ((name_end - name_start) > MAX_NAME) {
+ scn_error("%s:name too big at %d chars line %d\n",
+ __func__, name_end - name_start, line);
+ return -EINVAL;
+ }
+ strncpy(name, name_start, name_end - name_start);
+ name[name_end - name_start] = 0;
+ if (strcmp(name, control->name)) {
+ scn_error("%s: name %s and %s don't match at line %d\n",
+ __func__, name, control->name, line);
+ return -EINVAL;
+ }
+
+ /* get count */
+ tbuf++;
+ count = atoi(tbuf);
+ if (count == 0) {
+ scn_error("%s:count == 0 on line %d\n", __func__,
+ line);
+ return -EINVAL;
+ }
+ if (count != control->count) {
+ scn_error("%s:count does not match at line %d\n",
+ __func__, line);
+ return -EINVAL;
+ }
+
+ /* get vals */
+ control->value = malloc(control->count * scn->num_scenarios *
+ sizeof(unsigned short));
+ if (control->value == NULL)
+ return -ENOMEM;
+
+ while (*tbuf != 0 && *tbuf != ':')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ tbuf++;
+
+ for (i = 0; i < count; i++) {
+ set_value(scn, control, i, atoi(tbuf));
+ while (*tbuf != 0 && *tbuf != ',')
+ tbuf++;
+
+ if (*tbuf++ == 0 && i < (count - 1))
+ return -EINVAL;
+ }
+ line++;
+ }
+
+ return 0;
+}
+
+static char *get_string (char *buf)
+{
+ char *str, *end;
+
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return NULL;
+ }
+
+ /* find leading '"' */
+ buf++;
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing start '\"'\n", __func__);
+ return NULL;
+ }
+ str = ++buf;
+
+ /* get value */
+ while (*buf != 0 && *buf != '"')
+ buf++;
+ end = buf;
+
+ /* find '"' terminator */
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing terminator '\"' %s\n", __func__, buf);
+ return NULL;
+ }
+
+ *end = 0;
+ return strdup(str);
+}
+
+static char *get_control_name (char *buf)
+{
+ char *str, *end;
+
+ /* find leading '"' */
+ buf++;
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing start '\"'\n", __func__);
+ return NULL;
+ }
+ str = ++buf;
+
+ /* get value */
+ while (*buf != 0 && *buf != '"')
+ buf++;
+ end = buf;
+
+ /* find '"' terminator */
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing terminator '\"' %s\n", __func__, buf);
+ return NULL;
+ }
+
+ *end = 0;
+ return strdup(str);
+}
+
+static int get_int (char *buf)
+{
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return -EINVAL;
+ }
+ buf++;
+ return atoi(buf);
+}
+
+static int get_enum (char *buf)
+{
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return -EINVAL;
+ }
+ buf++;
+ return 0; /* TODO */
+}
+
+static void seq_list_append(struct snd_scenario *scn,
+ struct sequence_element *curr, int position)
+{
+ struct sequence_element *last, *tmp;
+
+ if (position) {
+ if (!scn->post_seq_list)
+ scn->post_seq_list = curr;
+
+ else {
+ tmp = scn->post_seq_list;
+ while (tmp) {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ last->next = curr;
+ }
+ }
+ else {
+ if (!scn->pre_seq_list) {
+ scn->pre_seq_list = curr;
+ }
+ else {
+ tmp = scn->pre_seq_list;
+ while (tmp) {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ last->next = curr;
+ }
+ }
+}
+
+static int parse_sequences(struct snd_scenario *scn, FILE *f, int position)
+{
+ char buf[MAX_BUF], *tbuf, *control_value;
+ int control_len, i;
+ struct sequence_element *curr;
+
+ while (fgets(buf, MAX_BUF, f) != NULL) {
+
+ /* Check for lines with comments and ignore */
+ if (buf[0] == '#')
+ continue;
+
+ /* Parse current line and skip blanks */
+ tbuf = buf;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ curr = malloc(sizeof(struct sequence_element));
+ if (curr == NULL)
+ return -ENOMEM;
+ bzero(curr, sizeof(struct sequence_element));
+
+ curr->control = malloc(sizeof(struct control_settings));
+ if (curr->control == NULL)
+ return -ENOMEM;
+ bzero(curr->control, sizeof(struct control_settings));
+
+ curr->control->value = malloc(curr->control->count * scn->num_scenarios
+ * sizeof(unsigned short));
+ if (curr->control->value == NULL)
+ return -ENOMEM;
+ bzero(curr->control->value, curr->control->count * scn->num_scenarios
+ * sizeof(unsigned short));
+
+ if (strncmp(tbuf, "kcontrol", 8) == 0) {
+ strncpy(curr->control->name, get_control_name(tbuf + 8), MAX_NAME);
+ control_len = strlen(curr->control->name);
+ /* 11 = 8 from kcontrol + 2 quotes + 1 blank */
+ control_value = get_string(tbuf + 11 + control_len);
+
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ if (strncmp(curr->control->name, scn->control[i].name,
+ control_len) == 0) {
+ curr->sleep = 0;
+ curr->control->id = scn->control[i].id;
+ curr->control->type = scn->control[i].type;
+ curr->control->count = scn->control[i].count;
+ set_value(scn, curr->control, curr->control->count,
+ atoi(control_value));
+ seq_list_append(scn, curr, position);
+ }
+ }
+
+ continue;
+ }
+
+ if (strncmp(tbuf, "msleep", 6) == 0) {
+ curr->sleep = get_int(tbuf + 6);
+
+ /* Free control elements as we only have a sleep element
+ * here */
+ if (curr->control) {
+ if (curr->control->value)
+ free(curr->control->value);
+ free(curr->control);
+ }
+
+ seq_list_append(scn, curr, position);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* load scenario i */
+static int read_scenario_file(struct snd_scenario *scn)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+ struct scenario_info *info = &scn->scenario[scn->current_scenario];
+
+ snprintf(filename, MAX_FILE, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->file);
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ scn_error("%s: couldn't open %s\n", __func__, filename);
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = parse_controls(scn, f);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+static int read_sequence_file(struct snd_scenario *scn, int position)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+ struct scenario_info *info = &scn->scenario[scn->current_scenario];
+
+ if (position == PRE_SEQ) {
+ sprintf(filename, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->pre_sequence_file);
+ }
+ else {
+ sprintf(filename, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->post_sequence_file);
+ }
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = parse_sequences(scn, f, position);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+static int parse_scenario(struct snd_scenario *scn, FILE *f, int line_)
+{
+ struct scenario_info *info;
+ int line = line_ - 1, id = 0, file = 0, len, offset;
+ char buf[MAX_BUF], *tbuf;
+
+ offset = sizeof(scn->scenario);
+ scn->scenario = realloc(scn->scenario,
+ (scn->num_scenarios + 1) * sizeof(struct scenario_info));
+ len = sizeof(scn->scenario) - offset;
+ if (scn->scenario == NULL)
+ return -ENOMEM;
+ bzero(scn->scenario + offset, len);
+ info = scn->scenario + scn->num_scenarios;
+
+ /* Set sequence filename to NULL as it is optional and we want to check
+ * for NULL to avoid segfaults */
+ info->pre_sequence_file = NULL;
+ info->post_sequence_file = NULL;
+
+ while(fgets(buf, MAX_BUF, f) != NULL) {
+
+ line++;
+ if (buf[0] == '#')
+ continue;
+
+ tbuf = buf;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ if (strncmp(tbuf, "Identifier", 10) == 0) {
+ info->name = get_string(tbuf + 10);
+ if (info->name == NULL) {
+ scn_error("%s: failed to get Identifer\n",
+ __func__);
+ goto err;
+ }
+ id = 1;
+ continue;
+ }
+
+ if (strncmp(tbuf, "File", 4) == 0) {
+ info->file = get_string(tbuf + 4);
+ if (info->file == NULL) {
+ scn_error("%s: failed to get File\n",
+ __func__);
+ goto err;
+ }
+ file = 1;
+ continue;
+ }
+
+ if (strncmp(tbuf, "QoS", 3) == 0) {
+ info->qos = get_enum(tbuf + 3);
+ if (info->qos < 0) {
+ scn_error("%s: failed to get QoS\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "Master Playback Volume", 22) == 0) {
+ info->playback_volume_id = get_int(tbuf + 22);
+ if (info->playback_volume_id < 0) {
+ scn_error("%s: failed to get Master Playback Volume\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "Master Playback Switch", 22) == 0) {
+ info->playback_switch_id = get_int(tbuf + 22);
+ if (info->playback_switch_id < 0) {
+ scn_error("%s: failed to get Master Playback Switch\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "Master Capture Volume", 21) == 0) {
+ info->capture_volume_id = get_int(tbuf + 21);
+ if (info->capture_volume_id < 0) {
+ scn_error("%s: failed to get Master Capture Volume\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "Master Capture Switch", 21) == 0) {
+ info->capture_switch_id = get_int(tbuf + 21);
+ if (info->capture_switch_id < 0) {
+ scn_error("%s: failed to get Master Capture Switch\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "PreSequenceFile", 15) == 0) {
+ info->pre_sequence_file = get_string(tbuf + 15);
+ if (info->pre_sequence_file == NULL) {
+ scn_error("%s: failed to get PreSequenceFile\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "PostSequenceFile", 16) == 0) {
+ info->post_sequence_file = get_string(tbuf + 16);
+ if (info->post_sequence_file == NULL) {
+ scn_error("%s: failed to get PostSequenceFile\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "EndSection", 10) == 0) {
+ break;
+ }
+ }
+
+ if (file & id) {
+ scn->num_scenarios++;
+ return 0;
+ }
+err:
+ if (file) {
+ free(info->file);
+ info->file = NULL;
+ }
+ if (id) {
+ free(info->name);
+ info->name = NULL;
+ }
+ return -EINVAL;
+}
+
+static int read_master_file(struct snd_scenario *scn, FILE *f)
+{
+ int line = 0, ret = 0, i;
+ char buf[MAX_BUF], *tbuf;
+
+ /* parse master config sections */
+ while(fgets(buf, MAX_BUF, f) != NULL) {
+
+ if (buf[0] == '#') {
+ line++;
+ continue;
+ }
+
+ if (strncmp(buf, "Section", 7) == 0) {
+
+ tbuf = buf + 7;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ if (strncmp(tbuf, "\"Scenario\"", 10) == 0) {
+ line = parse_scenario(scn, f, line);
+ if (line < 0) {
+ scn_error("%s: failed to parse "
+ "scenario\n", __func__);
+ goto err;
+ }
+ continue;
+ }
+ }
+ line++;
+ }
+
+ /* copy ptrs to scenario names */
+ scn->list = malloc(scn->num_scenarios * sizeof(char *));
+ if (scn->list == NULL)
+ ret = -ENOMEM;
+ for (i = 0; i < scn->num_scenarios; i++)
+ scn->list[i] = scn->scenario[i].name;
+
+err:
+ if (ferror(f)) {
+ scn_error("%s: failed to read master\n", __func__);
+ return ferror(f);
+ }
+ return ret;
+}
+
+/* load scenario i */
+static int import_master_config(struct snd_scenario *scn)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+
+ sprintf(filename, "%s/%s.conf", ALSA_SCN_DIR, scn->card_name);
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ scn_error("%s: couldn't open %s\n", __func__, filename);
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = read_master_file(scn, f);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+/* parse_card_controls
+ * @scn: scenario
+ *
+ * Parse sound card and store control data in memory db.
+ */
+static int parse_card_controls(struct snd_scenario *scn)
+{
+ struct control_settings *control;
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ int ret, i;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* open and load snd card */
+ ret = snd_ctl_open(&handle, scn->card_name, SND_CTL_READONLY);
+ if (ret < 0) {
+ scn_error("%s: control %s open retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ scn->num_kcontrols = snd_ctl_elem_list_get_count(list);
+ if (scn->num_kcontrols < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, scn->num_kcontrols) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* allocate db memory for controls */
+ scn->control = calloc(scn->num_kcontrols,
+ sizeof(struct control_settings));
+ if (scn->control == NULL) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ control = scn->control;
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < scn->num_kcontrols; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = add_control(handle, id, control++);
+ if (ret < 0) {
+ scn_error("%s: failed to add control error %s\n",
+ __func__, snd_strerror(ret));
+ goto close;
+ }
+ }
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+/* import_scenario_files -
+ * @scn: scenario
+ *
+ * Read and parse scenario_info files the store in memory.
+ */
+static int import_scenario_files(struct snd_scenario *scn)
+{
+ int ret;
+
+ ret = import_master_config(scn);
+ if (ret < 0) {
+ scn_error("%s: failed to parse master scenario config\n",
+ __func__);
+ return ret;
+ }
+
+ for (scn->current_scenario = 0;
+ scn->current_scenario < scn->num_scenarios;
+ scn->current_scenario++) {
+
+ ret = read_scenario_file(scn);
+ if (ret < 0) {
+ scn_error("%s: failed to parse scenario %s\n",
+ __func__,
+ scn->scenario[scn->current_scenario].name);
+ scn->current_scenario = -1;
+ return ret;
+ }
+
+ if (scn->scenario[scn->current_scenario].pre_sequence_file != NULL) {
+ ret = read_sequence_file(scn, PRE_SEQ);
+ if (ret < 0) {
+ scn_stdout("Warning: PreSequence file defined but"
+ " missing in scenario \"%s\"\n",
+ scn->scenario[scn->current_scenario].name);
+ }
+ }
+
+ if (scn->scenario[scn->current_scenario].post_sequence_file != NULL) {
+ ret = read_sequence_file(scn, POST_SEQ);
+ if (ret < 0) {
+ scn_stdout("Warning: PostSequence file defined but"
+ " missing in scenario \"%s\"\n",
+ scn->scenario[scn->current_scenario].name);
+ }
+ }
+
+ }
+ return 0;
+}
+
+/* free all resorces */
+static void free_scn(struct snd_scenario *scn)
+{
+ /* TODO: valgrind to make sure. */
+ int i;
+
+ if (scn == NULL)
+ return;
+
+ if (scn->control) {
+ if (scn->control->value)
+ free(scn->control->value);
+ free(scn->control);
+ }
+
+ if (scn->list)
+ free(scn->list);
+ if (scn->card_name)
+ free(scn->card_name);
+ if (scn->pre_seq_list)
+ free(scn->pre_seq_list);
+ if (scn->post_seq_list)
+ free(scn->post_seq_list);
+
+ if (scn->scenario) {
+ for (i = 0; i < scn->num_scenarios; i++) {
+ struct scenario_info *info = &scn->scenario[i];
+
+ if (info->name)
+ free(info->name);
+ if (info->file)
+ free(info->file);
+ if (info->pre_sequence_file)
+ free(info->pre_sequence_file);
+ if (info->post_sequence_file)
+ free(info->post_sequence_file);
+ }
+ free(scn->scenario);
+ }
+ free(scn);
+}
+
+/*
+ * Init sound card scenario db.
+ */
+struct snd_scenario *snd_scenario_open(const char *card_name)
+{
+ struct snd_scenario *scn;
+ int err;
+
+ /* TODO: locking and
+ * check if card_name scn is already loaded,
+ * if so reuse to conserve ram. */
+
+ scn = malloc(sizeof(struct snd_scenario));
+ if (scn == NULL)
+ return NULL;
+ bzero(scn, sizeof(struct snd_scenario));
+ scn->card_name = strdup(card_name);
+ if (scn->card_name == NULL) {
+ free(scn);
+ return NULL;
+ }
+
+ /* get info about sound card */
+ err = parse_card_controls(scn);
+ if (err < 0) {
+ free_scn(scn);
+ return NULL;
+ }
+
+ /* get info on scenarios and verify against card */
+ err = import_scenario_files(scn);
+ if (err < 0) {
+ free_scn(scn);
+ return NULL;
+ }
+
+ return scn;
+}
+
+/*
+ * Reload and reparse scenario db.
+ */
+int snd_scenario_reload(struct snd_scenario *scn)
+{
+ free_scn(scn);
+
+ scn->num_kcontrols = parse_card_controls(scn);
+ if (scn->num_kcontrols <= 0) {
+ free_scn(scn);
+ return -EINVAL;
+ }
+
+ scn->num_scenarios = import_scenario_files(scn);
+ if (scn->num_scenarios <= 0) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void snd_scenario_close(struct snd_scenario *scn)
+{
+ free_scn(scn);
+}
+
+static int set_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
+ struct snd_scenario *scn)
+{
+ struct control_settings *setting;
+ int ret, count, i, idnum;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_type_t type;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ ret = snd_ctl_elem_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s: failed to get ctl info\n", __func__);
+ return ret;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ idnum = snd_ctl_elem_id_get_numid(id);
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ setting = &scn->control[i];
+ if (setting->id == idnum)
+ goto set_val;
+ }
+ scn_error("%s: failed to find control %d\n", __func__, idnum);
+ return 0;
+
+set_val:
+ type = snd_ctl_elem_info_get_type(info);
+ count = snd_ctl_elem_info_get_count(info);
+ if (count == 0)
+ return 0;
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_boolean(control, i,
+ get_value(scn, setting, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_integer(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_integer64(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_enumerated(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_byte(control, i,
+ get_value(scn, setting, i));
+ break;
+ default:
+ break;
+ }
+
+ ret = snd_ctl_elem_write(handle, control);
+ if (ret < 0) {
+ scn_error("%s: control %s failed: %s\n", __func__,
+ setting->name, snd_strerror(ret));
+ scn_error("%s: count %d type: %d\n", __func__,
+ count, type);
+ for (i = 0; i < count; i++)
+ fprintf(stderr, "%d ", get_value(scn, setting, i));
+ return ret;
+ }
+ return 0;
+}
+
+static void exec_sequence(struct sequence_element *seq, struct snd_scenario
+ *scn, snd_ctl_elem_list_t *list, snd_ctl_t *handle)
+{
+ int count = snd_ctl_elem_list_get_count(list);
+ while (seq) {
+ if (seq->sleep)
+ usleep(seq->sleep);
+ else {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ int ret, i, numid;
+ /* Where is id lookup from numid if you need it? */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_list_get_id(list, i, id);
+ numid = snd_ctl_elem_id_get_numid(id);
+ if (numid == seq->control->id) {
+ ret = set_control(handle, id, scn);
+ if (ret < 0) {
+ scn_error("%s: failed to set control %s\n",
+ __func__, scn->card_name);
+ }
+ break;
+ }
+ }
+ }
+ seq = seq->next;
+ }
+}
+
+int snd_scenario_set_scn(struct snd_scenario *scn, const char *name)
+{
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ snd_ctl_t *handle;
+ int ret, count, i;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* find scenario name */
+ for (i = 0; i < scn->num_scenarios; i++) {
+ if (!strcmp(scn->scenario[i].name, name))
+ goto found;
+ }
+ scn_error("%s: scenario %s not found\n", __func__, name);
+ return -EINVAL;
+
+found:
+ /* scenario found - now open card */
+ scn->current_scenario = i;
+ ret = snd_ctl_open(&handle, scn->card_name, 0);
+ if (ret) {
+ scn_error("%s: control %s open error: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ count = snd_ctl_elem_list_get_count(list);
+ if (count < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* If we have a sequence list that should be executed before the new
+ * scenario is set do it now */
+ if (scn->pre_seq_list)
+ exec_sequence(scn->pre_seq_list, scn, list, handle);
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = set_control(handle, id, scn);
+ if (ret < 0) {
+ scn_error("%s: failed to set control %s\n", __func__,
+ scn->card_name);
+ }
+ }
+
+ /* If we have a sequence list that should be executed after the new
+ * scenario is set do it now */
+ if (scn->post_seq_list)
+ exec_sequence(scn->post_seq_list, scn, list, handle);
+
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+int snd_scenario_dump(const char *card_name)
+{
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ int ret, i, count;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* open and load snd card */
+ ret = snd_ctl_open(&handle, card_name, SND_CTL_READONLY);
+ if (ret < 0) {
+ scn_error("%s: control %s open retor: %s\n", __func__, card_name,
+ snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ count = snd_ctl_elem_list_get_count(list);
+ if (count < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = dump_control(handle, id);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n",
+ __func__, snd_strerror(ret));
+ goto free;
+ }
+ }
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+const char *snd_scenario_get_scn(struct snd_scenario *scn)
+{
+ if (scn->current_scenario > 0 && scn->current_scenario < MAX_SCN)
+ return scn->scenario[scn->current_scenario].name;
+ else
+ return NULL;
+}
+
+int snd_scenario_set_qos(struct snd_scenario *scn, int qos)
+{
+ /* TODO: change QoS kcontrols */
+ scn->scenario[scn->current_scenario].qos = qos;
+ return 0;
+}
+
+int snd_scenario_get_qos(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].qos;
+}
+
+int snd_scenario_get_master_playback_volume(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].playback_volume_id;
+}
+
+int snd_scenario_get_master_playback_switch(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].playback_switch_id;
+}
+
+int snd_scenario_get_master_capture_volume(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].capture_volume_id;
+}
+
+int snd_scenario_get_master_capture_switch(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].capture_switch_id;
+}
+
+int snd_scenario_list(struct snd_scenario *scn, const char **list[])
+{
+ *list = scn->list;
+ return scn->num_scenarios;
+}
--
1.6.4.3
regards
Stefan Schmidt
1
0
It allows switching audio settings between scenarios or uses-cases like
listening to music and answering an incoming phone call. Made of control
aliasing for playback, capture master and switch as well as the option to
post- and prefix a sequence of control changes avoiding pops and other
unwanted noise. Some example programs will be available in alsa-utils.
CC: Ian Molton <ian(a)mnementh.co.uk>
CC: Graeme Gregory <gg(a)slimlogic.co.uk>
Signed-off-by: Liam Girdwood <lrg(a)slimlogic.co.uk>
Signed-off-by: Stefan Schmidt <stefan(a)slimlogic.co.uk>
---
include/Makefile.am | 2 +-
include/ascenario.h | 170 +++++++
src/Makefile.am | 2 +-
src/ascenario.c | 1358 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1530 insertions(+), 2 deletions(-)
create mode 100644 include/ascenario.h
create mode 100644 src/ascenario.c
diff --git a/include/Makefile.am b/include/Makefile.am
index a291503..572fbc9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -3,7 +3,7 @@ SUBDIRS = sound
sysincludedir = ${includedir}/sys
alsaincludedir = ${includedir}/alsa
-alsainclude_HEADERS = asoundlib.h asoundef.h \
+alsainclude_HEADERS = asoundlib.h asoundef.h ascenario.h \
version.h global.h input.h output.h error.h \
conf.h control.h iatomic.h
diff --git a/include/ascenario.h b/include/ascenario.h
new file mode 100644
index 0000000..869f2ea
--- /dev/null
+++ b/include/ascenario.h
@@ -0,0 +1,170 @@
+/*
+* ALSA Scenario header file
+*
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* Copyright (C) 2008-2009 SlimLogic Ltd
+* Authors: Liam Girdwood <lrg(a)slimlogic.co.uk>
+* Stefan Schmidt <stefan(a)slimlogic.co.uk>
+*/
+
+/**
+ * Scenario IDs
+ *
+ * Standard Scenario IDs - Add new scenarios at the end.
+ */
+
+#define SND_SCN_PLAYBACK_SPEAKER "playback speaker"
+#define SND_SCN_PLAYBACK_HEADPHONES "playback headphone"
+#define SND_SCN_PLAYBACK_HEADSET "playback headset"
+#define SND_SCN_PLAYBACK_BLUETOOTH "playback bluetooth"
+#define SND_SCN_PLAYBACK_HANDSET "playback handset"
+#define SND_SCN_PLAYBACK_GSM "playback gsm"
+#define SND_SCN_PLAYBACK_LINE "playback line"
+
+#define SND_SCN_CAPTURE_MIC "capture mic"
+#define SND_SCN_CAPTURE_LINE "capture line"
+#define SND_SCN_CAPTURE_HEADSET "capture headset"
+#define SND_SCN_CAPTURE_HANDSET "capture handset"
+#define SND_SCN_CAPTURE_BLUETOOTH "capture bluetooth"
+#define SND_SCN_CAPTURE_GSM "capture gsm"
+
+#define SND_SCN_PHONECALL_GSM_HANDSET "phonecall gsm handset"
+#define SND_SCN_PHONECALL_BT_HANDSET "phonecall bt handset"
+#define SND_SCN_PHONECALL_IP_HANDSET "phonecall ip handset"
+#define SND_SCN_PHONECALL_GSM_HEADSET "phonecall gsm headset"
+#define SND_SCN_PHONECALL_BT_HEADSET "phonecall bt headset"
+#define SND_SCN_PHONECALL_IP_HEADSET "phonecall ip headset"
+
+/**
+ * QOS
+ *
+ * Defines Audio Quality of Service. Systems supporting different types of QoS
+ * often have lower power consumption on lower quality levels.
+ */
+#define SND_POWER_QOS_HIFI 0
+#define SND_POWER_QOS_VOICE 1
+#define SND_POWER_QOS_SYSTEM 2
+
+struct snd_scenario;
+
+/* TODO: add notification */
+
+/**
+ * snd_scenario_list - list supported scenarios
+ * @scn: scenario
+ * @list: list of supported scenario names.
+ *
+ * List supported scenarios for this sound card.
+ * Returns number of scenarios.
+ */
+int snd_scenario_list(struct snd_scenario *scn, const char **list[]);
+
+/**
+ * snd_scenario_set_scn - set scenario
+ * @scn: scenario
+ * @scenario: scenario name
+ *
+ * Set new scenario for sound card.
+ */
+int snd_scenario_set_scn(struct snd_scenario *scn, const char *scenario);
+
+/**
+ * snd_scenario_get_scn - get scenario
+ * @scn: scenario
+ *
+ * Get current sound card scenario.
+ */
+const char *snd_scenario_get_scn(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_playback_volume - get playback volume
+ * @scn: scenario
+ *
+ * Get the master playback volume control name for the current scenario.
+ */
+int snd_scenario_get_master_playback_volume(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_playback_switch - get playback switch
+ * @scn: scenario
+ *
+ * Get the master playback switch control name for the current scenario.
+ */
+ int snd_scenario_get_master_playback_switch(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_capture_volume - get capture volume
+ * @scn: scenario
+ *
+ * Get the master capture volume control name for the current scenario.
+ */
+int snd_scenario_get_master_capture_volume(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_get_master_capture_switch - get capture switch
+ * @scn: scenario
+ *
+ * Get the master capture switch control name for the current scenario.
+ */
+int snd_scenario_get_master_capture_switch(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_set_qos - set qos
+ * @qos: qos
+ *
+ * Set Quality of Service for this scenario.
+ */
+int snd_scenario_set_qos(struct snd_scenario *scn, int qos);
+
+/**
+ * snd_scenario_get_qos - get qos
+ * @scn: scenario
+ *
+ * Get Quality of Service for this scenario.
+ */
+int snd_scenario_get_qos(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_open - open scenario core
+ * @card_name: sound card name.
+ *
+ * Open scenario manager core for sound card.
+ */
+struct snd_scenario *snd_scenario_open(const char *card_name);
+
+/**
+ * snd_scenario_reload - reload and reparse scenario configuration
+ * @scn: scenario
+ *
+ * Reloads and reparses sound card scenario configuration.
+ */
+int snd_scenario_reload(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_close - close scenario core
+ * @scn: scenario
+ *
+ * Free scenario manager core for sound card.
+ */
+void snd_scenario_close(struct snd_scenario *scn);
+
+/**
+ * snd_scenario_dump - dump
+ * @card_name: sound card name.
+ *
+ * Dump current sound card settings to stdout in scn format.
+ */
+int snd_scenario_dump(const char *card_name);
diff --git a/src/Makefile.am b/src/Makefile.am
index 3204fe4..be46cb3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,7 +14,7 @@ SYMFUNCS =
endif
lib_LTLIBRARIES = libasound.la
-libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c
+libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c ascenario.c
SUBDIRS=control
libasound_la_LIBADD = control/libcontrol.la
diff --git a/src/ascenario.c b/src/ascenario.c
new file mode 100644
index 0000000..74082d4
--- /dev/null
+++ b/src/ascenario.c
@@ -0,0 +1,1358 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2008-2009 SlimLogic Ltd
+ * Authors: Liam Girdwood <lrg(a)slimlogic.co.uk>
+ * Stefan Schmidt <stefan(a)slimlogic.co.uk>
+ */
+
+#define _GNU_SOURCE /* needed of O_NOATIME */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <alsa/asoundlib.h>
+
+#include "../include/ascenario.h"
+
+#define PRE_SEQ 0
+#define POST_SEQ 1
+#define MAX_SCN 32
+#define MAX_NAME 64
+#define MAX_FILE 256
+#define MAX_BUF 256
+#define ALSA_SCN_DIR "/etc/alsa/scenario"
+
+/*
+ * Stores all scenario settings for 1 kcontrol. Hence we have a
+ * control_settings for each kcontrol in card.
+ */
+struct control_settings {
+ char name[MAX_NAME];
+ int id;
+ snd_ctl_elem_type_t type;
+ int count; /* 1 = mono, 2 = stereo, etc */
+ unsigned short *value; /* kcontrol value 2D array */
+};
+
+/*
+ * If sleep is 0 the element contains the settings in control. Else sleep
+ * contains the sleep time in micro seconds.
+ */
+struct sequence_element {
+ unsigned int sleep; /* Sleep time in msecs if sleep element, else 0 */
+ struct control_settings *control;
+ struct sequence_element *next; /* Pointer to next list element */
+};
+
+/*
+ * Describes default mixers and qos for scenario.
+ * We have a scenario_info for each scenario loaded.
+ */
+struct scenario_info {
+ char *name;
+ char *file;
+ char *pre_sequence_file;
+ char *post_sequence_file;
+ short playback_volume_id;
+ short playback_switch_id;
+ short capture_volume_id;
+ short capture_switch_id;
+ int qos;
+};
+
+/* Describe a snd card and all its scenarios.
+ */
+struct snd_scenario {
+ char *card_name;
+ int current_scenario;
+ int num_scenarios; /* number of supported scenarios */
+ int num_kcontrols; /* number of kcontrols */
+ struct sequence_element *pre_seq_list; /* Linked list for pre sequence */
+ struct sequence_element *post_seq_list; /* Linked list for post sequence */
+ const char **list;
+ struct scenario_info *scenario; /* var len array of scenario info */
+ struct control_settings *control; /* var len array of controls */
+};
+
+static void scn_error(const char *fmt,...)
+{
+ va_list va;
+ va_start(va, fmt);
+ fprintf(stderr, "scenario: ");
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+}
+
+static void scn_stdout(const char *fmt,...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stdout, fmt, va);
+ va_end(va);
+}
+
+static inline void set_value(struct snd_scenario *scn,
+ struct control_settings *control, int count, unsigned short val)
+{
+ int offset = scn->current_scenario * control->count;
+ control->value[offset + count] = val;
+}
+
+static inline unsigned short get_value(struct snd_scenario *scn,
+ struct control_settings *control, int count)
+{
+ int offset = scn->current_scenario * control->count;
+ return control->value[offset + count];
+}
+
+static int dump_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
+{
+ int err, count, i;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_type_t type;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ scn_stdout("%s: failed to get ctl info\n");
+ return err;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ type = snd_ctl_elem_info_get_type(info);
+ count = snd_ctl_elem_info_get_count(info);
+ if (count == 0)
+ return 0;
+
+ scn_stdout("%u:'%s':%d:",
+ snd_ctl_elem_id_get_numid(id),
+ snd_ctl_elem_id_get_name(id), count);
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_boolean(control, i));
+ scn_stdout("%d", snd_ctl_elem_value_get_boolean(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_integer(control, i));
+ scn_stdout("%d", snd_ctl_elem_value_get_integer(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%ld,",
+ snd_ctl_elem_value_get_integer64(control, i));
+ scn_stdout("%ld",
+ snd_ctl_elem_value_get_integer64(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%d,",
+ snd_ctl_elem_value_get_enumerated(control, i));
+ scn_stdout("%d",
+ snd_ctl_elem_value_get_enumerated(control, i));
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ for (i = 0; i < count - 1; i++)
+ scn_stdout("%2.2x,",
+ snd_ctl_elem_value_get_byte(control, i));
+ scn_stdout("%2.2x", snd_ctl_elem_value_get_byte(control, i));
+ break;
+ default:
+ break;
+ }
+ scn_stdout("\n");
+ return 0;
+}
+
+/*
+ * Add new kcontrol from sound card into memory database.
+ */
+static int add_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
+ struct control_settings *control_settings)
+{
+ int err;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ scn_stdout("%s: failed to get ctl info\n");
+ return err;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ strncpy(control_settings->name, snd_ctl_elem_id_get_name(id),
+ MAX_NAME);
+ control_settings->count = snd_ctl_elem_info_get_count(info);
+ control_settings->type = snd_ctl_elem_info_get_type(info);
+ control_settings->id = snd_ctl_elem_id_get_numid(id);
+ return 0;
+}
+
+static int parse_controls(struct snd_scenario *scn, FILE *f)
+{
+ struct control_settings *control;
+ char buf[MAX_BUF], name[MAX_NAME];
+ int id, count, line = 1, i;
+ char *name_start, *name_end, *tbuf;
+
+ while (fgets(buf, MAX_BUF, f) != NULL) {
+
+ /* get id */
+ tbuf = buf;
+ id = atoi(tbuf);
+ if (id == 0) {
+ scn_error("%s:id == 0 on line %d\n", __func__, line);
+ return -EINVAL;
+ }
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ if (id == scn->control[i].id) {
+ control = &scn->control[i];
+ goto get_name;
+ }
+ }
+ scn_error("%s:id not found at line %d\n", __func__, line);
+ return -EINVAL;
+get_name:
+ /* get name start */
+ while (*tbuf != 0 && *tbuf != '\'')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ name_start = ++tbuf;
+
+ /* get name end */
+ while (*tbuf != 0 && *tbuf != '\'')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ name_end = tbuf++;
+
+ /* copy name */
+ if ((name_end - name_start) > MAX_NAME) {
+ scn_error("%s:name too big at %d chars line %d\n",
+ __func__, name_end - name_start, line);
+ return -EINVAL;
+ }
+ strncpy(name, name_start, name_end - name_start);
+ name[name_end - name_start] = 0;
+ if (strcmp(name, control->name)) {
+ scn_error("%s: name %s and %s don't match at line %d\n",
+ __func__, name, control->name, line);
+ return -EINVAL;
+ }
+
+ /* get count */
+ tbuf++;
+ count = atoi(tbuf);
+ if (count == 0) {
+ scn_error("%s:count == 0 on line %d\n", __func__,
+ line);
+ return -EINVAL;
+ }
+ if (count != control->count) {
+ scn_error("%s:count does not match at line %d\n",
+ __func__, line);
+ return -EINVAL;
+ }
+
+ /* get vals */
+ control->value = malloc(control->count * scn->num_scenarios *
+ sizeof(unsigned short));
+ if (control->value == NULL)
+ return -ENOMEM;
+
+ while (*tbuf != 0 && *tbuf != ':')
+ tbuf++;
+ if (*tbuf == 0)
+ return -EINVAL;
+ tbuf++;
+
+ for (i = 0; i < count; i++) {
+ set_value(scn, control, i, atoi(tbuf));
+ while (*tbuf != 0 && *tbuf != ',')
+ tbuf++;
+
+ if (*tbuf++ == 0 && i < (count - 1))
+ return -EINVAL;
+ }
+ line++;
+ }
+
+ return 0;
+}
+
+static char *get_string (char *buf)
+{
+ char *str, *end;
+
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return NULL;
+ }
+
+ /* find leading '"' */
+ buf++;
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing start '\"'\n", __func__);
+ return NULL;
+ }
+ str = ++buf;
+
+ /* get value */
+ while (*buf != 0 && *buf != '"')
+ buf++;
+ end = buf;
+
+ /* find '"' terminator */
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing terminator '\"' %s\n", __func__, buf);
+ return NULL;
+ }
+
+ *end = 0;
+ return strdup(str);
+}
+
+static char *get_control_name (char *buf)
+{
+ char *str, *end;
+
+ /* find leading '"' */
+ buf++;
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing start '\"'\n", __func__);
+ return NULL;
+ }
+ str = ++buf;
+
+ /* get value */
+ while (*buf != 0 && *buf != '"')
+ buf++;
+ end = buf;
+
+ /* find '"' terminator */
+ if (*buf == 0 || *buf != '"') {
+ scn_error("%s: missing terminator '\"' %s\n", __func__, buf);
+ return NULL;
+ }
+
+ *end = 0;
+ return strdup(str);
+}
+
+static int get_int (char *buf)
+{
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return -EINVAL;
+ }
+ buf++;
+ return atoi(buf);
+}
+
+static int get_enum (char *buf)
+{
+ /* find '=' */
+ while (isblank(*buf))
+ buf++;
+ if (*buf == 0 || *buf != '=') {
+ scn_error("%s: missing '='\n", __func__);
+ return -EINVAL;
+ }
+ buf++;
+ return 0; /* TODO */
+}
+
+static void seq_list_append(struct snd_scenario *scn,
+ struct sequence_element *curr, int position)
+{
+ struct sequence_element *last, *tmp;
+
+ if (position) {
+ if (!scn->post_seq_list)
+ scn->post_seq_list = curr;
+
+ else {
+ tmp = scn->post_seq_list;
+ while (tmp) {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ last->next = curr;
+ }
+ }
+ else {
+ if (!scn->pre_seq_list) {
+ scn->pre_seq_list = curr;
+ }
+ else {
+ tmp = scn->pre_seq_list;
+ while (tmp) {
+ last = tmp;
+ tmp = tmp->next;
+ }
+ last->next = curr;
+ }
+ }
+}
+
+static int parse_sequences(struct snd_scenario *scn, FILE *f, int position)
+{
+ char buf[MAX_BUF], *tbuf, *control_value;
+ int control_len, i;
+ struct sequence_element *curr;
+
+ while (fgets(buf, MAX_BUF, f) != NULL) {
+
+ /* Check for lines with comments and ignore */
+ if (buf[0] == '#')
+ continue;
+
+ /* Parse current line and skip blanks */
+ tbuf = buf;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ curr = malloc(sizeof(struct sequence_element));
+ if (curr == NULL)
+ return -ENOMEM;
+ bzero(curr, sizeof(struct sequence_element));
+
+ curr->control = malloc(sizeof(struct control_settings));
+ if (curr->control == NULL)
+ return -ENOMEM;
+ bzero(curr->control, sizeof(struct control_settings));
+
+ curr->control->value = malloc(curr->control->count * scn->num_scenarios
+ * sizeof(unsigned short));
+ if (curr->control->value == NULL)
+ return -ENOMEM;
+ bzero(curr->control->value, curr->control->count * scn->num_scenarios
+ * sizeof(unsigned short));
+
+ if (strncmp(tbuf, "kcontrol", 8) == 0) {
+ strncpy(curr->control->name, get_control_name(tbuf + 8), MAX_NAME);
+ control_len = strlen(curr->control->name);
+ /* 11 = 8 from kcontrol + 2 quotes + 1 blank */
+ control_value = get_string(tbuf + 11 + control_len);
+
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ if (strncmp(curr->control->name, scn->control[i].name,
+ control_len) == 0) {
+ curr->sleep = 0;
+ curr->control->id = scn->control[i].id;
+ curr->control->type = scn->control[i].type;
+ curr->control->count = scn->control[i].count;
+ set_value(scn, curr->control, curr->control->count,
+ atoi(control_value));
+ seq_list_append(scn, curr, position);
+ }
+ }
+
+ continue;
+ }
+
+ if (strncmp(tbuf, "msleep", 6) == 0) {
+ curr->sleep = get_int(tbuf + 6);
+
+ /* Free control elements as we only have a sleep element
+ * here */
+ if (curr->control) {
+ if (curr->control->value)
+ free(curr->control->value);
+ free(curr->control);
+ }
+
+ seq_list_append(scn, curr, position);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/* load scenario i */
+static int read_scenario_file(struct snd_scenario *scn)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+ struct scenario_info *info = &scn->scenario[scn->current_scenario];
+
+ snprintf(filename, MAX_FILE, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->file);
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ scn_error("%s: couldn't open %s\n", __func__, filename);
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = parse_controls(scn, f);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+static int read_sequence_file(struct snd_scenario *scn, int position)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+ struct scenario_info *info = &scn->scenario[scn->current_scenario];
+
+ if (position == PRE_SEQ) {
+ sprintf(filename, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->pre_sequence_file);
+ }
+ else {
+ sprintf(filename, "%s/%s/%s", ALSA_SCN_DIR, scn->card_name,
+ info->post_sequence_file);
+ }
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = parse_sequences(scn, f, position);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+static int parse_scenario(struct snd_scenario *scn, FILE *f, int line_)
+{
+ struct scenario_info *info;
+ int line = line_ - 1, id = 0, file = 0;
+ char buf[MAX_BUF], *tbuf;
+
+ scn->scenario = realloc(scn->scenario,
+ (scn->num_scenarios + 1) * sizeof(struct scenario_info));
+ if (scn->scenario == NULL)
+ return -ENOMEM;
+ bzero(scn->scenario, sizeof(struct scenario_info));
+ info = scn->scenario + scn->num_scenarios;
+
+ /* Set sequence filename to NULL as it is optional and we want to check
+ * for NULL to avoid segfaults */
+ info->pre_sequence_file = NULL;
+ info->post_sequence_file = NULL;
+
+ while(fgets(buf, MAX_BUF, f) != NULL) {
+
+ line++;
+ if (buf[0] == '#')
+ continue;
+
+ tbuf = buf;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ if (strncmp(tbuf, "Identifier", 10) == 0) {
+ info->name = get_string(tbuf + 10);
+ if (info->name == NULL) {
+ scn_error("%s: failed to get Identifer\n",
+ __func__);
+ goto err;
+ }
+ id = 1;
+ continue;
+ }
+
+ if (strncmp(tbuf, "File", 4) == 0) {
+ info->file = get_string(tbuf + 4);
+ if (info->file == NULL) {
+ scn_error("%s: failed to get File\n",
+ __func__);
+ goto err;
+ }
+ file = 1;
+ continue;
+ }
+
+ if (strncmp(tbuf, "QoS", 3) == 0) {
+ info->qos = get_enum(tbuf + 3);
+ if (info->qos < 0) {
+ scn_error("%s: failed to get QoS\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "MasterPlaybackVolume", 20) == 0) {
+ info->playback_volume_id = get_int(tbuf + 20);
+ if (info->playback_volume_id < 0) {
+ scn_error("%s: failed to get MasterPlaybackVolume\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "MasterPlaybackSwitch", 20) == 0) {
+ info->playback_switch_id = get_int(tbuf + 20);
+ if (info->playback_switch_id < 0) {
+ scn_error("%s: failed to get MasterPlaybackSwitch\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "MasterCaptureVolume", 19) == 0) {
+ info->capture_volume_id = get_int(tbuf + 19);
+ if (info->capture_volume_id < 0) {
+ scn_error("%s: failed to get MasterCaptureVolume\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "MasterCaptureSwitch", 19) == 0) {
+ info->capture_switch_id = get_int(tbuf + 19);
+ if (info->capture_switch_id < 0) {
+ scn_error("%s: failed to get MasterCaptureSwitch\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "PreSequenceFile", 15) == 0) {
+ info->pre_sequence_file = get_string(tbuf + 15);
+ if (info->pre_sequence_file == NULL) {
+ scn_error("%s: failed to get PreSequenceFile\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "PostSequenceFile", 16) == 0) {
+ info->post_sequence_file = get_string(tbuf + 16);
+ if (info->post_sequence_file == NULL) {
+ scn_error("%s: failed to get PostSequenceFile\n",
+ __func__);
+ goto err;
+ }
+ continue;
+ }
+
+ if (strncmp(tbuf, "EndSection", 10) == 0) {
+ break;
+ }
+ }
+
+ if (file & id) {
+ scn->num_scenarios++;
+ return 0;
+ }
+err:
+ if (file) {
+ free(info->file);
+ info->file = NULL;
+ }
+ if (id) {
+ free(info->name);
+ info->name = NULL;
+ }
+ return -EINVAL;
+}
+
+static int read_master_file(struct snd_scenario *scn, FILE *f)
+{
+ int line = 0, ret = 0, i;
+ char buf[MAX_BUF], *tbuf;
+
+ /* parse master config sections */
+ while(fgets(buf, MAX_BUF, f) != NULL) {
+
+ if (buf[0] == '#') {
+ line++;
+ continue;
+ }
+
+ if (strncmp(buf, "Section", 7) == 0) {
+
+ tbuf = buf + 7;
+ while (isblank(*tbuf))
+ tbuf++;
+
+ if (strncmp(tbuf, "\"Scenario\"", 10) == 0) {
+ line = parse_scenario(scn, f, line);
+ if (line < 0) {
+ scn_error("%s: failed to parse "
+ "scenario\n", __func__);
+ goto err;
+ }
+ continue;
+ }
+ }
+ line++;
+ }
+
+ /* copy ptrs to scenario names */
+ scn->list = malloc(scn->num_scenarios * sizeof(char *));
+ if (scn->list == NULL)
+ ret = -ENOMEM;
+ for (i = 0; i < scn->num_scenarios; i++)
+ scn->list[i] = scn->scenario[i].name;
+
+err:
+ if (ferror(f)) {
+ scn_error("%s: failed to read master\n", __func__);
+ return ferror(f);
+ }
+ return ret;
+}
+
+/* load scenario i */
+static int import_master_config(struct snd_scenario *scn)
+{
+ int fd, ret;
+ FILE *f;
+ char filename[MAX_FILE];
+
+ sprintf(filename, "%s/%s.conf", ALSA_SCN_DIR, scn->card_name);
+
+ fd = open(filename, O_RDONLY | O_NOATIME);
+ if (fd < 0) {
+ scn_error("%s: couldn't open %s\n", __func__, filename);
+ return fd;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ ret = errno;
+ goto close;
+ }
+
+ ret = read_master_file(scn, f);
+ fclose(f);
+close:
+ close(fd);
+ return ret;
+}
+
+/* parse_card_controls
+ * @scn: scenario
+ *
+ * Parse sound card and store control data in memory db.
+ */
+static int parse_card_controls(struct snd_scenario *scn)
+{
+ struct control_settings *control;
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ int ret, i;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* open and load snd card */
+ ret = snd_ctl_open(&handle, scn->card_name, SND_CTL_READONLY);
+ if (ret < 0) {
+ scn_error("%s: control %s open retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ scn->num_kcontrols = snd_ctl_elem_list_get_count(list);
+ if (scn->num_kcontrols < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, scn->num_kcontrols) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* allocate db memory for controls */
+ scn->control = calloc(scn->num_kcontrols,
+ sizeof(struct control_settings));
+ if (scn->control == NULL) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ control = scn->control;
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < scn->num_kcontrols; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = add_control(handle, id, control++);
+ if (ret < 0) {
+ scn_error("%s: failed to add control error %s\n",
+ __func__, snd_strerror(ret));
+ goto close;
+ }
+ }
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+/* import_scenario_files -
+ * @scn: scenario
+ *
+ * Read and parse scenario_info files the store in memory.
+ */
+static int import_scenario_files(struct snd_scenario *scn)
+{
+ int ret;
+
+ ret = import_master_config(scn);
+ if (ret < 0) {
+ scn_error("%s: failed to parse master scenario config\n",
+ __func__);
+ return ret;
+ }
+
+ for (scn->current_scenario = 0;
+ scn->current_scenario < scn->num_scenarios;
+ scn->current_scenario++) {
+
+ ret = read_scenario_file(scn);
+ if (ret < 0) {
+ scn_error("%s: failed to parse scenario %s\n",
+ __func__,
+ scn->scenario[scn->current_scenario].name);
+ scn->current_scenario = -1;
+ return ret;
+ }
+
+ if (scn->scenario[scn->current_scenario].pre_sequence_file != NULL) {
+ ret = read_sequence_file(scn, PRE_SEQ);
+ if (ret < 0) {
+ scn_stdout("Warning: PreSequence file defined but"
+ " missing in scenario \"%s\"\n",
+ scn->scenario[scn->current_scenario].name);
+ }
+ }
+
+ if (scn->scenario[scn->current_scenario].post_sequence_file != NULL) {
+ ret = read_sequence_file(scn, POST_SEQ);
+ if (ret < 0) {
+ scn_stdout("Warning: PostSequence file defined but"
+ " missing in scenario \"%s\"\n",
+ scn->scenario[scn->current_scenario].name);
+ }
+ }
+
+ }
+ return 0;
+}
+
+/* free all resorces */
+static void free_scn(struct snd_scenario *scn)
+{
+ /* TODO: valgrind to make sure. */
+ int i;
+
+ if (scn == NULL)
+ return;
+
+ if (scn->control) {
+ if (scn->control->value)
+ free(scn->control->value);
+ free(scn->control);
+ }
+
+ if (scn->list)
+ free(scn->list);
+ if (scn->card_name)
+ free(scn->card_name);
+ if (scn->pre_seq_list)
+ free(scn->pre_seq_list);
+ if (scn->post_seq_list)
+ free(scn->post_seq_list);
+
+ if (scn->scenario) {
+ for (i = 0; i < scn->num_scenarios; i++) {
+ struct scenario_info *info = &scn->scenario[i];
+
+ if (info->name)
+ free(info->name);
+ if (info->file)
+ free(info->file);
+ if (info->pre_sequence_file)
+ free(info->pre_sequence_file);
+ if (info->post_sequence_file)
+ free(info->post_sequence_file);
+ }
+ free(scn->scenario);
+ }
+ free(scn);
+}
+
+/*
+ * Init sound card scenario db.
+ */
+struct snd_scenario *snd_scenario_open(const char *card_name)
+{
+ struct snd_scenario *scn;
+ int err;
+
+ /* TODO: locking and
+ * check if card_name scn is already loaded,
+ * if so reuse to conserve ram. */
+
+ scn = malloc(sizeof(struct snd_scenario));
+ if (scn == NULL)
+ return NULL;
+ bzero(scn, sizeof(struct snd_scenario));
+ scn->card_name = strdup(card_name);
+ if (scn->card_name == NULL) {
+ free(scn);
+ return NULL;
+ }
+
+ /* get info about sound card */
+ err = parse_card_controls(scn);
+ if (err < 0) {
+ free_scn(scn);
+ return NULL;
+ }
+
+ /* get info on scenarios and verify against card */
+ err = import_scenario_files(scn);
+ if (err < 0) {
+ free_scn(scn);
+ return NULL;
+ }
+
+ return scn;
+}
+
+/*
+ * Reload and reparse scenario db.
+ */
+int snd_scenario_reload(struct snd_scenario *scn)
+{
+ free_scn(scn);
+
+ scn->num_kcontrols = parse_card_controls(scn);
+ if (scn->num_kcontrols <= 0) {
+ free_scn(scn);
+ return -EINVAL;
+ }
+
+ scn->num_scenarios = import_scenario_files(scn);
+ if (scn->num_scenarios <= 0) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void snd_scenario_close(struct snd_scenario *scn)
+{
+ free_scn(scn);
+}
+
+static int set_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
+ struct snd_scenario *scn)
+{
+ struct control_settings *setting;
+ int ret, count, i, idnum;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_type_t type;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_set_id(info, id);
+ ret = snd_ctl_elem_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s: failed to get ctl info\n", __func__);
+ return ret;
+ }
+
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_read(handle, control);
+
+ idnum = snd_ctl_elem_id_get_numid(id);
+ for (i = 0; i < scn->num_kcontrols; i++) {
+ setting = &scn->control[i];
+ if (setting->id == idnum)
+ goto set_val;
+ }
+ scn_error("%s: failed to find control %d\n", __func__, idnum);
+ return 0;
+
+set_val:
+ type = snd_ctl_elem_info_get_type(info);
+ count = snd_ctl_elem_info_get_count(info);
+ if (count == 0)
+ return 0;
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_boolean(control, i,
+ get_value(scn, setting, i));
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_integer(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_integer64(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_enumerated(control, i,
+ get_value(scn, setting, i));
+
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ for (i = 0; i < count; i++)
+ snd_ctl_elem_value_set_byte(control, i,
+ get_value(scn, setting, i));
+ break;
+ default:
+ break;
+ }
+
+ ret = snd_ctl_elem_write(handle, control);
+ if (ret < 0) {
+ scn_error("%s: control %s failed: %s\n", __func__,
+ setting->name, snd_strerror(ret));
+ scn_error("%s: count %d type: %d\n", __func__,
+ count, type);
+ for (i = 0; i < count; i++)
+ fprintf(stderr, "%d ", get_value(scn, setting, i));
+ return ret;
+ }
+ return 0;
+}
+
+static void exec_sequence(struct sequence_element *seq, struct snd_scenario
+ *scn, snd_ctl_elem_list_t *list, snd_ctl_t *handle)
+{
+ int count = snd_ctl_elem_list_get_count(list);
+ while (seq) {
+ if (seq->sleep)
+ usleep(seq->sleep);
+ else {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ int ret, i, numid;
+ /* Where is id lookup from numid if you need it? */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_list_get_id(list, i, id);
+ numid = snd_ctl_elem_id_get_numid(id);
+ if (numid == seq->control->id) {
+ ret = set_control(handle, id, scn);
+ if (ret < 0) {
+ scn_error("%s: failed to set control %s\n",
+ __func__, scn->card_name);
+ }
+ break;
+ }
+ }
+ }
+ seq = seq->next;
+ }
+}
+
+int snd_scenario_set_scn(struct snd_scenario *scn, const char *name)
+{
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ snd_ctl_t *handle;
+ int ret, count, i;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* find scenario name */
+ for (i = 0; i < scn->num_scenarios; i++) {
+ if (!strcmp(scn->scenario[i].name, name))
+ goto found;
+ }
+ scn_error("%s: scenario %s not found\n", __func__, name);
+ return -EINVAL;
+
+found:
+ /* scenario found - now open card */
+ scn->current_scenario = i;
+ ret = snd_ctl_open(&handle, scn->card_name, 0);
+ if (ret) {
+ scn_error("%s: control %s open error: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ scn->card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ count = snd_ctl_elem_list_get_count(list);
+ if (count < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* If we have a sequence list that should be executed before the new
+ * scenario is set do it now */
+ if (scn->pre_seq_list)
+ exec_sequence(scn->pre_seq_list, scn, list, handle);
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = set_control(handle, id, scn);
+ if (ret < 0) {
+ scn_error("%s: failed to set control %s\n", __func__,
+ scn->card_name);
+ }
+ }
+
+ /* If we have a sequence list that should be executed after the new
+ * scenario is set do it now */
+ if (scn->post_seq_list)
+ exec_sequence(scn->post_seq_list, scn, list, handle);
+
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+int snd_scenario_dump(const char *card_name)
+{
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_ctl_elem_list_t *list;
+ int ret, i, count;
+
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ /* open and load snd card */
+ ret = snd_ctl_open(&handle, card_name, SND_CTL_READONLY);
+ if (ret < 0) {
+ scn_error("%s: control %s open retor: %s\n", __func__, card_name,
+ snd_strerror(ret));
+ return ret;
+ }
+
+ ret = snd_ctl_card_info(handle, info);
+ if (ret < 0) {
+ scn_error("%s :control %s local retor: %s\n", __func__,
+ card_name, snd_strerror(ret));
+ goto close;
+ }
+
+ ret = snd_ctl_elem_list(handle, list);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto close;
+ }
+
+ count = snd_ctl_elem_list_get_count(list);
+ if (count < 0) {
+ ret = 0;
+ goto close;
+ }
+
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+ scn_error("%s: not enough memory...\n", __func__);
+ ret = -ENOMEM;
+ goto close;
+ }
+ if ((ret = snd_ctl_elem_list(handle, list)) < 0) {
+ scn_error("%s: cannot determine controls: %s\n", __func__,
+ snd_strerror(ret));
+ goto free;
+ }
+
+ /* iterate through each kcontrol and add to db */
+ for (i = 0; i < count; ++i) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, i, id);
+
+ ret = dump_control(handle, id);
+ if (ret < 0) {
+ scn_error("%s: cannot determine controls: %s\n",
+ __func__, snd_strerror(ret));
+ goto free;
+ }
+ }
+free:
+ snd_ctl_elem_list_free_space(list);
+close:
+ snd_ctl_close(handle);
+ return ret;
+}
+
+const char *snd_scenario_get_scn(struct snd_scenario *scn)
+{
+ if (scn->current_scenario > 0 && scn->current_scenario < MAX_SCN)
+ return scn->scenario[scn->current_scenario].name;
+ else
+ return NULL;
+}
+
+int snd_scenario_set_qos(struct snd_scenario *scn, int qos)
+{
+ /* TODO: change QoS kcontrols */
+ scn->scenario[scn->current_scenario].qos = qos;
+ return 0;
+}
+
+int snd_scenario_get_qos(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].qos;
+}
+
+int snd_scenario_get_master_playback_volume(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].playback_volume_id;
+}
+
+int snd_scenario_get_master_playback_switch(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].playback_switch_id;
+}
+
+int snd_scenario_get_master_capture_volume(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].capture_volume_id;
+}
+
+int snd_scenario_get_master_capture_switch(struct snd_scenario *scn)
+{
+ return scn->scenario[scn->current_scenario].capture_switch_id;
+}
+
+int snd_scenario_list(struct snd_scenario *scn, const char **list[])
+{
+ *list = scn->list;
+ return scn->num_scenarios;
+}
--
1.6.4.3
regards
Stefan Schmidt
1
0
Hi,
I just bought an Auzentech Prelude and compiled a 2.6.31.1 kernel to test it.
Analog and digital outputs are working but I experience some problems:
- There are some lags when I play long tracks on Amarok.
- Sometimes the track suddenly play slower for a time and go back, or not, and
I have to pause and play to fix it.
- Sometimes there is noise (only) for a time when I play a track, then it go
back after a while.
- I was unable to play an audio file correctly with VLC, the sound is very
saccaded.
- No sound at all with Flash and Iceweasel.
Are they known bugs ? Are there plans to fix them ?
Thank you,
--
Laurent Léonard
1
0
30 Sep '09
Hello.
While working with alsa-lib to get scenarios in shape and submitted later I
added support for the new automake feature AM_SILENT_RULES and did some small
cleanups.
[PATCH 1/3] cvscompile: Remove in favour of gitcompile.
I see no reason to keep this around when CVS is no longer used and gitcompile
does replace it. Something I miss here?
[PATCH 2/3] configure.in: Add m4 check for new AM_SILENT_RULES
The kbuild like output makes it a lot easier to follow the compile and linking
process, at least in my opinion. It is available with automake 1.11 and we use a
m4 macro here to only use it when it is available.
[PATCH 3/3] control: Remove unused variable.
Found while really seeing what is going on thanks to AM_SILENT_RULES: ;)
There are also some unused paramter and a function around. I don't know how you
guys prefer to handle this in the ALSA project so I did not prepare patches for
this yet. Let me know if you want some for it.
pcm_route.c: In function ‘snd_pcm_route_convert1_many’:
pcm_route.c:350: warning: ‘src’ may be used uninitialized in this function
pcm_rate_linear.c: In function ‘get_supported_rates’:
pcm_rate_linear.c:408: warning: unused parameter ‘rate’
pcm_rate_linear.c: In function ‘linear_dump’:
pcm_rate_linear.c:416: warning: unused parameter ‘rate’
pcm_rate_linear.c: In function ‘_snd_pcm_rate_linear_open’:
pcm_rate_linear.c:435: warning: unused parameter ‘version’
alisp.c:237: warning: ‘incref_tree_explicit’ defined but not used
regards
Stefan Schmidt
3
7
[alsa-devel] [PATCH 1/2] pcm_rate_linear: Annotate unused function parameter to avoid compiler warnings.
by Stefan Schmidt 30 Sep '09
by Stefan Schmidt 30 Sep '09
30 Sep '09
From: Stefan Schmidt <stefan(a)slimlogic.co.uk>
Signed-off-by: Stefan Schmidt <stefan(a)slimlogic.co.uk>
---
src/pcm/pcm_rate_linear.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c
index 8b2d2d0..7481b38 100644
--- a/src/pcm/pcm_rate_linear.c
+++ b/src/pcm/pcm_rate_linear.c
@@ -405,15 +405,15 @@ static void linear_close(void *obj)
free(obj);
}
-static int get_supported_rates(void *rate, unsigned int *rate_min,
- unsigned int *rate_max)
+static int get_supported_rates(ATTRIBUTE_UNUSED void *rate,
+ unsigned int *rate_min, unsigned int *rate_max)
{
*rate_min = SND_PCM_PLUGIN_RATE_MIN;
*rate_max = SND_PCM_PLUGIN_RATE_MAX;
return 0;
}
-static void linear_dump(void *rate, snd_output_t *out)
+static void linear_dump(ATTRIBUTE_UNUSED void *rate, snd_output_t *out)
{
snd_output_printf(out, "Converter: linear-interpolation\n");
}
@@ -432,7 +432,8 @@ static const snd_pcm_rate_ops_t linear_ops = {
.dump = linear_dump,
};
-int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
+int SND_PCM_RATE_PLUGIN_ENTRY(linear) (ATTRIBUTE_UNUSED unsigned int version,
+ void **objp, snd_pcm_rate_ops_t *ops)
{
struct rate_linear *rate;
--
1.6.4.3
1
1
30 Sep '09
Hello,
The following patch fixes the handling of the line widget, which can be input or
output.
Additional check is added to is_connected_output_ep and is_connected_input_ep
functions when the line widget is checked to handle only the appropriate
line widget.
Without the patch these two functions would pick up either line widgets, which
confused DAPM.
Note: checkpach complains for the long lines, but I feel that it is appropriate
here to use the long lines.
---
Peter Ujfalusi (1):
ASoC: Fix SND_SOC_DAPM_LINE handling
sound/soc/soc-dapm.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
2
2
This change:
http://git.alsa-project.org/?p=alsa-kernel.git;a=blobdiff;f=sound/pci/echoa…
broke the Mia driver because it removed the line-out control interface from
all vmixer cards. But the Mia has that (undocumented!!) control and after
the change it is impossible to raise the volume. I tested the driver with
my IndigoIOx that was supposed to have the same set of controls... sorry.
I hope it's not too late to merge this patch in linux-2.6.32.
----------
Re-enable the line-out control for the Mia card.
Signed-off-by: Giuliano Pochini <pochini(a)shiny.it>
diff -u alsa-driver-1.0.21/alsa-kernel/pci/echoaudio__orig/echoaudio.c alsa-driver-1.0.21/alsa-kernel/pci/echoaudio/echoaudio.c
--- alsa-driver-1.0.21/alsa-kernel/pci/echoaudio__orig/echoaudio.c 2009-08-28 13:41:39.000000000 +0200
+++ alsa-driver-1.0.21/alsa-kernel/pci/echoaudio/echoaudio.c 2009-09-29 21:55:48.000000000 +0200
@@ -950,7 +950,7 @@
Control interface
******************************************************************************/
-#ifndef ECHOCARD_HAS_VMIXER
+#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN)
/******************* PCM output volume *******************/
static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
@@ -1003,6 +1003,19 @@
return changed;
}
+#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
+/* On the Mia this one controls the line-out volume */
+static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+ .name = "Line Playback Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_echo_output_gain_info,
+ .get = snd_echo_output_gain_get,
+ .put = snd_echo_output_gain_put,
+ .tlv = {.p = db_scale_output_gain},
+};
+#else
static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1012,9 +1025,10 @@
.put = snd_echo_output_gain_put,
.tlv = {.p = db_scale_output_gain},
};
-
#endif
+#endif /* !ECHOCARD_HAS_VMIXER || ECHOCARD_HAS_LINE_OUT_GAIN */
+
#ifdef ECHOCARD_HAS_INPUT_GAIN
@@ -2030,10 +2044,14 @@
snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
goto ctl_error;
-#else
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0)
+#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0)
goto ctl_error;
#endif
+#else /* ECHOCARD_HAS_VMIXER */
+ if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0)
+ goto ctl_error;
+#endif /* ECHOCARD_HAS_VMIXER */
#ifdef ECHOCARD_HAS_INPUT_GAIN
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
diff -u alsa-driver-1.0.21/alsa-kernel/pci/echoaudio__orig/mia.c alsa-driver-1.0.21/alsa-kernel/pci/echoaudio/mia.c
--- alsa-driver-1.0.21/alsa-kernel/pci/echoaudio__orig/mia.c 2009-08-28 13:41:39.000000000 +0200
+++ alsa-driver-1.0.21/alsa-kernel/pci/echoaudio/mia.c 2009-09-29 21:55:48.000000000 +0200
@@ -29,6 +29,7 @@
#define ECHOCARD_HAS_ADAT FALSE
#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
#define ECHOCARD_HAS_MIDI
+#define ECHOCARD_HAS_LINE_OUT_GAIN
/* Pipe indexes */
#define PX_ANALOG_OUT 0 /* 8 */
--
Giuliano.
2
2