What’s alsa
ALSA是Advanced Linux Sound Architecture的缩写,高级Linux声音架构的简称,它在Linux操作系统上提供了音频和MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持。
alsa 代码分析
snd_pcm_open
snd_pcm_start
snd_pcm_writei
snd_pcm_readi
snd_pcm_pause
snd_pcm_close
如何选定snd_pcm_fast_ops_t
从_snd_pcm_writei函数对应的ops上来看,open时会指定fast_ops来代表不同的声卡,那么如何选定这个声卡以及驱动呢?
Samples:
pcm.plughw {
@args [ CARD DEV SUBDEV ]
@args.CARD {
type string
default {
@func getenv
vars [
ALSA_PCM_CARD
ALSA_CARD
]
default {
@func refer
name defaults.pcm.card
}
}
}
@args.DEV {
type integer
default {
@func igetenv
vars [
ALSA_PCM_DEVICE
]
default {
@func refer
name defaults.pcm.device
}
}
}
@args.SUBDEV {
type integer
default {
@func refer
name defaults.pcm.subdevice
}
}
type plug
slave.pcm {
type hw
card $CARD
device $DEV
subdevice $SUBDEV
}
hint {
show {
@func refer
name defaults.namehint.extended
}
description "Hardware device with all software conversions"
}
}
const char *name = "plughw:0,0";
int ret = snd_pcm_open(&handle, name, stream, mode);
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
const char *name, snd_pcm_stream_t stream,
int mode, int hop)
int snd_config_search_definition(snd_config_t *config,
const char *base, const char *name,
snd_config_t **result)
if (args) {
args++;
key = alloca(args - name);
memcpy(key, name, args - name - 1); //这里得到key = plughw
key[args - name - 1] = '\0';
} else {
key = (char *) name;
}
err = snd_config_search(pcm_conf, "type", &conf); //得到 type = plug
if (err < 0) {
SNDERR("type is not defined");
return err;
}
open_name = buf;
sprintf(buf, "_snd_pcm_%s_open", str);
得到函数名为:
_snd_pcm_plug_open
//dlopen/dlsym加载函数名,并调用函数
open_func = snd_dlobj_cache_get(lib, open_name,
SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION), 1);
if (open_func) {
err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);
int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *root, snd_config_t *conf,
snd_pcm_stream_t stream, int mode)
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
pcm->fast_ops = slave->fast_ops;
int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name,
snd_config_t *root,
snd_config_t *conf, snd_pcm_stream_t stream,
int mode, snd_config_t *parent_conf)
根据slave的type:hw,再找到
slave.pcm {
type hw
card $CARD
device $DEV
subdevice $SUBDEV
}
int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
snd_pcm_stream_t stream, int mode)
{
int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
int sync_ptr_ioctl)
最早找到fast_ops对应这个struct
pcm->fast_ops = &snd_pcm_hw_fast_ops;
write 函数
r = snd_pcm_writei(handle, pData, count); =>
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
/* lock handled in the callback */
if (!pcm->fast_ops->writei)
return -ENOSYS;
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
=>
static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
=> 写入驱动当中
int err;
snd_pcm_hw_t *hw = pcm->private_data;
int fd = hw->fd;
struct snd_xferi xferi;
xferi.buf = (char*) buffer;
xferi.frames = size;
xferi.result = 0; /* make valgrind happy */
if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0)
err = -errno;
else
err = query_status_and_control_data(hw);
驱动
kernel/sound/core/pcm_native.c:2833:
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
./core/pcm_lib.c:2127:snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
2127 snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
./core/pcm_lib.c:2011:static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
通过transfer接口把数据发送到USB声卡
snd_pcm_stream_unlock_irq(substream);
err = transfer(substream, appl_ofs, data, offset, frames);
snd_pcm_stream_lock_irq(substream);
mmap的方式写入PCM数据
最终ALSA是靠mmap的方式将数据写入到驱动
snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
snd_pcm_xfer_areas_func_t func)
{
snd_pcm_uframes_t xfer = 0;
snd_pcm_sframes_t err = 0;
snd_pcm_state_t state;
参考
https://www.cnblogs.com/wen123456/p/14043087.html