PCM的create和open过程及核心层数据结构[kernel 4.19]
4.19 核心层数据结构
核心层为用户空间提供逻辑设备接口, 同时为驱动提供接口来驱动硬件设备, 主要位于sound/core目录下.
核心层数据结构自上向下为
snd_card //表示一个声卡实例, 包含多个声卡设备
└──snd_device //表示一个声卡设备部件
└── snd_pcm //表示一个PCM设备, 声卡设备的一种, 用于播放和录音
│ └── snd_pcm_str //表示PCM流, 分为playback和capture
│ └── snd_pcm_substream //PCM子流, 用于音频的播放或录制
│ └── snd_pcm_ops //PCM流操作集
└── snd_control //表示Control设备, 声卡设备的一种, 用于控制声卡
此处暂时先略过snd_control部分的分析。
snd_card
struct snd_card {
int number; /* number of soundcard (声卡设备的索引) */
char id[16]; /* id string of this card(标识符) */
char driver[16]; /* 驱动名 */
char shortname[32]; /* 声卡的短名 */
char longname[80]; /* 声卡名 */
char irq_descr[32]; /* Interrupt description */
char mixername[80]; /* mixer name */
char components[128]; /* card components delimited with space */
struct module *module; /* top-level module */
void *private_data; /* 声卡私有数据 */
void (*private_free) (struct snd_card *card); /* 私有数据释放回调 */
struct list_head devices; /* 该声卡下所有设备 */
struct device ctl_dev; /* control设备*/
unsigned int last_numid; /* last used numeric ID */
struct rw_semaphore controls_rwsem; /* controls list lock */
rwlock_t ctl_files_rwlock; /* ctl_files list lock */
int controls_count; /* count of all controls */
int user_ctl_count; /* count of all user controls */
struct list_head controls; /* 该声卡下所有control设备 */
struct list_head ctl_files; /* active control files */
struct snd_info_entry *proc_root; /* root for soundcard specific files */
struct snd_info_entry *proc_id; /* the card id */
struct proc_dir_entry *proc_root_link; /* number link to real id */
struct list_head files_list; /* 与此声卡关联的所有文件 */
struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */
spinlock_t files_lock; /* lock the files for this card */
int shutdown; /* this card is going down */
struct completion *release_completion;
struct device *dev; /* 声卡相关的device */
struct device card_dev; /* cardX object for sysfs(用于sysfs, 代表该声卡) */
const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
bool registered; /* card_dev is registered?(是否注册标记) */
wait_queue_head_t remove_sleep;
#ifdef CONFIG_PM
unsigned int power_state; /* power state */
wait_queue_head_t power_sleep;
#endif
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
struct snd_mixer_oss *mixer_oss;
int mixer_oss_change_count;
#endif
};
snd_device
struct snd_device {
struct list_head list; /* 所有注册的device链表 */
struct snd_card *card; /*设备所属声卡 */
enum snd_device_state state; /* 设备状态 */
enum snd_device_type type; /* 设备类型 */
void *device_data; /* device structure,指向具体的声卡设备,如snd_pcm或snd_control */
struct snd_device_ops *ops; /* 设备操作函数集 */
};
snd_pcm
在调用_snd_pcm_new时就会创建一个snd_pcm类型的PCM 实例.
struct snd_pcm {
struct snd_card *card;//该PCM设备所属声卡
struct list_head list;//所有注册的PCM设备链表,一个Card可能有多个PCM 实例
int device; /* device number,即PCM设备的索引 */
unsigned int info_flags;
unsigned short dev_class;
unsigned short dev_subclass;
char id[64]; //PCM设备标识
char name[80]; //PCM设备名
struct snd_pcm_str streams[2];//PCM的playback和capture stream
struct mutex open_mutex;
wait_queue_head_t open_wait;
void *private_data;//PCM设备私有数据
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 IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss oss;
#endif
};
snd_pcm_str
一个PCM 实例分别有一个playback, capture stream,由snd_pcm_new_stream创建。
playback 和capture stream是一个snd_pcm_str的结构体。
struct snd_pcm_str {
int stream; /* stream (direction),playback stream 或 capture stream */
struct snd_pcm *pcm;//当前的PCM 实例
/* -- substreams -- */
unsigned int substream_count;//substream的数目。
unsigned int substream_opened;//已经open的substream数目。每次open +1,close -1.
struct snd_pcm_substream *substream;//playback stream 或 capture stream的substream链表
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
/* -- 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结构
};
snd_pcm_substream
substream的结构体snd_pcm_substream :
struct snd_pcm_substream {
struct snd_pcm *pcm;//PCM 实例
struct snd_pcm_str *pstr;//playback stream or substream
//private_data通常是和PCM 实例的private data相同。
void *private_data; /* copied from pcm->private_data */
int number;//当前substream的index
char name[32]; /* substream name */
int stream; /* stream (direction),playback or capture */
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;
size_t dma_max;
/* -- hardware operations -- */
const struct snd_pcm_ops *ops; //substream的操作函数,在创建完PCM 实例后,调用snd_pcm_set_ops设置PCM实例playback stream的每一个substream的操作函数。
/* -- runtime information -- */
struct snd_pcm_runtime *runtime;//runtime信息
/* -- timer section -- */
struct snd_timer *timer; /* timer */
unsigned timer_running: 1; /* time is running */
long wait_time; /* time in ms for R/W to wait for avail */
/* -- 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 IS_ENABLED(CONFIG_SND_PCM_OSS)
/* -- 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;
};
4.19 pcm创建过程
首先,在soc-core.c中
soc_probe [soc-core.c]
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
/*
* no card, so machine driver should be registering card
* we should not be here in that case so ret error
*/
if (!card)
return -EINVAL;
dev_warn(&pdev->dev,
"ASoC: machine %s should use snd_soc_register_card()\n",
card->name);
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
return snd_soc_register_card(card);
}
snd_soc_register_card
Register a card with the ASoC core
/**
* snd_soc_register_card - Register a card with the ASoC core
*
* @card: Card to register
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
int i, ret;
struct snd_soc_pcm_runtime *rtd;
if (!card->name || !card->dev)
return -EINVAL;
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
ret = soc_init_dai_link(card, link);
if (ret) {
dev_err(card->dev, "ASoC: failed to init link %s\n",
link->name);
return ret;
}
}
dev_set_drvdata(card->dev, card);
snd_soc_initialize_card_lists(card);
INIT_LIST_HEAD(&card->dai_link_list);
card->num_dai_links = 0;
INIT_LIST_HEAD(&card->rtd_list);
card->num_rtd = 0;
INIT_LIST_HEAD(&card->dapm_dirty);
INIT_LIST_HEAD(&card->dobj_list);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
ret = snd_soc_instantiate_card(card);
if (ret != 0)
return ret;
/* deactivate pins to sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int j;
for (j = 0; j < rtd->num_codecs; j++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
if (!codec_dai->active)
pinctrl_pm_select_sleep_state(codec_dai->dev);
}
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
snd_soc_instantiate_card
太长,只列部分
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai_link *dai_link;
int ret, i, order;
/* add predefined DAI links to the list */
for (i = 0; i < card->num_links; i++)
snd_soc_add_dai_link(card, card->dai_link+i);
/* card bind complete so register a sound card */
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
dev_err(card->dev,
"ASoC: can't create sound card for card %s: %d\n",
card->name, ret);
goto base_error;
}
……
snd_soc_dapm_new_widgets(card);
ret = snd_card_register(card->snd_card);
if (ret < 0) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
ret);
goto probe_aux_dev_err;
}
……
}
snd_card_register [init.c]
/**
* snd_card_register - register the soundcard
* @card: soundcard structure
*
* This function registers all the devices assigned to the soundcard.
* Until calling this, the ALSA control interface is blocked from the
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
* Return: Zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
if (!card->registered) {
err = device_add(&card->card_dev);
if (err < 0)
return err;
card->registered = true;
}
if ((err = snd_device_register_all(card)) < 0)
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* 已经注册过 */
mutex_unlock(&snd_card_mutex);
return snd_info_card_register(card); /* register pending info */
}
if (*card->id) {
/* 根据给定的字符串创建唯一的ID名称 */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
} else {
/* 从shortname或longname创建ID */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src,
retrieve_id_from_card_name(src));
}
snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
return 0;
}
EXPORT_SYMBOL(snd_card_register);
snd_device_register_all
/*
* register all the devices on the card.
* called from init.c
*/
int snd_device_register_all(struct snd_card *card)
{
struct snd_device *dev;
int err;
if (snd_BUG_ON(!card))
return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
err = __snd_device_register(dev);
if (err < 0)
return err;
}
return 0;
}
__snd_device_register
static int __snd_device_register(struct snd_device *dev)
{
if (dev->state == SNDRV_DEV_BUILD) {
if (dev->ops->dev_register) {
int err = dev->ops->dev_register(dev);
if (err < 0)
return err;
}
dev->state = SNDRV_DEV_REGISTERED;
}
return 0;
}
调用了pcm.c中的.dev_register = snd_pcm_dev_register
_snd_pcm_new [pcm.c]
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 = {//device的操作函数
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,//此处指定注册函数,在__snd_device_register调用
.dev_disconnect = snd_pcm_dev_disconnect,
};
static struct snd_device_ops internal_ops = {
.dev_free = snd_pcm_dev_free,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//为PCM 实例创建空间
if (!pcm)
return -ENOMEM;
pcm->card = card;//将声卡保存在PCM 实例中
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));
//为PCM 实例创建playback stream
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
playback_count);
if (err < 0)
goto free_pcm;
//为PCM 实例创建capture stream
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
if (err < 0)
goto free_pcm;
//以PCM 实例创建snd_device并挂载到Card上。
err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
internal ? &internal_ops : &ops);
if (err < 0)
goto free_pcm;
if (rpcm)
*rpcm = pcm;
return 0;
free_pcm:
snd_pcm_free(pcm);
return err;
}
snd_pcm_new_stream
/**
* snd_pcm_new_stream - 创建一个新的 PCM stream
* @pcm: the pcm instance
* @stream: the stream direction, SNDRV_PCM_STREAM_XXX
* @substream_count: the number of substreams
*
* Creates a new stream for the pcm.
* The corresponding stream on the pcm must have been empty before
* calling this, i.e. zero must be given to the argument of
* snd_pcm_new().
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
int idx, err;
//当前的playback stream or capture stream
struct snd_pcm_str *pstr = &pcm->streams[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'); //设置pcm设备名称
if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);//将playback or capture stream挂载到/proc目录下
if (err < 0) {
pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
//一个playback or capture stream下可有多个substream,创建substream
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
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 or capture 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;
}
EXPORT_SYMBOL(snd_pcm_new_stream);
snd_pcm_dev_register
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
struct snd_pcm *pcm;
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
//snd_device的device_data即PCM实例,是在snd_device_new时将pcm赋值给device_data
pcm = device->device_data;
mutex_lock(®ister_mutex);
err = snd_pcm_add(pcm);
if (err)
goto unlock;
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
/* register pcm */
//注册pcm实例的device,device的操作函数为snd_pcm_f_ops.
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
if (err < 0) {
list_del_init(&pcm->list);
goto unlock;
}
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);//初始化每个substream 的timer
}
pcm_call_notify(pcm, n_register);
unlock:
mutex_unlock(®ister_mutex);
return err;
}
snd_register_device
Register the ALSA device file for the card
/**
* snd_register_device - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
* @device: the device to register
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
* Return: Zero if successful, or a negative error code on failure.
*/
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);
4.19 pcm open过程
大致流程如下:
snd_pcm_open("default", SND_PCM_STREAM_PLAYBACK) // alsa-lib接口
├── open("/dev/snd/controlC0") // 打开控制设备; 主设备116, 次设备0
└── open("/dev/snd/pcmC0D0p") // 打开PCM设备; 主设备116, 次设备16
└──snd_open() // 根据主设备号找到该入口
└──snd_minors[minor] // 根据次设备号找到对应操作集
└──snd_ctl_f_ops::open() // control设备打开方法
│ └──snd_ctl_open()
└──snd_pcm_f_ops::open() // PCM设备打开方法
└──snd_pcm_playback_open()
└──snd_lookup_minor_data() // 根据次设备号查找对应PCM设备(snd_pcm)
└──snd_pcm_open() // 打开PCM播放子流
pcm_native.c中
snd_pcm_f_ops
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open, // 打开/dev/snd/pcmC0D0p时调用到此函数
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
snd_pcm_playback_open
static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
int err = nonseekable_open(inode, file);
if (err < 0)
return err;
//获取注册设备的user data
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
if (pcm)
snd_card_unref(pcm->card);
return err;
}
snd_pcm_open
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
wait_queue_entry_t wait;
if (pcm == NULL) {
err = -ENODEV;
goto __error1;
}
err = snd_card_file_add(pcm->card, file); //add the file to the file list of the card
if (err < 0)
goto __error1;
if (!try_module_get(pcm->card->module)) {
err = -EFAULT;
goto __error2;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&pcm->open_wait, &wait);
mutex_lock(&pcm->open_mutex);
while (1) {
err = snd_pcm_open_file(file, pcm, stream);
if (err >= 0)
break;
if (err == -EAGAIN) {
if (file->f_flags & O_NONBLOCK) {
err = -EBUSY;
break;
}
} else
break;
set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
if (pcm->card->shutdown) {
err = -ENODEV;
break;
}
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
}
remove_wait_queue(&pcm->open_wait, &wait);
mutex_unlock(&pcm->open_mutex);
if (err < 0)
goto __error;
return err;
__error:
module_put(pcm->card->module);
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
return err;
}
snd_pcm_open_file
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
int err;
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
if (pcm_file == NULL) {
snd_pcm_release_substream(substream);
return -ENOMEM;
}
pcm_file->substream = substream;
if (substream->ref_count == 1) {
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file;
return 0;
}
snd_pcm_open_substream
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_substream *substream;
int err;
err = snd_pcm_attach_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
if (substream->ref_count > 1) {
*rsubstream = substream;
return 0;
}
err = snd_pcm_hw_constraints_init(substream); //硬件参数约束初始化
if (err < 0) {
pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n");
goto error;
}
if ((err = substream->ops->open(substream)) < 0)
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream); //
if (err < 0) {
pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n");
goto error;
}
*rsubstream = substream;
return 0;
error:
snd_pcm_release_substream(substream);
return err;
}
EXPORT_SYMBOL(snd_pcm_open_substream);
经验证,hw.info是在substream->ops->open中设置的
以rk3308为例,hw:0,0的.open指向soc_pcm_open,而hw:7,0的.open指向的是loopback_open。
if ((err = substream->ops->open(substream)) < 0)
goto error;
soc_pcm_open [soc-pcm.c]
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
* startup for the cpu DAI, component, machine and codec DAI.
* 当PCM子流打开时,由ALSA调用,然后初始化runtime->hw的记录,并可以分配任何专用数据。
* 这也要求启动cpu DAI,component,machine和codec DAI。
*/
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_component *component;
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
const char *codec_dai_name = "multicodec";
int i, ret = 0;
pinctrl_pm_select_default_state(cpu_dai->dev);
for (i = 0; i < rtd->num_codecs; i++)
pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
pm_runtime_get_sync(component->dev);
}
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
/* 启动音频子系统 */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
if (ret < 0) {
dev_err(cpu_dai->dev, "ASoC: can't open interface"
" %s: %d\n", cpu_dai->name, ret);
goto out;
}
}
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
if (!component->driver->ops ||
!component->driver->ops->open)
continue;
ret = component->driver->ops->open(substream); //在此处调用了dmaengine_pcm_open,4.19中删去了platform部分,pcm dmaengine从platform改为component。
if (ret < 0) {
dev_err(component->dev,
"ASoC: can't open component %s: %d\n",
component->name, ret);
goto component_err;
}
}
component = NULL;
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream,
codec_dai);
if (ret < 0) {
dev_err(codec_dai->dev,
"ASoC: can't open codec %s: %d\n",
codec_dai->name, ret);
goto codec_dai_err;
}
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
codec_dai->tx_mask = 0;
else
codec_dai->rx_mask = 0;
}
if (rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream);
if (ret < 0) {
pr_err("ASoC: %s startup failed: %d\n",
rtd->dai_link->name, ret);
goto machine_err;
}
}
/* Dynamic PCM DAI links compat checks use dynamic capabilities */
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
goto dynamic;
/* Check that the codec and cpu DAIs are compatible */
soc_pcm_init_runtime_hw(substream);
if (rtd->num_codecs == 1)
codec_dai_name = rtd->codec_dai->name;
if (soc_pcm_has_symmetry(substream))
runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
ret = -EINVAL;
if (!runtime->hw.rates) {
printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
codec_dai_name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
codec_dai_name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
runtime->hw.channels_min > runtime->hw.channels_max) {
printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
codec_dai_name, cpu_dai->name);
goto config_err;
}
soc_pcm_apply_msb(substream);
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active) {
ret = soc_pcm_apply_symmetry(substream, cpu_dai);
if (ret != 0)
goto config_err;
}
for (i = 0; i < rtd->num_codecs; i++) {
if (rtd->codec_dais[i]->active) {
ret = soc_pcm_apply_symmetry(substream,
rtd->codec_dais[i]);
if (ret != 0)
goto config_err;
}
}
pr_debug("ASoC: %s <-> %s info:\n",
codec_dai_name, cpu_dai->name);
pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
dynamic:
snd_soc_runtime_activate(rtd, substream->stream);
mutex_unlock(&rtd->pcm_mutex);
return 0;
config_err:
if (rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
machine_err:
i = rtd->num_codecs;
codec_dai_err:
while (--i >= 0) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
}
component_err:
soc_pcm_components_close(substream, component);
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&rtd->pcm_mutex);
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
}
for (i = 0; i < rtd->num_codecs; i++) {
if (!rtd->codec_dais[i]->active)
pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
}
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
return ret;
}