rk音頻驅動分析之tinymix控制


一.tinymix調用,主要是控制接口,調用到底層的control
操作方法:tinymix 0 SPK
Tinymix.c (external\tinyalsa)
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\t%s\t%d\t%-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);


    



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM