三论声卡值controls:
首先我们操作control设备节点文件时候要先打开,看看打开函数做了什么?
snd_ctl_open(struct inode *inode, struct file *file)
err = nonseekable_open(inode, file);//设置filp->f_mode,这样内核就不会让lseek调用成功
card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); //得到snd_card结构体
err = snd_card_file_add(card, file);//设置snd_monitor_file结构体,并放到card->files_list链表
/*申请并初始化snd_ctl_file结构体*/
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
INIT_LIST_HEAD(&ctl->events);
init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock);
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结构体放到上面申请的file->private_data
list_add_tail(&ctl->list, &card->ctl_files); //吧snd_ctl_file结构体放到card->ctl_files链表
总结:这个打开函数:申请和初始化snd_monitor_file结构体、snd_ctl_file结构体,并放到card的对应链表成员。
操作该control设备节点,主要通过调用snd_ctl_ioctl来实现多种设置.
例如用户空间设置某个control:
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);
kctl = snd_ctl_find_id(card, &control->id); //获取card里对应的control里的snd_kcontrol结构体
result = kctl->put(kctl, control); //调用上面获取的snd_kcontrol结构体的put函数,传入的参数为card里对应的control里的snd_kcontrol结构体和从用户空间获得snd_ctl_elem_value设置好的结构体,所以可以推测就是用后一个参数来设置前一个参数。
加入这个control是SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,out_tlv),那么对应的put函数为snd_soc_put_volsw:
int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,truct snd_ctl_elem_value *ucontrol)
/*处理从用户空间传入的值*/
val = (ucontrol->value.integer.value[0] & mask);
..........
unsigned int reg = mc->reg; // 这里对应AC97_HEADPHONE寄存器
snd_soc_update_bits_locked(codec, reg, val_mask, val);
change = snd_soc_update_bits(codec, reg, mask, value);
snd_soc_write(codec, reg, new); //new就是上面的val,设置寄存器,这样用户空间就能实现对声卡的灵活性设置了。(像设置声音)