alsa音频架构4-声卡控制

第十五部分 声卡控制接口

前面第一篇中讲到了每注册一个声卡都会创建注册一个对应的声卡控制设备,并给出了其操作函数集snd_ctl_f_ops,
在"amixer,aplay,arecord的使用"一文中提及了amixer如何设置获取声卡的控制选项
接着在第二篇中讲到Asoc声卡驱动编写的步骤,步骤中没牵扯到控制设备的控制选项,这里要补充第5个步骤
第5个步骤就是在设备驱动中要调用snd_soc_add_controls函数添加相应的控制选项,也就是添加一系列的snd_kcontrol_new对象

1.snd_kcontrol_new结构体

struct snd_kcontrol_new {
	snd_ctl_elem_iface_t iface;	/* 接口号 */
	unsigned int device;		/* 设备号 */
	unsigned int subdevice;		/* 子设备号 */
	unsigned char *name;		/* item名 */
	unsigned int index;			/* item索引号 */
	unsigned int access;		/* 通道权限(读/写/执行) */
	unsigned int count;			/* count of same elements */
	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;
};


3.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->snd_card;	//获取声卡
	int err, i;

	for (i = 0; i < num_controls; i++) {	//添加num_controls个控制元素
		const struct snd_kcontrol_new *control = &controls[i];	//获取snd_kcontrol_new对象
		err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));	//添加1个控制元素
		if (err < 0) {
			dev_err(codec->dev, "%s: Failed to add %s: %d\n",codec->name, control->name, err);
			return err;
		}
	}
	return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_add_controls);

调用snd_ctl_add函数之前会先调用snd_soc_cnew创建一个新的snd_kcontrol结构体对象

4.snd_kcontrol结构体

struct snd_kcontrol {
	struct list_head list;		/* list of controls */
	struct snd_ctl_elem_id id;	//控制元素id
	unsigned int count;		/* count of same elements */
	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];	/* volatile data */
};

5.snd_soc_cnew函数

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));	//拷贝一份snd_kcontrol_new
	if (long_name)	//"NULL"
		template.name = long_name;
	template.index = 0;
	return snd_ctl_new1(&template, data);
}
EXPORT_SYMBOL_GPL(snd_soc_cnew);

snd_soc_cnew函数基本只是做个拷贝工作,主要的任务还是交给snd_ctl_new1函数处理

5.1snd_ctl_new1函数

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));	//初始化snd_kcontrol对象
//_____________________下面的工作基本上是将snd_kcontrol_new对象的对应值赋值给snd_kcontrol对象_____________________//
	kctl.id.iface = ncontrol->iface;	//接口号
	kctl.id.device = ncontrol->device;	//设备号
	kctl.id.subdevice = ncontrol->subdevice;	//子设备号
	if (ncontrol->name) {
		strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));	//拷贝item名到snd_kcontrol对象
		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_COMMAND|SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
	kctl.info = ncontrol->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;	//指向codec设备
	return snd_ctl_new(&kctl, access);
}

赋值完后又交由snd_ctl_new函数处理通道权限

5.2snd_ctl_new函数

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;
	if (control->count > MAX_CONTROL_COUNT)
		return NULL;
	kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);	//分配snd_kcontrol内存	
	if (kctl == NULL) {
		snd_printk(KERN_ERR "Cannot allocate control instance\n");
		return NULL;
	}
	*kctl = *control;	//获取snd_kcontrol对象
	for (idx = 0; idx < kctl->count; idx++)
		kctl->vd[idx].access = access;	//设置通道权限
	return kctl;
}

最后将初始化,赋值处理好的snd_kcontrol结构体对象返回作为snd_ctl_add函数的第二个参数

6.添加1个控制元素

添加1个控制元素由snd_ctl_add函数实现,而1个控制元素用snd_ctl_elem_id结构体对象去表达
6.1 snd_ctl_elem_id结构体

struct snd_ctl_elem_id {
	unsigned int numid;			/* 控制编号 */
	snd_ctl_elem_iface_t iface;	/* 接口号 */
	unsigned int device;		/* 设备号 */
	unsigned int subdevice;		/* 子设备号 */
	unsigned char name[44];		/* item名 */
	unsigned int index;			/* item索引号 */
};

6.2 snd_ctl_add函数

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
	down_write(&card->controls_rwsem);
	if (snd_ctl_find_id(card, &id)) {	//根据id查找控制接口
		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) {	//设置card的last_numid
		up_write(&card->controls_rwsem);
		err = -ENOMEM;
		goto error;
	}
	list_add_tail(&kcontrol->list, &card->controls);	//添加到所属声卡控制链表(以后可以在链表中搜查需要的项)
	card->controls_count += kcontrol->count;
	kcontrol->id.numid = card->last_numid + 1;	//获取控制编号
	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;
}
EXPORT_SYMBOL(snd_ctl_add);

设置snd_kcontrol对象的控制元素snd_ctl_elem_id对象,并将其添加到所属声卡的控制链表中

7.前面提到的snd_ctl_f_ops

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,
};

主要关注其unlocked_ioctl方法既snd_ctl_ioctl

8.snd_ctl_ioctl

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct snd_ctl_file *ctl;
	struct snd_card *card;
	struct snd_kctl_ioctl *p;
	void __user *argp = (void __user *)arg;
	int __user *ip = argp;
	int err;

	ctl = file->private_data;
	card = ctl->card;
	if (snd_BUG_ON(!card))
		return -ENXIO;
	switch (cmd) {
	case SNDRV_CTL_IOCTL_PVERSION:
		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
	case SNDRV_CTL_IOCTL_CARD_INFO:
		return snd_ctl_card_info(card, ctl, cmd, argp);
	case SNDRV_CTL_IOCTL_ELEM_LIST:	//对应amixer controls
		return snd_ctl_elem_list(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_INFO:	//对应amixer contents
		return snd_ctl_elem_info_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_READ:	//对应amixer cget cID P
		return snd_ctl_elem_read_user(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_WRITE:     //对应amixer cget cID
		return snd_ctl_elem_write_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_LOCK:
		return snd_ctl_elem_lock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
		return snd_ctl_elem_unlock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_ADD:
		return snd_ctl_elem_add_user(ctl, argp, 0);
	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
		return snd_ctl_elem_add_user(ctl, argp, 1);
	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
		return snd_ctl_elem_remove(ctl, argp);
	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
		return snd_ctl_subscribe_events(ctl, ip);
	case SNDRV_CTL_IOCTL_TLV_READ:
		return snd_ctl_tlv_ioctl(ctl, argp, 0);
	case SNDRV_CTL_IOCTL_TLV_WRITE:
		return snd_ctl_tlv_ioctl(ctl, argp, 1);
	case SNDRV_CTL_IOCTL_TLV_COMMAND:
		return snd_ctl_tlv_ioctl(ctl, argp, -1);
	case SNDRV_CTL_IOCTL_POWER:
		return -ENOPROTOOPT;
	case SNDRV_CTL_IOCTL_POWER_STATE:
		return put_user(card->power_state, ip) ? -EFAULT : 0;
	}
	down_read(&snd_ioctl_rwsem);
	list_for_each_entry(p, &snd_control_ioctls, list) {
		err = p->fioctl(card, ctl, cmd, arg);
		if (err != -ENOIOCTLCMD) {
			up_read(&snd_ioctl_rwsem);
			return err;
		}
	}
	up_read(&snd_ioctl_rwsem);
	snd_printdd("unknown ioctl = 0x%x\n", cmd);
	return -ENOTTY;
}





 

 

 

 

 

 



 

 

 

你可能感兴趣的:(linux,控制,alsa,设备驱动,声卡)