硬件原理图
说明:mic连接的是WM8960的LINPUT1脚。
2. kcontrol是什么
kcontrol代表一个功能,使用一个寄存器里某些位;kcontrol有R/W函数。
3. kcontrol特性
1)一个声卡有多个kcontrol
2)一个kcontrol对应一个功能
比如: 音量、开/关、录音
4. snd_kcontrol结构体
62 struct snd_kcontrol {
63 struct list_head list; /* list of controls */
64 struct snd_ctl_elem_id id;
65 unsigned int count; /* count of same elements */
66 snd_kcontrol_info_t *info;
67 snd_kcontrol_get_t *get;
68 snd_kcontrol_put_t *put;
69 union {
70 snd_kcontrol_tlv_rw_t *c;
71 const unsigned int *p;
72 } tlv;
73 unsigned long private_value;
74 void *private_data;
75 void (*private_free)(struct snd_kcontrol *kcontrol);
76 struct snd_kcontrol_volatile vd[0]; /* volatile data */
77 };
769 struct snd_ctl_elem_id {
770 unsigned int numid; /* numeric identifier, zero = invalid */
771 snd_ctl_elem_iface_t iface; /* interface identifier */
772 unsigned int device; /* device/client number */
773 unsigned int subdevice; /* subdevice (substream) number */
774 unsigned char name[44]; /* ASCII name of item */
775 unsigned int index; /* index of item */
776 };
说明:
1)函数说明
info //获得kcontrol信息
get //获得kcontrol的值
put //设置kcontrol的值
2) private_value、private_data
提供 info/put/get使用
也许含有reg Addr和那些位
3)snd_ctl_elem_id中的numid和count
numid和count是对应的
有两个snd_kcontrol
第一个snd_kcontrol是count=3,则numid=1
第二个snd_kcontrol是count=1,则numid=4
一个numid对应着一个snd_kcontrol。
tinymix可以通过numid或者name,操作相应的kcontrol。
4. 构造kcontrol变量
4.1 声明snd_kcontrol_new结构变量
163 static const struct snd_kcontrol_new wm8960_snd_controls[] = {
164 SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
165 0, 63, 0, adc_tlv),
166 SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
167 6, 1, 0),
168 SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
169 7, 1, 0),
......
}
wm8960_snd_controls传递给函数snd_soc_add_controls
978 snd_soc_add_controls(codec, wm8960_snd_controls,
979 ARRAY_SIZE(wm8960_snd_controls));
snd_soc_add_controls
2501 int snd_soc_add_controls(struct snd_soc_codec *codec,
2502 const struct snd_kcontrol_new *controls, int num_controls)
2503 {
2504 struct snd_card *card = codec->card->snd_card;
2505 int err, i;
2506
2507 for (i = 0; i < num_controls; i++) {
2508 const struct snd_kcontrol_new *control = &controls[i];
2509 err = snd_ctl_add(card, snd_soc_cnew(control, codec,
2510 control->name,
2511 codec->name_prefix));
2512 if (err < 0) {
2513 dev_err(codec->dev, "%s: Failed to add %s: %d\n",
2514 codec->name, control->name, err);
2515 return err;
2516 }
2517 }
2518
2519 return 0;
2520 }
说明:使用函数snd_soc_cnew把snd_kcontrol_new结构转换成snd_kcontrol结构。
5. 存放kcontrol变量
329 int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
330 {
331 struct snd_ctl_elem_id id;
332 unsigned int idx;
333 int err = -EINVAL;
334
335 if (! kcontrol)
336 return err;
337 if (snd_BUG_ON(!card || !kcontrol->info))
338 goto error;
339 id = kcontrol->id;
340 down_write(&card->controls_rwsem);
341 if (snd_ctl_find_id(card, &id)) {
342 up_write(&card->controls_rwsem);
343 snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
344 id.iface,
345 id.device,
346 id.subdevice,
347 id.name,
348 id.index);
349 err = -EBUSY;
350 goto error;
351 }
352 if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
353 up_write(&card->controls_rwsem);
354 err = -ENOMEM;
355 goto error;
356 }
357 list_add_tail(&kcontrol->list, &card->controls);
358 card->controls_count += kcontrol->count;
359 kcontrol->id.numid = card->last_numid + 1;
360 card->last_numid += kcontrol->count;
361 up_write(&card->controls_rwsem);
362 for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
363 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
364 return 0;
365
366 error:
367 snd_ctl_free_one(kcontrol);
368 return err;
369 }
说明:list_add_tail(&kcontrol->list, &card->controls); 说明kcontrol是挂在链表card->control中。
怎么构造snd_kcontrol_new
参考wm8960.c,读芯片手册,确定reg addr位,使用宏设置结构体。
怎么使用snd_kcontrol
- 打开control设备
open("/dev/snd/controlC0")
- 通过ioctl控制
1)在sound/core/control.c中,file_operations的结构
1508 static const struct file_operations snd_ctl_f_ops =
1509 {
1510 .owner = THIS_MODULE,
1511 .read = snd_ctl_read,
1512 .open = snd_ctl_open,
1513 .release = snd_ctl_release,
1514 .llseek = no_llseek,
1515 .poll = snd_ctl_poll,
1516 .unlocked_ioctl = snd_ctl_ioctl,
1517 .compat_ioctl = snd_ctl_ioctl_compat,
1518 .fasync = snd_ctl_fasync,
1519 };
2)snd_ctl_ioctl函数中:
1272 static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1273 {
1274 struct snd_ctl_file *ctl;
1275 struct snd_card *card;
1276 struct snd_kctl_ioctl *p;
1277 void __user *argp = (void __user *)arg;
1278 int __user *ip = argp;
1279 int err;
1280
1281 ctl = file->private_data;
1282 card = ctl->card;
1283 if (snd_BUG_ON(!card))
1284 return -ENXIO;
1285 switch (cmd) {
1286 case SNDRV_CTL_IOCTL_PVERSION:
1287 return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
1288 case SNDRV_CTL_IOCTL_CARD_INFO:
1289 return snd_ctl_card_info(card, ctl, cmd, argp);
1290 case SNDRV_CTL_IOCTL_ELEM_LIST:
1291 return snd_ctl_elem_list(card, argp);
1292 case SNDRV_CTL_IOCTL_ELEM_INFO:
1293 return snd_ctl_elem_info_user(ctl, argp);
1294 case SNDRV_CTL_IOCTL_ELEM_READ:
1295 return snd_ctl_elem_read_user(card, argp);
1296 case SNDRV_CTL_IOCTL_ELEM_WRITE:
1297 return snd_ctl_elem_write_user(ctl, argp);
1298 case SNDRV_CTL_IOCTL_ELEM_LOCK:
1299 return snd_ctl_elem_lock(ctl, argp);
1300 case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
1301 return snd_ctl_elem_unlock(ctl, argp);
1302 case SNDRV_CTL_IOCTL_ELEM_ADD:
1303 return snd_ctl_elem_add_user(ctl, argp, 0);
1304 case SNDRV_CTL_IOCTL_ELEM_REPLACE:
1305 return snd_ctl_elem_add_user(ctl, argp, 1);
1306 case SNDRV_CTL_IOCTL_ELEM_REMOVE:
1307 return snd_ctl_elem_remove(ctl, argp);
1308 case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
1309 return snd_ctl_subscribe_events(ctl, ip);
1310 case SNDRV_CTL_IOCTL_TLV_READ:
1311 return snd_ctl_tlv_ioctl(ctl, argp, 0);
1312 case SNDRV_CTL_IOCTL_TLV_WRITE:
1313 return snd_ctl_tlv_ioctl(ctl, argp, 1);
1314 case SNDRV_CTL_IOCTL_TLV_COMMAND:
1315 return snd_ctl_tlv_ioctl(ctl, argp, -1);
1316 case SNDRV_CTL_IOCTL_POWER:
1317 return -ENOPROTOOPT;
1318 case SNDRV_CTL_IOCTL_POWER_STATE:
1319 #ifdef CONFIG_PM
1320 return put_user(card->power_state, ip) ? -EFAULT : 0;
1321 #else
1322 return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
1323 #endif
1324 }
1325 down_read(&snd_ioctl_rwsem);
1326 list_for_each_entry(p, &snd_control_ioctls, list) {
1327 err = p->fioctl(card, ctl, cmd, arg);
1328 if (err != -ENOIOCTLCMD) {
1329 up_read(&snd_ioctl_rwsem);
1330 return err;
1331 }
1332 }
1333 up_read(&snd_ioctl_rwsem);
1334 snd_printdd("unknown ioctl = 0x%x\n", cmd);
1335 return -ENOTTY;
1336 }
说明:
在1294行的宏对应着snd_ctl_elem_read_use
2)snd_ctl_elem_read_use函数
852 static int snd_ctl_elem_read_user(struct snd_card *card,
853 struct snd_ctl_elem_value __user *_control)
854 {
855 struct snd_ctl_elem_value *control;
856 int result;
857
858 control = memdup_user(_control, sizeof(*control));
859 if (IS_ERR(control))
860 return PTR_ERR(control);
861
862 snd_power_lock(card);
863 result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
864 if (result >= 0)
865 result = snd_ctl_elem_read(card, control);
866 snd_power_unlock(card);
867 if (result >= 0)
868 if (copy_to_user(_control, control, sizeof(*control)))
869 result = -EFAULT;
870 kfree(control);
871 return result;
872 }
3)进入865行的snd_ctl_elem_read
826 static int snd_ctl_elem_read(struct snd_card *card,
827 struct snd_ctl_elem_value *control)
828 {
829 struct snd_kcontrol *kctl;
830 struct snd_kcontrol_volatile *vd;
831 unsigned int index_offset;
832 int result;
833
834 down_read(&card->controls_rwsem);
835 kctl = snd_ctl_find_id(card, &control->id);
836 if (kctl == NULL) {
837 result = -ENOENT;
838 } else {
839 index_offset = snd_ctl_get_ioff(kctl, &control->id);
840 vd = &kctl->vd[index_offset];
841 if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
842 kctl->get != NULL) {
843 snd_ctl_build_ioff(&control->id, kctl, index_offset);
844 result = kctl->get(kctl, control);
845 } else
846 result = -EPERM;
847 }
848 up_read(&card->controls_rwsem);
849 return result;
850 }
说明: 在844行使用kcontrol中的get函数。