ALSA driver --PCM device 创建过程
Posted fellow_jing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ALSA driver --PCM device 创建过程相关的知识,希望对你有一定的参考价值。
前面已经写过PCM device的创建框架,我们现在来看看PCM device是如何创建的。
在调用snd_pcm_new时就会创建一个snd_pcm类型的PCM device.
struct snd_pcm {
struct snd_card *card;//PCM device 说挂载的声卡
struct list_head list;//一个Card可能有多个PCM device,PCM device列表
int device; /* device number *///PCM device的索引
unsigned int info_flags;
unsigned short dev_class;
unsigned short dev_subclass;
char id[64];
char name[80];
struct snd_pcm_str streams[2];//PCM的playback和capture stream
struct mutex open_mutex;
wait_queue_head_t open_wait;
void *private_data;//private_data一般为芯片专用信息
void (*private_free) (struct snd_pcm *pcm);//用来释放private_data
bool internal; /* pcm is for internal use only */
bool nonatomic; /* whole PCM operations are in non-atomic context */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
struct snd_pcm_oss oss;
#endif
};
接下来我们来看看在snd_pcm_new中有些什么操作:
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
static struct snd_device_ops ops = {//PCM device的操作函数
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,//在PCM device rigister调用
.dev_disconnect = snd_pcm_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//为PCM device创建空间
if (!pcm)
return -ENOMEM;
pcm->card = card;//将声卡保存在PCM device中
pcm->device = device;
pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
INIT_LIST_HEAD(&pcm->list);
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {//为PCM device 创建playback stream
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {//为PCM device 创建capture stream
snd_pcm_free(pcm);
return err;
}
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {//将PCM device 挂载到Card上。
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
一个PCM device分别有一个playback, capture stream,由snd_pcm_new_stream创建。
playback 和capture stream是一个snd_pcm_str的结构体,
struct snd_pcm_str {
int stream; /* stream (direction) *///是playback stream or capture stream
struct snd_pcm *pcm;//当前的PCM device
/* -- substreams -- */
unsigned int substream_count;//substream的数目。
unsigned int substream_opened;//已经open的substream数目。每次open +1,close -1.
struct snd_pcm_substream *substream;//playback stream or capture stream的substream链表
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_stream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
unsigned int xrun_debug; /* 0 = disabled, 1 = verbose, 2 = stacktrace */
struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
struct device dev;//stream的device结构
};
substream的结构体snd_pcm_substream :
struct snd_pcm_substream {
struct snd_pcm *pcm;//PCM device
struct snd_pcm_str *pstr;//playback stream or substream
void *private_data; /* copied from pcm->private_data *///private_data通常是和PCM device的private data相同。
int number;//当前substream的index
char name[32]; /* substream name */
int stream; /* stream (direction) *///是playback or capture的substream
struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
size_t buffer_bytes_max; /* limit ring buffer size *///最大的buffer size
struct snd_dma_buffer dma_buffer;//DMA buffer
size_t dma_max;
/* -- hardware operations -- */
const struct snd_pcm_ops *ops;//substream的操作函数
/* -- runtime information -- */
struct snd_pcm_runtime *runtime;//runtime信息
/* -- timer section -- */
struct snd_timer *timer; /* timer */
unsigned timer_running: 1; /* time is running */
/* -- next substream -- */
struct snd_pcm_substream *next;
/* -- linked substreams -- */
struct list_head link_list; /* linked list member */
struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */
struct snd_pcm_group *group; /* pointer to current group */
/* -- assigned files -- */
void *file;
int ref_count;
atomic_t mmap_count;
unsigned int f_flags;
void (*pcm_release)(struct snd_pcm_substream *);
struct pid *pid;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_substream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
struct snd_info_entry *proc_root;
struct snd_info_entry *proc_info_entry;
struct snd_info_entry *proc_hw_params_entry;
struct snd_info_entry *proc_sw_params_entry;
struct snd_info_entry *proc_status_entry;
struct snd_info_entry *proc_prealloc_entry;
struct snd_info_entry *proc_prealloc_max_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
struct snd_info_entry *proc_xrun_injection_entry;
#endif
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/* misc flags */
unsigned int hw_opened: 1;
};
/**
* snd_pcm_new_stream - create a new PCM stream
* @pcm: the pcm instance
* @stream: the stream direction, SNDRV_PCM_STREAM_XXX
* @substream_count: the number of substreams
*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
int idx, err;
struct snd_pcm_str *pstr = &pcm->streams[stream];//当前的playback stream or capture stream
struct snd_pcm_substream *substream, *prev;
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
mutex_init(&pstr->oss.setup_mutex);
#endif
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
if (!substream_count)
return 0;
snd_device_initialize(&pstr->dev, pcm->card);
pstr->dev.groups = pcm_dev_attr_groups;
dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, stream == SNDRV_PCM_STREAM_PLAYBACK ? ‘p‘ : ‘c‘);//playback stream的名字
if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);//将playback stream挂到/proc目录下,我们通过cat可以查看。
if (err < 0) {
pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {//一个playback stream下可有多个substream,创建substream
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
if (!substream)
return -ENOMEM;
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL)//将substream链接到playback stream的substream成员中。
pstr->substream = substream;
else
prev->next = substream;
if (!pcm->internal) {
err = snd_pcm_substream_proc_init(substream);//将substream挂到/proc下。
if (err < 0) {
pcm_err(pcm,"Error in snd_pcm_stream_proc_init\n");
if (prev == NULL)
pstr->substream = NULL;
else
prev->next = NULL;
kfree(substream);
return err;
}
}
substream->group = &substream->self_group;
spin_lock_init(&substream->self_group.lock);
mutex_init(&substream->self_group.mutex);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
return 0;
}
以上是关于ALSA driver --PCM device 创建过程的主要内容,如果未能解决你的问题,请参考以下文章
Linux ALSA 之三:简单的 ALSA Driver 实现
c_cpp 这个脚本以编程方式在Linux中找到ALSA设备,这可能有点棘手。 #alsa#device-driver #linux #c