linux alsa 音频路径切换
kcontrol的创建和注册和调用流程
步骤1:创建过程
通过如下等宏来初始化一个 snd_kcontrol_new 结构的实例SOC_DOUBLE_R_TLV/SOC_ENUM/SOC_SINGLE/SOC_SINGLE_TLV......步骤2:注册过程
调用 snd_soc_add_codec_controls,该函数首先通过 snd_soc_cnew 函数将这些来自snd_kcontrol_new的成员组织到新分配的snd_kcontrol结构体成员中,然后调用 snd_ctl_add 函数,将这些音频控件添加到声卡对象(struct snd_card)的控件列表中(card->controls),同时为这个kcontrol分配一个唯一的id号。步骤3:kcontrol的调用流程
kcontrol的操作是通过向控制设备节点:/dev/controlC0,进行ioctl来实现的。
kcontrol的读操作:case SNDRV_CTL_IOCTL_ELEM_READ:
snd_ctl_elem_read_user(card, argp); |->snd_ctl_elem_read |->snd_ctl_find_id |->snd_ctl_get_ioff(kctl, &control->id); |->snd_ctl_build_ioff(&control->id, kctl, index_offset); |->result = kctl->get(kctl, control); //如果是SOC_SINGLE宏定义的控件则是: snd_soc_get_volsw
kcontrol的写操作:case SNDRV_CTL_IOCTL_ELEM_WRITE:
|->snd_ctl_elem_write_user(ctl, argp); |->control = memdup_user(_control, sizeof(*control));//拷贝应用空间的参数:snd_ctl_elem_value到内核空间 |->result = snd_ctl_elem_write(card, file, control); |->snd_ctl_get_ioff(kctl, &control->id);//一个kcotrol占据多个numid,所以需要确认具体一个numid, //在这个中间的偏移,根据这个偏移index值,就可以确定存储 //snd_ctl_elem_value.value.value[index]的下标 |->kctl = snd_ctl_find_id(card, &control->id);//根据num id号或控件的name在声卡对象的控件列表中,找到对应的控件。 |->snd_ctl_build_ioff(&control->id, kctl, index_offset);//更新index和numid为下面的put操作准备 |->result = kctl->put(kctl, control); //如果是SOC_SINGLE宏定义的控件put函数指针则是: snd_soc_put_volsw |->copy_to_user(_control, control, sizeof(*control))//将结果拷贝到用户空间
在用户空间跟内核空间进行ioctl交互时,涉及到两个很重要的数据结构:struct snd_ctl_elem_id 和 struct snd_ctl_elem_value:
以上结构体中各成员含义如下:struct snd_ctl_elem_id { unsigned int numid; /* numeric identifier, zero = invalid */ snd_ctl_elem_iface_t iface; /* interface identifier */ unsigned int device; /* device/client number */ unsigned int subdevice; /* subdevice (substream) number */ unsigned char name[44]; /* ASCII name of item */ unsigned int index; /* index of item */ };
unsigned int numid:是在控件注册时(snd_soc_add_codec_controls)分配的唯一标识号