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 工具

Android:如何配置“tinymix”以使用“tinycap”录制系统音频

RK系列(RK3568) i2s 音频输入 麦克风驱动

linux音频alsa-uda134x驱动分析之二(时钟)

Android音频底层调试-基于tinyalsa