我对linux理解之alsa二 (2011-10-28 10:43)
------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:
amingriyue.blog.chinaunix.net
------------------------------------------
static int wm8994_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = wm8994_codec;
int ret = 0;
socdev->card->codec = wm8994_codec;//已经在codec列表里
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);//注册pcms,1
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms\n");
return ret;
}
snd_soc_add_controls(codec, wm8994_snd_controls,//添加控制,2
ARRAY_SIZE(wm8994_snd_controls));
snd_soc_dapm_new_controls(codec, wm8994_dapm_widgets,//添加dapm的widgets,3
ARRAY_SIZE(wm8994_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));//添加dapm的路径,4
// wm8994_audio_start_dac(codec);
// wm8994_audio_stop_speaker(codec);
if (tda19989_get_status()) {
wm8994_update_bit(codec, WM8994_POWER_MANAGEMENT_1,
0, WM8994_SPKOUTR_ENA_MASK | WM8994_SPKOUTL_ENA_MASK);
wm8994_update_bit(codec, WM8994_AIF1_DAC1_FILTERS_1,
WM8994_AIF1DAC1_MUTE, WM8994_AIF1DAC1_MUTE);
tda19989_pcm_mute((bool)1);
}
ret = snd_soc_init_card(socdev);//初始化card,5
if (ret < 0) {
printk(KERN_ERR "wm8994: failed to register card\n");
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return ret;
}
INIT_WORK(&wm8994_trigger_event, wm8994_do_trigger);
return 0;
}
我们将分为上面标记的5部分进行分析。
1,snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1):
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
{
struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec = card->codec;
int ret = 0, i;
mutex_lock(&codec->mutex);
/* register a sound card */
ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);//注册一个声卡,其中将会有control的ops注册到card的devices里面,1-1
if (ret < 0) {
printk(KERN_ERR "asoc: can't create sound card for codec %s\n",
codec->name);
mutex_unlock(&codec->mutex);
return -ENODEV;
}
codec->socdev = socdev;
codec->card->dev = socdev->dev;
codec->card->private_data = codec;
strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
/* create the pcms */
for (i = 0; i < card->num_links; i++) {//根据dai连接数目,决定创建多少个pcm
ret = soc_new_pcm(socdev, &card->dai_link[i], i);//为每个card新建pcm流,1-2
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm %s\n",
card->dai_link[i].stream_name);
mutex_unlock(&codec->mutex);
return ret;
}
}
mutex_unlock(&codec->mutex);
return ret;
}
1-1,snd_card_create(idx, xid, codec->owner, 0, &codec->card):
int snd_card_create(int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
int err, idx2;
if (snd_BUG_ON(!card_ret))
return -EINVAL;
*card_ret = NULL;
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);//申请声卡空间
if (!card)
return -ENOMEM;
if (xid)
strlcpy(card->id, xid, sizeof(card->id));
err = 0;
mutex_lock(&snd_card_mutex);
if (idx < 0) {//从上面知道idx=-1
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
/* idx == -1 == 0xffff means: take any free slot */
if (~snd_cards_lock & idx & 1<<idx2) {
if (module_slot_match(module, idx2)) {
idx = idx2;//找到对应module名字的minor
break;
}
}
}
if (idx < 0) {//如果上面没找到
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
/* idx == -1 == 0xffff means: take any free slot */
if (~snd_cards_lock & idx & 1<<idx2) {
if (!slots[idx2] || !*slots[idx2]) {
idx = idx2;//找第一个非空的slot
break;
}
}
}
if (idx < 0)//判断idx的合法性
err = -ENODEV;
else if (idx < snd_ecards_limit) {
if (snd_cards_lock & (1 << idx))
err = -EBUSY; /* invalid */
} else if (idx >= SNDRV_CARDS)
err = -ENODEV;
if (err < 0) {
mutex_unlock(&snd_card_mutex);
snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
idx, snd_ecards_limit - 1, err);
goto __error;
}
snd_cards_lock |= 1 << idx; /* lock it */
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
card->number = idx;//声卡的序号
card->module = module;//声卡所属的module
INIT_LIST_HEAD(&card->devices);//初始化card的设备列表
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
INIT_LIST_HEAD(&card->controls);//初始化card的控制列表
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
init_waitqueue_head(&card->shutdown_sleep);
#ifdef CONFIG_PM
mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
#endif
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
err = snd_ctl_create(card);//创建controlC节点,1-1-1
if (err < 0) {
snd_printk(KERN_ERR "unable to register control minors\n");
goto __error;
}
err = snd_info_card_create(card);//在/proc/asound/下创建card0文件夹
if (err < 0) {
snd_printk(KERN_ERR "unable to create card info\n");
goto __error_ctl;
}
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
*card_ret = card;//返回card
return 0;
__error_ctl:
snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
__error:
kfree(card);
return err;
}
1-1-1,snd_ctl_create(card):
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,//这里会注册一个控制节点,但不是在这里执行
.dev_disconnect = snd_ctl_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//将ops添加到card devices里面,1-1-1-1
}
1-1-1-1,snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops)
int snd_device_new(struct snd_card *card, snd_device_type_t type,
void *device_data, struct snd_device_ops *ops)
{
struct snd_device *dev;
if (snd_BUG_ON(!card || !device_data || !ops))
return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
snd_printk(KERN_ERR "Cannot allocate device\n");
return -ENOMEM;
}
dev->card = card;//device的card
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;//将ops添加到card的device中
list_add(&dev->list, &card->devices); //添加到card的设备列表里面/* add to the head of list */
return 0;
}
snd_soc_new_pcms()的1-1部分主要做了创建声卡,将控制节点添加到card设备列表里面,同时在/proc/asound/下创建card0文件夹。
下面我们看1-2部分soc_new_pcm(socdev, &card->dai_link[i], i):
static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)
{
struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec = card->codec;
struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *codec_dai = dai_link->codec_dai;
struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
struct snd_soc_pcm_runtime *rtd;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);//申请snd_soc_pcm_runtim空间
if (rtd == NULL)
return -ENOMEM;
rtd->dai = dai_link;//将dai_link赋值给pcm_runtime
rtd->socdev = socdev;//将soc_device赋值给pcm_runtime
codec_dai->codec = card->codec;//将codec赋值给codec_dai
/* check client and interface hw capabilities */
sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,//WM8994 WM8994-0
num);
if (codec_dai->playback.channels_min)
playback = 1;
if (codec_dai->capture.channels_min)
capture = 1;
ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,//创建一个新的pcm实例,1-2-1
capture, &pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n",
codec->name);
kfree(rtd);
return ret;
}
dai_link->pcm = pcm;//将pcm赋值给dai_link
pcm->private_data = rtd;
soc_pcm_ops.mmap = platform->pcm_ops->mmap;//初始化soc_pcm_ops,platform有在imx_pcm.c中定义
soc_pcm_ops.pointer = platform->pcm_ops->pointer;
soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
soc_pcm_ops.copy = platform->pcm_ops->copy;
soc_pcm_ops.silence = platform->pcm_ops->silence;
soc_pcm_ops.ack = platform->pcm_ops->ack;
soc_pcm_ops.page = platform->pcm_ops->page;
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);//设置pcm的ops,pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]->substream->ops= &soc_pcm_ops
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
ret = platform->pcm_new(codec->card, codec_dai, pcm);//申请playback和capture buffer
if (ret < 0) {
printk(KERN_ERR "asoc: platform pcm constructor failed\n");
kfree(rtd);
return ret;
}
pcm->private_free = platform->pcm_free;
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}
1-2-1,snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback, capture, &pcm):
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm ** rpcm)
{
struct snd_pcm *pcm;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,//会注册pcm设备,但是不是现在执行
.dev_disconnect = snd_pcm_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//申请pcm空间
if (pcm == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM\n");
return -ENOMEM;
}
pcm->card = card;//pcm的card
pcm->device = device;//pcm的device
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));//将id赋值给pcm->id
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {//新的playback stream,1-2-1-1
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {//capture stream
snd_pcm_free(pcm);
return err;
}
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {//将ops添加到card devices里面,后面将会用到这个ops,与上面添加ctl节点相同
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
1-2-1-1,snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count):
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];
struct snd_pcm_substream *substream, *prev;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
mutex_init(&pstr->oss.setup_mutex);
#endif
//这里对应/proc/asound/card0/
pstr->stream = stream;//stream标记了是PLAYBACK还是CAPTURE
pstr->pcm = pcm;//对应的pcm
pstr->substream_count = substream_count;//substream数目
if (substream_count > 0) {
err = snd_pcm_stream_proc_init(pstr);// 创建pcm0p或pcm0c目录
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);//对每一个申请substream空间
if (substream == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM substream\n");
return -ENOMEM;
}
substream->pcm = pcm;//该substream对应的pcm
substream->pstr = pstr;
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
snprintf(substream->latency_id, sizeof(substream->latency_id),
"ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device,
(stream ? 'c' : 'p'), idx);
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL)
pstr->substream = substream;
else
prev->next = substream;
err = snd_pcm_substream_proc_init(substream);//创建sub0文件夹及其内部的一些文件
if (err < 0) {
snd_printk(KERN_ERR "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);
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;
}
根据stream的类型(PLAYBACK/CAPTURE)新建substream流,并在/proc/asound/card0/下新建相关文件夹和文件。
1-2部分主要是新建pcm实例,设置pcm的ops,申请playback和capture的dma buffer。
这样我们第一部分就分析到此了,下面看第2部分snd_soc_add_controls(codec, wm8994_snd_controls, ARRAY_SIZE(wm8994_snd_controls)):
2,我们线看wm8994_snd_controls定义:
static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
1, 119, 0, digital_tlv),
....//其它的宏类似
};
我们这里就选取这一个control的定义分析,因为其它都是比较类似的。我们看下SOC_DOUBLE_R_TLV定义:
#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_2r, \
.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
.max = xmax, .invert = xinvert} }
那SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, WM8994_AIF1_ADC1_RIGHT_VOLUME, 1, 119, 0, digital_tlv)就变成:
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = ("AIF1ADC1 Volume"),\
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ //读写权限
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (digital_tlv), \
.info = snd_soc_info_volsw_2r, \
.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = WM8994_AIF1_ADC1_LEFT_VOLUME, .rreg = WM8994_AIF1_ADC1_RIGHT_VOLUME, .shift = 1, \
.max = 119, .invert = 0} }
我们解释一下各个字段:
(1) iface 字段定义了control 的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,通常是MIXER,对于不属于mixer的全局控制,使用CARD。如果关联于某类设备,则使用HWDEP、 PCM、RAWMIDI、TIMER 或SEQUENCER。
(2) name 是名称标识字符串,control 的名称非常重要,因为control 的作用由名称来区分。对于名称相同的control,则使用index 区分。name 定义的标准是“SOURCE DIRECTION FUNCTION”即“源、方向、功能”:
SOURCE 定义了control 的源,如“Master”、“PCM”、“CD”和“Line”等
DIRECTION方向则为“Playback”、“Capture”、“Bypass Playback”或“Bypass Capture”,如果方向省略,意味着playback 和capture双向。
FUNCTION可以是“Switch”、“Volume”和“Route”等
iface 和 name 可以通过上层输入amixer命令获取,如下:
# # amixer controls
numid=100,iface=CARD,name='BB'
numid=30,iface=MIXER,name='Headphone Playback ZC Switch'
numid=6,iface=MIXER,name='AIF1ADC1 Volume'
(3) info()函数用于获得该control 的详细信息,该函数必须填充传递给它的第二个参数。
(4) get()函数用于得到control 的目前值并返回用户空间。
(5) put()函数用于从用户空间写入值,如果值被改变,该函数返回1,否则返回0;如果发生错误,该函数返回错误码。
get()和put()的第二个参数的类型为snd_ctl_elem_value。snd_ctl_elem_value结构体的内部也包含一个由integer、integer64、enumerated 等组成的值联合体,它的具体类型依赖于control 的类型和info()函数。
对于get()和put()函数而言,如果control 有多于一个元素,即count>1,则每个元素都需要被返回或写入。在使用amixer命令配置声卡时都会调用到get()和put()函数。 get()和put()函数中讲判断设置值与原有值,判断是否配置寄存器。
我们下面看snd_soc_add_controls定义:
int snd_soc_add_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls)
{
struct snd_card *card = codec->card;
int err, i;
for (i = 0; i < num_controls; i++) {//一个一个添加
const struct snd_kcontrol_new *control = &controls[i];
err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));//添加控制实例到card,2-1,2-2
if (err < 0) {
dev_err(codec->dev, "%s: Failed to add %s\n",
codec->name, control->name);
return err;
}
}
return 0;
}
2-1,snd_soc_cnew(control, codec, NULL):
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
void *data, char *long_name)
{
struct snd_kcontrol_new template;
memcpy(&template, _template, sizeof(template));
if (long_name)
template.name = long_name;
template.index = 0;
return snd_ctl_new1(&template, data);//返回kcontrol,2-1-1
}
2-1-1,snd_ctl_new1(&template, data):
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
struct snd_kcontrol kctl;
unsigned int access;
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface;//赋值iface
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
if (strcmp(ncontrol->name, kctl.id.name) != 0)//如果不等,说明名字被截断
snd_printk(KERN_WARNING
"Control name '%s' truncated to '%s'\n",
ncontrol->name, kctl.id.name);
}
kctl.id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE ://赋值读写权限
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
kctl.info = ncontrol->info;//info函数
kctl.get = ncontrol->get;
kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);//新生成控制实例,2-1-1-1
}
2-1-1-1,snd_ctl_new(&kctl, access):
static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
unsigned int access)
{
struct snd_kcontrol *kctl;
unsigned int idx;
if (snd_BUG_ON(!control || !control->count))
return NULL;
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);//申请kctl空间
if (kctl == NULL) {
snd_printk(KERN_ERR "Cannot allocate control instance\n");
return NULL;
}
*kctl = *control;//拷贝到kctl
for (idx = 0; idx < kctl->count; idx++)
kctl->vd[idx].access = access;//访问权限
return kctl;//返回kctl
}
2-2,snd_ctl_add(card, snd_soc_cnew(control, codec, NULL)):
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id;
unsigned int idx;
int err = -EINVAL;
if (! kcontrol)
return err;
if (snd_BUG_ON(!card || !kcontrol->info))
goto error;
id = kcontrol->id;//id包含iface,device
down_write(&card->controls_rwsem);
if (snd_ctl_find_id(card, &id)) {//查找看这个control是否已经在card里面
up_write(&card->controls_rwsem);
snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
id.iface,
id.device,
id.subdevice,
id.name,
id.index);
err = -EBUSY;
goto error;
}
if (snd_ctl_find_hole(card, kcontrol->count) < 0) {//申请numid
up_write(&card->controls_rwsem);
err = -ENOMEM;
goto error;
}
list_add_tail(&kcontrol->list, &card->controls);//将该kcontrol添加到card的control列表里面
card->controls_count += kcontrol->count;//增加控制的数目
kcontrol->id.numid = card->last_numid + 1;//更新id的numid
card->last_numid += kcontrol->count;//更新card的last_numid
up_write(&card->controls_rwsem);
for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);//通知有添加事件产生
return 0;
error:
snd_ctl_free_one(kcontrol);
return err;
}
我们看到最终是将control转化成kcontrol添加到card->controls列表里面,以备查询。
3,snd_soc_dapm_new_controls(codec, wm8994_dapm_widgets, ARRAY_SIZE(wm8994_dapm_widgets)):
int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
const struct snd_soc_dapm_widget *widget,
int num)
{
int i, ret;
for (i = 0; i < num; i++) {
ret = snd_soc_dapm_new_control(codec, widget);//单个widget控制,3-1
if (ret < 0) {
printk(KERN_ERR
"ASoC: Failed to create DAPM control %s: %d\n",
widget->name, ret);
return ret;
}
widget++;
}
return 0;
}
3-1,snd_soc_dapm_new_control(codec, widget):
int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
if ((w = dapm_cnew_widget(widget)) == NULL)
return -ENOMEM;
w->codec = codec;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
list_add(&w->list, &codec->dapm_widgets);//添加到codec的dapm_widgets列表
/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
return 0;
}
最后将widget添加到codec的dapm_widgets列表。
4,snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)):
int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
const struct snd_soc_dapm_route *route, int num)
{
int i, ret;
for (i = 0; i < num; i++) {
ret = snd_soc_dapm_add_route(codec, route->sink,//添加单个route,4-1
route->control, route->source);
if (ret < 0) {
printk(KERN_ERR "Failed to add route %s->%s\n",
route->source,
route->sink);
return ret;
}
route++;
}
return 0;
}
4-1,snd_soc_dapm_add_route(codec, route->sink, route->control, route->source):
static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
const char *sink, const char *control, const char *source)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
int ret = 0;
/* find src and dest widgets */
list_for_each_entry(w, &codec->dapm_widgets, list) {
if (!wsink && !(strcmp(w->name, sink))) {
wsink = w;
continue;
}
if (!wsource && !(strcmp(w->name, source))) {
wsource = w;
}
}
if (wsource == NULL || wsink == NULL)
return -ENODEV;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);//申请snd_soc_dapm_path空间
if (!path)
return -ENOMEM;
path->source = wsource;//赋值source
path->sink = wsink;//赋值sink
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
/* check for external widgets */
if (wsink->id == snd_soc_dapm_input) {//检查是否为外部组件
if (wsource->id == snd_soc_dapm_micbias ||
wsource->id == snd_soc_dapm_mic ||
wsource->id == snd_soc_dapm_line ||
wsource->id == snd_soc_dapm_output)
wsink->ext = 1;
}
if (wsource->id == snd_soc_dapm_output) {
if (wsink->id == snd_soc_dapm_spk ||
wsink->id == snd_soc_dapm_hp ||
wsink->id == snd_soc_dapm_line ||
wsink->id == snd_soc_dapm_input)
wsource->ext = 1;
}
/* connect static paths */
if (control == NULL) {
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
return 0;
}
/* connect dynamic paths */
switch(wsink->id) {//sink的id连接动态路径
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
case snd_soc_dapm_vmid:
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
return 0;
case snd_soc_dapm_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(codec, wsource, wsink, path, control,
&wsink->kcontrols[0]);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 0;
return 0;
}
return 0;
err:
printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
control, sink);
kfree(path);
return ret;
}
这个函数主要功能是将两个widget连接起来。
5,snd_soc_init_card(socdev):
int snd_soc_init_card(struct snd_soc_device *socdev)
{
struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec = card->codec;
int ret = 0, i, ac97 = 0, err = 0;
for (i = 0; i < card->num_links; i++) {
if (card->dai_link[i].init) {
err = card->dai_link[i].init(codec);//dai link的init,对应imx_3stack_wm8994_init,5-1
if (err < 0) {
printk(KERN_ERR "asoc: failed to init %s\n",
card->dai_link[i].stream_name);
continue;
}
}
if (card->dai_link[i].codec_dai->ac97_control) {
ac97 = 1;
}
}
snprintf(codec->card->shortname, sizeof(codec->card->shortname),
"%s", card->name);
snprintf(codec->card->longname, sizeof(codec->card->longname),
"%s (%s)", card->name, codec->name);
/* Make sure all DAPM widgets are instantiated */
snd_soc_dapm_new_widgets(codec);//初始化dapm widgets
ret = snd_card_register(codec->card);//注册card,5-2
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
codec->name);
goto out;
}
mutex_lock(&codec->mutex);
#ifdef CONFIG_SND_SOC_AC97_BUS
/* Only instantiate AC97 if not already done by the adaptor
* for the generic AC97 subsystem.
*/
if (ac97 && strcmp(codec->name, "AC97") != 0) {
ret = soc_ac97_dev_register(codec);
if (ret < 0) {
printk(KERN_ERR "asoc: AC97 device register failed\n");
snd_card_free(codec->card);
mutex_unlock(&codec->mutex);
goto out;
}
}
#endif
err = snd_soc_dapm_sys_add(socdev->dev);//创建dapm_widget属性文件
if (err < 0)
printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
err = device_create_file(socdev->dev, &dev_attr_codec_reg);//创建codec_reg属性文件
if (err < 0)
printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
soc_init_codec_debugfs(codec);
mutex_unlock(&codec->mutex);
out:
return ret;
}
5-1,card->dai_link[i].init(codec):
我们在前面已经知道dai_link的定义,所以init对应imx_3stack_wm8994_init:
static int imx_3stack_wm8994_init(struct snd_soc_codec *codec)
{
int i, ret;
unsigned int hp;
#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
ret = snd_ctl_add(codec->card,
snd_soc_cnew(&asrc_controls[i], codec, NULL));
if (ret < 0)
return ret;
}
asrc_ssi_data.output_sample_rate = wm8994_rates[asrc_func];
#endif
/* Add imx_3stack specific controls */
for (i = 0; i < ARRAY_SIZE(wm8994_machine_controls); i++) {//添加cpu端的控制
ret = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8994_machine_controls[i],
codec, NULL));
if (ret < 0)
return ret;
}
/* Add imx_3stack specific widgets */
snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,//添加dapm组件
ARRAY_SIZE(imx_3stack_dapm_widgets));
/* Set up imx_3stack specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));//添加组件路径
snd_soc_dapm_disable_pin(codec, "Line In Jack");
hp = wm8994_hpdetect_irq(codec);
if (hp & 0x1) {
snd_soc_dapm_enable_pin(wm8994_dai.codec, "Headphone Jack");
if (hp & 0x4) {
snd_soc_dapm_enable_pin(wm8994_dai.codec,
"Headphone Mic");
card_priv.mic = 1;
} else
card_priv.mic = 0;
card_priv.headset = 1;
} else {
card_priv.headset = 0;
card_priv.mic = 0;
}
snd_soc_dapm_sync(codec);
if (card_priv.headset == 0)
switch_set_state(wm8994_psdev, H2W_NO_DEVICE);
else if (card_priv.mic == 0)
switch_set_state(wm8994_psdev, H2W_HEADSET_NO_MIC);
else
switch_set_state(wm8994_psdev, H2W_HEADSET);
return 0;
}
我们看到init主要添加了cpu端的控制和widgets等,还有一下状态的初始化。
5-2,snd_card_register(codec->card):
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
#ifndef CONFIG_SYSFS_DEPRECATED
if (!card->card_dev) {
card->card_dev = device_create(sound_class, card->dev,//创建/sys/class/sound/card0
MKDEV(0, 0), card,
"card%i", card->number);
if (IS_ERR(card->card_dev))
card->card_dev = NULL;
}
#endif
if ((err = snd_device_register_all(card)) < 0)//注册card devices上的所有设备,5-2-1
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
return 0;
}
snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
snd_cards[card->number] = card;//赋值到snd_cards数组
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
#ifndef CONFIG_SYSFS_DEPRECATED
if (card->card_dev) {
err = device_create_file(card->card_dev, &card_id_attrs);//创建id属性文件
if (err < 0)
return err;
err = device_create_file(card->card_dev, &card_number_attrs);//创建number属性文件
if (err < 0)
return err;
}
#endif
return 0;
}
5-2-1,snd_device_register_all(card)):
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) {//对于我们之前添加到card的devices列表里的每个device
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
if ((err = dev->ops->dev_register(dev)) < 0)//目前对应snd_pcm_dev_register或者snd_ctl_dev_register,这两个函数将生成pcmC0D0c/pcmC0D0p或controlC0节点
return err;
dev->state = SNDRV_DEV_REGISTERED;
}
}
return 0;
}
所以可以知道snd_card_register主要是注册了设备节点,还有一些sys下的属性文件。注册的这些设备节点将会在alsa-lib被使用,然后封装成容易使用的api对外使用。
哦,my god,整个初始化过程终于结束了!!! 从初始化过程我们就可以知道内核中alsa的主要架构,从而可以很方便地进行功能开发!