rk音频驱动分析之tinymix控制
Posted wen123456
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rk音频驱动分析之tinymix控制相关的知识,希望对你有一定的参考价值。
一.tinymix调用,主要是控制接口,调用到底层的control
操作方法:tinymix 0 SPK
Tinymix.c (external inyalsa)
int main(int argc, char **argv)
mixer = mixer_open(card);
snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
mixer = mixer_open(card); //单独分析1,打开control,并获取它们的信息
if (argc == 1)
tinymix_list_controls(mixer);
num_ctls = mixer_get_num_ctls(mixer); //得到mixer_ctl的个数
for (i = 0; i < num_ctls; i++) //获取所有的ctl 的名字,类型,值,打印出来
ctl = mixer_get_ctl(mixer, i);
name = mixer_ctl_get_name(ctl);
type = mixer_ctl_get_type_string(ctl);
num_values = mixer_ctl_get_num_values(ctl);
printf("%d %s %d %-40s", i, type, num_values, name);
tinymix_detail_control(mixer, name, 0); //获取更详细的信息
else if (argc == 2)
tinymix_detail_control(mixer, argv[1], 1); //获取详细信息
else //给control赋值
tinymix_set_value(mixer, argv[1], &argv[2], argc - 2);
type = mixer_ctl_get_type(ctl); //得到control的类型
num_ctl_values = mixer_ctl_get_num_values(ctl); //值得个数
if (isdigit(values[0][0]))
f (num_values == 1)
///* Set all values the same */
for (i = 0; i < num_ctl_values; i++)
mixer_ctl_set_value(ctl, i, value) //设置一样的值
ev.id.numid = ctl->info->id.numid;
//读取control的值,驱动分析五
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
//修改control的值,驱动分析六
ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
else
/* Set multiple values */
for (i = 0; i < num_values; i++)
mixer_ctl_set_value(ctl, i, atoi(values[i]))
else
if (type == MIXER_CTL_TYPE_ENUM) //如果是枚举
mixer_ctl_set_enum_by_string(ctl, values[0])
单独分析1
struct mixer *mixer_open(unsigned int card)
fd = open(fn, O_RDWR); //打开/dev/snd/controlC0,驱动分析一
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) //获取snd_ctl_elem_list结构体,主要是个数,驱动分析二
mixer = calloc(1, sizeof(*mixer));
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); //分配mixer_ctl
mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); //snd_ctl_elem_info
ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) ///驱动分析三.主要是获取card的信息,主要是名字一类的
eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); //分配snd_ctl_elem_id
mixer->count = elist.count;
mixer->fd = fd;
elist.space = mixer->count;
elist.pids = eid;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) //这里才是真正的获取snd_ctl_elem_list,前面只是获取个数,已经分析
for (n = 0; n < mixer->count; n++)
struct snd_ctl_elem_info *ei = mixer->elem_info + n;
ei->id.numid = eid[n].numid;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) //驱动分析四,获取control的信息
mixer->ctl[n].info = ei;
mixer->ctl[n].mixer = mixer;
if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) //如果是枚举类型,主要是这个SOC_GPIO_ENUM定义的control
char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
mixer->ctl[n].ename = enames;
for (m = 0; m < ei->value.enumerated.items; m++) //如果是枚举型,就需要单独获取每一个枚举
tmp.id.numid = ei->id.numid;
tmp.value.enumerated.item = m;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp)
二.驱动分析一
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,
};
这个文件操作函数已经在开机的注册,见rk音频驱动分析之machine
1.打开播放dev,驱动分析1
//主要是找到对应的设备,创建snd_monitor_file,初始化snd_ctl_file
snd_ctl_open
//通过次设备号,找到声卡
card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
//add the file to the file list of the card,这个file是用户层传下来的句柄
err = snd_card_file_add(card, file);
struct snd_monitor_file *mfile;
mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
INIT_LIST_HEAD(&mfile->shutdown_list);
list_add(&mfile->list, &card->files_list); //加入到card->files_list列表
try_module_get(card->module)
///* waiting events for read */
INIT_LIST_HEAD(&ctl->events);
//等待队列头
init_waitqueue_head(&ctl->change_sleep);
//赋值snd_ctl_file结构体
ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
list_add_tail(&ctl->list, &card->ctl_files); //放入card->ctl_files链表
三.驱动分析二
调用到snd_ctl_ioctl里面的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)
snd_ctl_elem_list list;
copy_from_user(&list, _list, sizeof(list))
offset = list.offset; ///* W: first element ID to get */,第一个元素ID
space = list.space; /* W: count of element IDs to get */ 得到元素ID的数量
if (space > 0)
/* allocate temporary buffer for atomic operation */
dst = vmalloc(space * sizeof(struct snd_ctl_elem_id)); //snd_ctl_elem_id结构体,用于记录element
list.count = card->controls_count; //contols的个数
plist = card->controls.next; //链表指针
while (plist != &card->controls) //snd_kcontrol不等于链表头,主要是遍历card->controls链表
kctl = snd_kcontrol(plist); //找到链表里面的snd_kcontrol
if (offset < kctl->count) // kctl->count是count of same elements ,offset 一般是小于1
break;
offset -= kctl->count; //offset = offset - kctl->count。
plist = plist->next; //下一个snd_kcontrol
while (space > 0 && plist != &card->controls) //// 遍历card->controls链表上的所有登记数据,当然包括微件widget合成的数据
kctl = snd_kcontrol(plist);
for (jidx = offset; space > 0 && jidx < kctl->count; jidx++)
snd_ctl_build_ioff(id, kctl, jidx);
id++;
space--;
list.used++; //加一
plist = plist->next; //找到下一个snd_kcontrol
copy_to_user(list.pids, dst, list.used * sizeof(struct snd_ctl_elem_id) //拷贝给user
else
list.count = card->controls_count;
copy_to_user(_list, &list, sizeof(list))
四.驱动分析三
调用snd_ctl_ioctl里面的snd_ctl_card_info(card, ctl, cmd, argp);
//主要是获取card的信息,主要是名字一类的
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,unsigned int cmd, void __user *arg)
info->card = card->number;
strlcpy(info->id, card->id, sizeof(info->id));
strlcpy(info->driver, card->driver, sizeof(info->driver));
strlcpy(info->name, card->shortname, sizeof(info->name));
strlcpy(info->longname, card->longname, sizeof(info->longname));
strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
strlcpy(info->components, card->components, sizeof(info->components));
五.驱动分析四
调用snd_ctl_ioctl的caseSNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info_user(ctl, argp);
struct snd_ctl_elem_info info;
copy_from_user(&info, _info, sizeof(info) //从用户空间拷贝
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); //wait until the power-state is changed. /* full On */
if (snd_power_get_state(card) == power_state) //等待声卡的电源状态变为SNDRV_CTL_POWER_D0
return 0;
init_waitqueue_entry(&wait, current);
add_wait_queue(&card->power_sleep, &wait);
while (1)
if (snd_power_get_state(card) == power_state)
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(30 * HZ);
remove_wait_queue(&card->power_sleep, &wait);
result = snd_ctl_elem_info(ctl, &info);
//通过id找到control 实体
kctl = snd_ctl_find_id(card, &info->id); //find the control instance with the given id
////获取snd_ctl_elem_info的信息,调用snd_soc_info_volsw这种,在定义snd_kcontrol_new的宏里面赋值SOC_SINGLE
result = kctl->info(kctl, info);
copy_to_user(_info, &info, sizeof(info)) //拷贝给应用
六.驱动分析五
调用snd_ctl_ioctl的case SNDRV_CTL_IOCTL_ELEM_READ:return snd_ctl_elem_read_user(card, argp);
static int snd_ctl_elem_read_user(struct snd_card *card, struct snd_ctl_elem_value __user *_control)
control = memdup_user(_control, sizeof(*control)); //从应用拷贝
result = snd_power_wait(card, SNDRV_CTL_POWER_D0); //等待card上电
result = snd_ctl_elem_read(card, control);
kctl = snd_ctl_find_id(card, &control->id); //从card->controls找到control
list_for_each_entry(kctl, &card->controls, list)
//一个kcotrol占据多个numid,所以需要确认具体一个numid,在这个中间的偏移,根据这个偏移index值,就可以确定存储 snd_ctl_elem_value.value.value[index]的下标
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL)
snd_ctl_build_ioff(&control->id, kctl, index_offset); //更新index和numid为下面的put操作准备
result = kctl->get(kctl, control); //如果是SOC_SINGLE宏定义的控件put函数指针则是: snd_soc_put_volsw
copy_to_user(_control, control, sizeof(*control))
六.驱动分析六
调用snd_ctl_ioctl的case SNDRV_CTL_IOCTL_ELEM_WRITE: return snd_ctl_elem_write_user(ctl, argp);
result = snd_ctl_elem_write(card, file, control);
kctl = snd_ctl_find_id(card, &control->id);
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
snd_ctl_build_ioff(&control->id, kctl, index_offset);
result = kctl->put(kctl, control); //这里是真正的写
struct snd_ctl_elem_id id = control->id;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); //与读不同的就是这里会发一个消息出来
list_for_each_entry(ctl, &card->ctl_files, list) //获取每一个snd_ctl_file
if (!ctl->subscribed) //如果不是active,就返回
continue;
list_for_each_entry(ev, &ctl->events, list) //遍历snd_kctl_event
if (ev->id.numid == id->numid) //如果是现在这个,就跳出
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
if (ev) {
ev->id = *id;
ev->mask = mask;
list_add_tail(&ev->list, &ctl->events);
以上是关于rk音频驱动分析之tinymix控制的主要内容,如果未能解决你的问题,请参考以下文章
RK3588平台开发系列讲解(AUDIO篇)Android音频调试--tiny-alsa 工具
RK3588平台开发系列讲解(AUDIO篇)Android音频调试--tiny-alsa 工具