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
- Android音频驱动-ASOC之Control Open
- Android音频驱动-ASOC之PCM Open
- Android音频驱动-ASOC之Control Device创建
- Android音频驱动-ASOC之Machine
- Android音频驱动-ASOC之Codec
- Android音频驱动-ASOC之Platform
- Android音频驱动-ASOC之CPU DAI
- Android音频驱动-ASOC之常用对象
- Android音频驱动-ASOC之PCM Prepare
- Android音频驱动-ASOC之PCM Write
- Android音频驱动-ASOC之DAMP
- Android音频驱动-ASOC之Sound Card注册
- Android音频驱动-ASOC之PCM Device创建
- Android音频驱动-ASOC之Sound Card创建
- Android音频驱动-ASOC之主&从设备号
- Android音频驱动-ASOC之创建设备节点
- Android音频驱动-ASOC之PCM HW Params
- Linux音频驱动之ASoC驱动架构
- 发现一个设计模式总结的很好的地方
- Fiddler命令行用法
- [LintCode 41] 最大子数组之和(Python)
- 欢迎使用CSDN-markdown编辑器
- Mac下Mysql启动异常["ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.so
- Android音频驱动-ASOC之Control Open
- Net-Snmp工具(学习SNMP的工具,开源项目)简单使用
- [shell] 小括号、中括号、大括号
- 002_table标签制作细线表格
- AutoLayout之关于苹果原生约束的探索
- SSH Could not find action or result: /ssh_01/hello.action
- codeforces 851 C (Div#432) Five Dimensional Points
- Mybatis获取插入记录的自增长ID
- 记1