Android音频驱动-ASOC之Control Open

来源:互联网 发布:网络捕鱼平台出租代理 编辑:程序博客网 时间:2024/05/20 04:31
struct mixer *mixer_open(unsigned int card){    struct snd_ctl_elem_list elist;    struct snd_ctl_elem_info tmp;    struct snd_ctl_elem_id *eid = NULL;    struct mixer *mixer = NULL;    unsigned int n, m;    int fd;    char fn[256];    /* 获取对应操作的文件句柄名称 */      snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);    fd = open(fn, O_RDWR);//打开control设备    memset(&elist, 0, sizeof(elist));    /* 第一遍调用SNDRV_CTL_IOCTL_ELEM_LIST,获取snd_kcontrol的个数 */      if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)        goto fail;    mixer = calloc(1, sizeof(*mixer));/* 获取个数后,对应分配空间 */     mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));    mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));    if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)//获取card信息        goto fail;    eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));    mixer->count = elist.count;    mixer->fd = fd;    elist.space = mixer->count;//space大于0,第二次调用SNDRV_CTL_IOCTL_ELEM_LIST获取更多信息    elist.pids = eid;//传入kernel获得实际值,包含了所有kctl的id对象    /* 第二遍调用,获取对应的snd_ctl_elem_id数组,且numid从1开始,0表示无效 */      if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)        goto fail;    for (n = 0; n < mixer->count; n++) {        struct snd_ctl_elem_info *ei = mixer->elem_info + n;        ei->id.numid = eid[n].numid;//从kernel获得了实际的numid值        /*根据获取的snd_kcontrol对应的numid,调用获取对应的snd_ctl_elem_info,         内核的具体步骤是,根据numid,获取对应的snd_kcontrol,之后调用其info操        作接口,填充用户传入snd_ctl_elem_info缓冲。*/        if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)//第一次获取指定控件信息,例如enumerated.items            goto fail;        mixer->ctl[n].info = ei;        mixer->ctl[n].mixer = mixer;        /* 如果为枚举量,还需要进一步获取每个枚举量对应的字符串描述 */        if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {            char **enames = calloc(ei->value.enumerated.items, sizeof(char*));//根据字符串的个数来申请,如on、off            mixer->ctl[n].ename = enames;            for (m = 0; m < ei->value.enumerated.items; m++) {                memset(&tmp, 0, sizeof(tmp));                tmp.id.numid = ei->id.numid;                tmp.value.enumerated.item = m;//字符串下标                if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)//第二次                    goto fail;                enames[m] = strdup(tmp.value.enumerated.name);//将字符串On、Off保存在mixer->ctl[n].ename中            }        }    }    return mixer;}static int snd_open(struct inode *inode, struct file *file){    unsigned int minor = iminor(inode);    struct snd_minor *mptr = NULL;    const struct file_operations *new_fops;    int err = 0;    if (minor >= ARRAY_SIZE(snd_minors))        return -ENODEV;    mptr = snd_minors[minor];    new_fops = fops_get(mptr->f_ops);    replace_fops(file, new_fops);    if (file->f_op->open)        err = file->f_op->open(inode, file);    return err;}/*static const struct file_operations snd_ctl_f_ops ={    .owner =    THIS_MODULE,    .read =     snd_ctl_read,    .open =     snd_ctl_open,    .release =  snd_ctl_release,    .llseek =   no_llseek,    .poll =     snd_ctl_poll,    .unlocked_ioctl =   snd_ctl_ioctl,    .compat_ioctl = snd_ctl_ioctl_compat,    .fasync =   snd_ctl_fasync,};*/static int snd_ctl_open(struct inode *inode, struct file *file){    unsigned long flags;    struct snd_card *card;    struct snd_ctl_file *ctl;    int err;    err = nonseekable_open(inode, file);    card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);    err = snd_card_file_add(card, file);    ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);    ctl->card = card;    ctl->prefer_pcm_subdevice = -1;    ctl->prefer_rawmidi_subdevice = -1;    ctl->pid = get_pid(task_pid(current));    file->private_data = ctl;//保存snd_ctl_file对象    list_add_tail(&ctl->list, &card->ctl_files);    return 0;}

获取所有kcontrol的列表,包含了所有kcontrol对应的物理控件

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){    case SNDRV_CTL_IOCTL_ELEM_LIST:        return snd_ctl_elem_list(card, argp);}static int snd_ctl_elem_list(struct snd_card *card,                 struct snd_ctl_elem_list __user *_list){    struct list_head *plist;    struct snd_ctl_elem_list list;    struct snd_kcontrol *kctl;    struct snd_ctl_elem_id *dst, *id;    unsigned int offset, space, jidx;    if (copy_from_user(&list, _list, sizeof(list)))        return -EFAULT;    offset = list.offset;    space = list.space;//等于card->controls_count,所有kcontrol对应的物理控件数    //第一次space等于NULL,所以只是返回list.count个数,calloc list.count个空间,第二次会读取实际数据    if (space > 0) {        dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));        list.count = card->controls_count;        plist = card->controls.next;//从card->controls管理的kcontrol列表里获取        while (plist != &card->controls) {            if (offset == 0)                break;            kctl = snd_kcontrol(plist);            if (offset < kctl->count)                break;            offset -= kctl->count;            plist = plist->next;        }        list.used = 0;        id = dst;        //遍历card->controls链表上所有的control对象        while (space > 0 && plist != &card->controls) {            kctl = snd_kcontrol(plist);            //因为一个逻辑kcontrol内有count个物理控件,需要通过offset来计算偏移量            for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {                snd_ctl_build_ioff(id, kctl, jidx);//逐个赋值id数组的index和numid变量                id++;                space--;                list.used++;            }            plist = plist->next;            offset = 0;        }        if (list.used > 0 && copy_to_user(list.pids, dst,//将id值返回用户空间的pids数组                 list.used * sizeof(struct snd_ctl_elem_id))) {            return -EFAULT;        }    } else {        list.count = card->controls_count;    }    if (copy_to_user(_list, &list, sizeof(list)))        return -EFAULT;    return 0;}

获取指定kcontrol的信息

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){    case SNDRV_CTL_IOCTL_ELEM_INFO:        return snd_ctl_elem_info_user(ctl, argp);}static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,                  struct snd_ctl_elem_info __user *_info){    struct snd_ctl_elem_info info;    int result;    if (result >= 0)        result = snd_ctl_elem_info(ctl, &info);}static int snd_ctl_elem_info(struct snd_ctl_file *ctl,                 struct snd_ctl_elem_info *info){    struct snd_card *card = ctl->card;    struct snd_kcontrol *kctl;    struct snd_kcontrol_volatile *vd;    unsigned int index_offset;    int result;    //在snd_card的kcontrol链表中根据info->id找到对应的kcontrol    kctl = snd_ctl_find_id(card, &info->id);    result = kctl->info(kctl, info);//调用kcontrol中的info回调函数获取struct snd_ctl_elem_info信息    if (result >= 0) {        index_offset = snd_ctl_get_ioff(kctl, &info->id);        vd = &kctl->vd[index_offset];        snd_ctl_build_ioff(&info->id, kctl, index_offset);        info->access = vd->access;        if (vd->owner) {            info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;            if (vd->owner == ctl)                info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;            info->owner = pid_vnr(vd->owner->pid);        } else {            info->owner = -1;        }    }    return result;}

根据id->numid找到对应的kcontrol对象。

struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,                     struct snd_ctl_elem_id *id){    struct snd_kcontrol *kctl;    if (id->numid != 0)        return snd_ctl_find_numid(card, id->numid);    return NULL;}struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid){    struct snd_kcontrol *kctl;    //因为一个kcontrol是一个逻辑控件,可能对应多个物理控件    //内核中的kcontrol->id.numid记录的是这个kcontrol中第一个物理控件在所有物理控件中的位置,    //用户空间也会传来struct snd_ctl_elem_id变量,里面的numid记录的是kcontrol中的具体某个物理控件的位置,    //所以需要判断用户空间的numid是否在内核空间的对应范围    list_for_each_entry(kctl, &card->controls, list) {        if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)            return kctl;    }    return NULL;}

调用control对应的info回调函数

/*struct snd_kcontrol {    struct list_head list;    struct snd_ctl_elem_id id;    unsigned int count;//记录kcontrol中包括的物理控件的数目     snd_kcontrol_info_t *info;    snd_kcontrol_get_t *get;    snd_kcontrol_put_t *put;    union {        snd_kcontrol_tlv_rw_t *c;        const unsigned int *p;    } tlv;    unsigned long private_value;    void *private_data;    void (*private_free)(struct snd_kcontrol *kcontrol);    struct snd_kcontrol_volatile vd[0];};struct snd_kcontrol_new {    snd_ctl_elem_iface_t iface;    unsigned int device;    unsigned int subdevice;    const unsigned char *name;    unsigned int index;    unsigned int access;    unsigned int count;    snd_kcontrol_info_t *info;    snd_kcontrol_get_t *get;    snd_kcontrol_put_t *put;    union {        snd_kcontrol_tlv_rw_t *c;        const unsigned int *p;    } tlv;    unsigned long private_value;};#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \{   .iface = SNDRV_CTL_ELEM_IFACE_MIXER,         .name = xname, \    .info = snd_soc_info_enum_double, \    .get = xhandler_get,         .put = xhandler_put, \    .private_value = (unsigned long)&xenum }static const struct snd_kcontrol_new mt6331_snd_controls[] = {    SOC_ENUM_EXT("Voice_Amp_Switch", // name             Audio_DL_Enum[2], // info函数             Voice_Amp_Get,// get函数             Voice_Amp_Set),// put函数}struct soc_enum {    int reg;    unsigned char shift_l;    unsigned char shift_r;    unsigned int items;    unsigned int mask;    const char * const *texts;    const unsigned int *values;};//private_valuestatic const struct soc_enum Audio_DL_Enum[] = {    SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amp_function), amp_function),    SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amp_function), amp_function),    SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amp_function), amp_function),//Audio_DL_Enum[2]}#define SOC_ENUM_SINGLE_EXT(xitems, xtexts) \{   .items = xitems, .texts = xtexts }//item等于2,texts指向amp_function数组static const char *const amp_function[] = { "Off", "On" };//字符串枚举类型*///info函数int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,    struct snd_ctl_elem_info *uinfo){    //使用注册control时保存的Audio_DL_Enum[2]    struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;//kcontrol为枚举类型    uinfo->count = e->shift_l == e->shift_r ? 1 : 2;    uinfo->value.enumerated.items = e->items;//获得ARRAY_SIZE(amp_function),等于2    if (uinfo->value.enumerated.item >= e->items)        uinfo->value.enumerated.item = e->items - 1;    strlcpy(uinfo->value.enumerated.name,        e->texts[uinfo->value.enumerated.item],        sizeof(uinfo->value.enumerated.name));//保存字符串"Off"和"On"    return 0;}

nd_ctl_elem_info结构体的type字段定于了control的类型,包括BOOLEAN、INTEGER、ENUMERATED、BYTES、IEC958和 INTEGER64。count字段定义了这个control中包含的元素的数量,例如1个立体声音量control的count = 2。value是1个联合体,其所存储的值的具体类型依赖于type。

/*struct snd_ctl_elem_info { struct snd_ctl_elem_id id; //元素ID* W: element ID */ snd_ctl_elem_type_t type; //值类型* R: value type - SNDRV_CTL_ELEM_TYPE_* */ unsigned int access;  //访问权限* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */ unsigned int count;  //值的计算 pid_t owner;    union {  struct {   long min;  //最小值   long max;  //最大值   long step;    } integer;  struct {   long long min;     long long max;     long long step;    } integer64;  struct {   unsigned int items;    unsigned int item;    char name[64];    } enumerated;  unsigned char reserved[128]; } value; union {  unsigned short d[4];    unsigned short *d_ptr;   } dimen; unsigned char reserved[64-4*sizeof(unsigned short)];};*/

打开听筒通路

/*#define AUDIO_DEVICE_RECEIVER                "receiver_output"*/status_t AudioALSAHardwareResourceManager::OpenReceiverPath(const uint32_t SampleRate __unused){    mDeviceConfigManager->ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_RECEIVER);    return NO_ERROR;}status_t AudioALSADeviceConfigManager::ApplyDeviceTurnonSequenceByName(const char *DeviceName){    int count = 0;    DeviceCtlDescriptor *descriptor = GetDeviceDescriptorbyname(DeviceName);    if (descriptor->DeviceStatusCounter == 0){        for (count = 0; count < descriptor->mDeviceCltonVector.size(); count += 2){            String8 cltname = descriptor->mDeviceCltonVector.itemAt(count);            String8 cltvalue = descriptor->mDeviceCltonVector.itemAt(count + 1);            //cltname等于"Voice_Amp_Switch",cltvalue等于"On"            if (mixer_ctl_set_enum_by_string(mixer_get_ctl_by_name(mMixer, cltname.string()), cltvalue.string())){                ASSERT(false);            }        }    }    descriptor->DeviceStatusCounter++;    return NO_ERROR;}/*audio_device.xml    <!--receiver output-->        <path name="receiver_output" value="turnon">//打开听筒        <kctl name="Voice_Amp_Switch" value="On" />    </path>    <path name="receiver_output" value="turnoff">//关闭听筒        <kctl name="Voice_Amp_Switch" value="Off" />    </path>*/struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name){    unsigned int n;    for (n = 0; n < mixer->count; n++)        if (!strcmp(name, (char*) mixer->elem_info[n].id.name))            return mixer->ctl + n;    return NULL;}

调用tinyalsa的接口

int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string){    unsigned int i, num_enums;    struct snd_ctl_elem_value ev;    int ret;    num_enums = ctl->info->value.enumerated.items;//等于2    for (i = 0; i < num_enums; i++) {        if (!strcmp(string, ctl->ename[i])) {//遍历字符串数组,是否有等于"On"的值            memset(&ev, 0, sizeof(ev));            ev.value.enumerated.item[0] = i;//i等于1表示调用"On"            ev.id.numid = ctl->info->id.numid;//用来匹配kcontrol的id            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);            if (ret < 0)                return ret;            return 0;        }    }    return -EINVAL;}

以下结构体主要用来存储用户想要读取或是写入控件的值,该结构体是一个联合体(union),size of是512个字节,这512个字节,根据控件的不同可以存储128个int,或是64 long long,或是512 byte的字节。譬如是做long value[128]使用时,为什么需要一个数组呢?
这个主要是由于一个kcontrol包含多个numid的情况;这样这个valueindex就是可以用来存储kcontrol.info.id.index对应的numid的物理控件的值,注意多个物理控件使用一个kcontrol来表示,但他们的值是对应数组的不同下标(index)的,而kcontrol.info.count则记录了该kcontrol包含了多少个物理控件。

/* struct snd_ctl_elem_value {          struct snd_ctl_elem_id id;        unsigned int indirect: 1;        union {              union {                  long value[128];                  long *value_ptr;            } integer;              union {                  long long value[64];                  long long *value_ptr;            } integer64;              union {                  unsigned int item[128];                  unsigned int *item_ptr;            } enumerated;              union {                  unsigned char data[512];                  unsigned char *data_ptr;            } bytes;              struct snd_aes_iec958 iec958;          } value;        struct timespec tstamp;          unsigned char reserved[128-sizeof(struct timespec)];   }; */

调用Controk device的回调函数snd_ctl_ioctl,进入kernel。

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){    switch (cmd) {        ......    case SNDRV_CTL_IOCTL_ELEM_WRITE:        return snd_ctl_elem_write_user(ctl, argp);}static int snd_ctl_elem_write_user(struct snd_ctl_file *file,                   struct snd_ctl_elem_value __user *_control){    struct snd_ctl_elem_value *control;    struct snd_card *card;    int result;    control = memdup_user(_control, sizeof(*control));    card = file->card;    if (result >= 0)        result = snd_ctl_elem_write(card, file, control);    if (result >= 0)        if (copy_to_user(_control, control, sizeof(*control)))            result = -EFAULT;    return result;}static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,                  struct snd_ctl_elem_value *control){    struct snd_kcontrol *kctl;    struct snd_kcontrol_volatile *vd;    unsigned int index_offset;    int result;    kctl = snd_ctl_find_id(card, &control->id);//通过id找到对应kcontrol对象    if (kctl == NULL) {        result = -ENOENT;    } else {        index_offset = snd_ctl_get_ioff(kctl, &control->id);        vd = &kctl->vd[index_offset];        if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||            kctl->put == NULL ||            (file && vd->owner && vd->owner != file)) {            result = -EPERM;        } else {            snd_ctl_build_ioff(&control->id, kctl, index_offset);            result = kctl->put(kctl, control);//调用put回调函数        }        if (result > 0) {            struct snd_ctl_elem_id id = control->id;            snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id);            return 0;        }    }    return result;}

如果驱动中需要在中断服务程序中改变或更新1个control,可以调用snd_ctl_notify()函数,此函数原型为:
void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id);
该函数的第2个参数为事件掩码(event-mask),第3个参数为该通知的control元素id指针。
例如,如下语句定义的事件掩码SNDRV_CTL_EVENT_MASK_VALUE意味着control值的改变被通知:
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer);

//put函数static int Voice_Amp_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    mutex_lock(&Ana_Ctrl_Mutex);    if ((ucontrol->value.integer.value[0] == true)//On        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] == false)) {        Voice_Amp_Change(true);        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)//Off           && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] ==               true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] =            ucontrol->value.integer.value[0];        Voice_Amp_Change(false);    }    mutex_unlock(&Ana_Ctrl_Mutex);    return 0;}static void Voice_Amp_Change(bool enable){    if (enable) {        /*pr_debug("%s\n", __func__);*/        if (GetDLStatus() == false) {            TurnOnDacPower();            Ana_Set_Reg(AUDDEC_ANA_CON9, 0xA155, 0xA000);            /* Enable cap-less LDOs (1.6V) */            Ana_Set_Reg(AUDDEC_ANA_CON10, 0x0100, 0x0100);            /* Enable NV regulator (-1.6V) */            Ana_Set_Reg(ZCD_CON0, 0x0000, 0xffff);            /* Disable AUD_ZCD */            Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE080, 0xffff);            /* Disable HS and short-ckt protection. HS MUX is opened */            Ana_Set_Reg(AUDDEC_ANA_CON9, 0xA055, 0x0100);            /* Enable IBIST */            Ana_Set_Reg(AUDDEC_ANA_CON6, 0x0400, 0xffff);            /* from yoyo HQA script */            Ana_Set_Reg(ZCD_CON3, 0x001F, 0xffff);            /* Set HS gain as minimum (~ -40dB) */            Ana_Set_Reg(AUDDEC_ANA_CON1, 0x0100, 0x0100);            /* De_OSC of HS and enable output STBENH */            Ana_Set_Reg(AUDDEC_ANA_CON9, 0xA255, 0x0200);            /* Enable AUD_CLK */            Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE089, 0xffff);            /* Enable Audio DAC  */            Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE109, 0xffff);            /* Switch HS MUX to audio DAC */            Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE119, 0xffff);            /* Enable HS */            Ana_Set_Reg(ZCD_CON3, 0x0009, 0xffff);            /* Set HS gain as 0dB */        }    } else {        /*pr_debug("Voice_Amp_Change off amp\n");*/        Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE109, 0xffff);        /* Disable voice driver */        if (GetDLStatus() == false) {            Ana_Set_Reg(AUDDEC_ANA_CON0, 0xE000, 0xffff);            /* Disable Audio DAC */            Ana_Set_Reg(AUDDEC_ANA_CON9, 0xA055, 0x0200);            /* Audio decoder reset - bit 9 */            Ana_Set_Reg(AUDDEC_ANA_CON6, 0x0000, 0xffff);            /* from yoyo HQA script - reset */            Ana_Set_Reg(AUDDEC_ANA_CON9, 0xA155, 0x0100);            /* Disable IBIST  - bit 8 */            Ana_Set_Reg(AUDDEC_ANA_CON10, 0x0000, 0x0100);            /* Disable NV regulator (-1.6V) */            Ana_Set_Reg(AUDDEC_ANA_CON9, 0x0155, 0xA000);            /* Disable cap-less LDOs (1.6V) */            TurnOffDacPower();        }    }}

snd_kcontrol对象的注册是在codec probe函数里完成的。

static int mt6331_codec_probe(struct snd_soc_codec *codec){    .......    snd_soc_add_codec_controls(codec, mt6331_snd_controls, ARRAY_SIZE(mt6331_snd_controls));    .......}int snd_soc_add_codec_controls(struct snd_soc_codec *codec,    const struct snd_kcontrol_new *controls, unsigned int num_controls){    return snd_soc_add_component_controls(&codec->component, controls,        num_controls);}int snd_soc_add_component_controls(struct snd_soc_component *component,    const struct snd_kcontrol_new *controls, unsigned int num_controls){    struct snd_card *card = component->card->snd_card;    return snd_soc_add_controls(card, component->dev, controls,            num_controls, component->name_prefix, component);}static int snd_soc_add_controls(struct snd_card *card, struct device *dev,    const struct snd_kcontrol_new *controls, int num_controls,    const char *prefix, void *data){    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, data, control->name, prefix));    }    return 0;}//使用codec driver里snd_kcontrol_new的内容填充snd_kcontrol对象struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,                  void *data, const char *long_name,                  const char *prefix){    struct snd_kcontrol_new template;    struct snd_kcontrol *kcontrol;    char *name = NULL;    memcpy(&template, _template, sizeof(template));    template.index = 0;    //会分配一个新的snd_kcontrol实例,并把template中相应的值复制到该实例中    kcontrol = snd_ctl_new1(&template, data);    return kcontrol;}

将这些音频控件添加到声卡对象(struct snd_card)的控件列表中(card->controls),同时为这个kcontrol分配一个唯一的id号。

int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol){    struct snd_ctl_elem_id id;    unsigned int idx;    unsigned int count;    int err = -EINVAL;    id = kcontrol->id;    if (snd_ctl_find_id(card, &id)) {        goto error;    }    if (snd_ctl_find_hole(card, kcontrol->count) < 0) {        goto error;    }    //将snd_kcontrol添加到card->controls链表中,mixer_open时会从card->controls里查询kcontrol信息    list_add_tail(&kcontrol->list, &card->controls);    card->controls_count += kcontrol->count;//一般等于1    //numid记录的是这个kcontrol中第一个物理控件在所有物理控件中的位置    kcontrol->id.numid = card->last_numid + 1;//匹配用户空间control的关键字    card->last_numid += kcontrol->count;//记录所有物理控件的数目    count = kcontrol->count;    for (idx = 0; idx < count; idx++, id.index++, id.numid++)        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);    return 0;}

需要注意的是
1、struct snd_ctl_elem_id中的numid和index
因为一个kcontrol是一个逻辑控件,可能对应多个物理控件,内核中的kcontrol->id.numid记录的是这个kcontrol中第一个物理控件在所有物理控件中的位置,用户空间也会传来struct snd_ctl_elem_id变量,里面的numid记录的是kcontrol中的具体某个物理控件的位置
2、struct snd_kcontrol中的count记录的是kcontrol中包括的物理控件的数目
3、strcut snd_card中的last_numid记录的是上一次所有物理控件的数目,当添加新的kcontrol之后,kcontrol->id.numid=card->last_numid+1, card->last_numid+=kcontrol->count

原创粉丝点击