在android或linux电子设备设备中,通常都有一个主声卡,通过cat /proc/asound/cards可以查看当前设备的声卡情况
以我的平台为例:
可以看到我的平台只有一个主声卡
命令cat /proc/asound/pcm 可以看到声卡下面所有的pcm devices,显示了平台所有的playback和capture能力
而当有外置声卡如插上一个usb声卡的时候,就能多出一个card 1的设备,
某次项目反馈说平台不识别usb声卡,插上之后没有看到多出的声卡设备,查了一下原来是alsa对于最大支持的声卡设备和每个声卡下面挂载的pcm device设备都做了限制。alsa默认最大只支持8个card设备。
每种设备在注册时会调用
int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data, struct device *device)
{
int minor;
int err = 0;
struct snd_minor *preg;
if (snd_BUG_ON(!device))
return -EINVAL;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
preg->card_ptr = card;
mutex_lock(&sound_mutex);
minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
err = minor;
goto error;
}
preg->dev = device;
device->devt = MKDEV(major, minor);
err = device_add(device);
if (err < 0)
goto error;
snd_minors[minor] = preg;
error:
mutex_unlock(&sound_mutex);
if (err < 0)
kfree(preg);
return err;
}
EXPORT_SYMBOL(snd_register_device);
注册时创建设备节点通过调用snd_find_free_minor获取一个空闲的minor
#define SNDRV_OS_MINORS 256
#define SNDRV_MINOR_DEVICES 32
#define SNDRV_MINOR_CARD(minor) ((minor) >> 5)
#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x001f)
#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev))
/* these minors can still be used for autoloading devices (/dev/aload*) */
#define SNDRV_MINOR_CONTROL 0 /* 0 */
#define SNDRV_MINOR_GLOBAL 1 /* 1 */
#define SNDRV_MINOR_SEQUENCER 1 /* SNDRV_MINOR_GLOBAL + 0 * 32 */
#define SNDRV_MINOR_TIMER 33 /* SNDRV_MINOR_GLOBAL + 1 * 32 */
#ifndef CONFIG_SND_DYNAMIC_MINORS
#define SNDRV_MINOR_COMPRESS 2 /* 2 - 3 */
#define SNDRV_MINOR_HWDEP 4 /* 4 - 7 */
#define SNDRV_MINOR_RAWMIDI 8 /* 8 - 15 */
#define SNDRV_MINOR_PCM_PLAYBACK 16 /* 16 - 23 */
#define SNDRV_MINOR_PCM_CAPTURE 24 /* 24 - 31 */
static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
switch (type) {
case SNDRV_DEVICE_TYPE_SEQUENCER:
case SNDRV_DEVICE_TYPE_TIMER:
minor = type;
break;
case SNDRV_DEVICE_TYPE_CONTROL:
if (snd_BUG_ON(!card))
return -EINVAL;
minor = SNDRV_MINOR(card->number, type);
break;
case SNDRV_DEVICE_TYPE_HWDEP:
case SNDRV_DEVICE_TYPE_RAWMIDI:
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
case SNDRV_DEVICE_TYPE_COMPRESS:
if (snd_BUG_ON(!card))
return -EINVAL;
minor = SNDRV_MINOR(card->number, type + dev);
break;
default:
return -EINVAL;
}
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
return -EINVAL;
if (snd_minors[minor])
return -EBUSY;
return minor;
}
虽然平台只有一个主声卡,但是由于card 0下面挂载的pcm 设备有8个而且第8个设备刚好具有capture能力,导致所有能用的minor都被占满了,再插上一个usb声卡设备就注册不上了
#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev))
minor = SNDRV_MINOR(card->number, type + dev);
card->number为0 SNDRV_MINOR_PCM_CAPTURE的type对应为24,在注册第8个dev时,SNDRV_MINOR计算的返回值为32
当usb声卡插上并开始注册的时候card->numbe为1,注册SNDRV_MINOR_CONTROL type时32已经被占了,导致声卡注册失败
最初看到这个问题的改法就是简单的改了alsa定义了这几个宏,用于增大alsa对于这里pcm device的限制,修改如下:
diff --git a/include/sound/minors.h b/include/sound/minors.h
old mode 100644
new mode 100755
index 5978f9a8c8b2..8169e507596a
--- a/include/sound/minors.h
+++ b/include/sound/minors.h
@@ -23,10 +23,10 @@
#define SNDRV_OS_MINORS 256
-#define SNDRV_MINOR_DEVICES 32
-#define SNDRV_MINOR_CARD(minor) ((minor) >> 5)
-#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x001f)
-#define SNDRV_MINOR(card, dev) (((card) << 5) | (dev))
+#define SNDRV_MINOR_DEVICES 64
+#define SNDRV_MINOR_CARD(minor) ((minor) >> 6)
+#define SNDRV_MINOR_DEVICE(minor) ((minor) & 0x003f)
+#define SNDRV_MINOR(card, dev) (((card) << 6) | (dev))
/* these minors can still be used for autoloading devices (/dev/aload*) */
#define SNDRV_MINOR_CONTROL 0 /* 0 */
后来发现alsa实际上用一个内核配置项用于配置最大声卡数的,CONFIG_SND_DYNAMIC_MINORS,alsa的解释为:
Dynamic device file minor numbers (EXPERIMENTAL) found in sound/core/Kconfig
The configuration item CONFIG_SND_DYNAMIC_MINORS:
prompt: Dynamic device file minor numbers (EXPERIMENTAL)
type: bool
depends on: CONFIG_SND && CONFIG_EXPERIMENTAL
defined in sound/core/Kconfig
found in Linux kernels: 2.6.16–2.6.17
Help text
If you say Y here, the minor numbers of ALSA device files in /dev/snd/ are allocated dynamically. This allows you to have more than 8 sound cards, but requires a dynamic device file system like udev.
If you are unsure about this, say N here.
同时我们也可以清楚的看到alsa对支持设备数做限制的地方:
/* number of supported soundcards */
#ifdef CONFIG_SND_DYNAMIC_MINORS
#define SNDRV_CARDS CONFIG_SND_MAX_CARDS
#else
#define SNDRV_CARDS 8 /* don't change - minor numbers */
#endif
#define CONFIG_SND_MAJOR 116 /* standard configuration */
内核打开这个配置项就可以解除限制了