Android音頻驅動-ASOC之Sound Card註冊

soc-core.c
    soc_probe
    platform_get_drvdata(pdev)//獲取聲卡
    snd_soc_register_card
    snd_soc_instantiate_card

static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
    struct snd_soc_codec *codec;
    struct snd_soc_dai_link *dai_link;
    int ret, i, order, dai_fmt;

    //card->rtd對象綁定codec dai、codec、cpu dai、platform
    for (i = 0; i < card->num_links; i++) {
        ret = soc_bind_dai_link(card, i);
    }

    /* card bind complete so register a sound card */
    ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 
                       card->owner, 0, &card->snd_card);

    card->dapm.bias_level = SND_SOC_BIAS_OFF;
    card->dapm.dev = card->dev;
    card->dapm.card = card;
    list_add(&card->dapm.list, &card->dapm_list);

    /* initialise the sound card only once */
    if (card->probe) {
        ret = card->probe(card);
    }

    /* probe all components used by DAI links on this card */
    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
        for (i = 0; i < card->num_links; i++) {
            ret = soc_probe_link_components(card, i, order);
        }
    }

    /* probe all DAI links on this card */
    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
        for (i = 0; i < card->num_links; i++) {
            ret = soc_probe_link_dais(card, i, order);
        }
    }

    if (card->controls)
        snd_soc_add_card_controls(card, card->controls, card->num_controls);

    ret = snd_card_register(card->snd_card);//註冊聲卡
    card->instantiated = 1;
    return 0;
}
int snd_card_new(struct device *parent, int idx, const char *xid,
            struct module *module, int extra_size,
            struct snd_card **card_ret)
{
    struct snd_card *card;
    int err;
    *card_ret = NULL;

    card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);

    card->dev = parent;
    card->number = idx;
    card->module = module;
    INIT_LIST_HEAD(&card->devices);
    INIT_LIST_HEAD(&card->controls);
    INIT_LIST_HEAD(&card->ctl_files);
    INIT_LIST_HEAD(&card->files_list);

    device_initialize(&card->card_dev);
    card->card_dev.parent = parent;
    card->card_dev.class = sound_class;
    card->card_dev.release = release_card_device;
    card->card_dev.groups = card_dev_attr_groups;

    err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
    err = snd_ctl_create(card);//創建control的設備節點
    err = snd_info_card_create(card);//創建該聲卡的proc/asound/card0文件
    *card_ret = card;
    return 0;

}

創建control對應的device並加入到聲卡的device鏈表,後續time和pcm都有類似的操作。

int snd_ctl_create(struct snd_card *card)
{
    static struct snd_device_ops ops = {
        .dev_free = snd_ctl_dev_free,
        .dev_register = snd_ctl_dev_register,
        .dev_disconnect = snd_ctl_dev_disconnect,
    };
    return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//將ctl device加入到聲卡的device鏈表
}
int snd_device_new(struct snd_card *card, enum snd_device_type type,
           void *device_data, struct snd_device_ops *ops)
{
    struct snd_device *dev;
    struct list_head *p;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    INIT_LIST_HEAD(&dev->list);
    dev->card = card;
    dev->type = type;
    dev->state = SNDRV_DEV_BUILD;
    dev->device_data = device_data;
    dev->ops = ops;

    /* insert the entry in an incrementally sorted list */
    list_for_each_prev(p, &card->devices) {//加入聲卡設備鏈表
        struct snd_device *pdev = list_entry(p, struct snd_device, list);
        if ((unsigned int)pdev->type <= (unsigned int)type)
            break;
    }

    list_add(&dev->list, p);
    return 0;
}

創建該聲卡的proc/asound/card0文件

int snd_info_card_create(struct snd_card *card)
{
    char str[8];
    struct snd_info_entry *entry;
    sprintf(str, "card%i", card->number);//生成聲卡名字card0
    if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
        return -ENOMEM;
    entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
    if (snd_info_register(entry) < 0) {//創建card相關的proc文件
        snd_info_free_entry(entry);
        return -ENOMEM;
    }
    card->proc_root = entry;
    return 0;
}

Anroid N:/ # cd proc/asound/
card0/ cards devices hwdep mtsndcard/ oss/ pcm seq/ timers version

static int soc_probe_link_components(struct snd_soc_card *card, int num,
                     int order)
{
    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_component *component;
    int i, ret;

    //調用cpu->component的probe函數,沒有實現
    component = rtd->cpu_dai->component;
    if (component->driver->probe_order == order) {
        ret = soc_probe_component(card, component);
        if (ret < 0)
            return ret;
    }

    //調用codec driver的probe函數,沒有實現
    for (i = 0; i < rtd->num_codecs; i++) {
        component = rtd->codec_dais[i]->component;
        if (component->driver->probe_order == order) {
            ret = soc_probe_component(card, component);
            if (ret < 0)
                return ret;
        }
    }

    //調用platform driver的probe函數
    if (platform->component.driver->probe_order == order) {
        ret = soc_probe_component(card, &platform->component);
        if (ret < 0)
            return ret;
    }

    return 0;
}
static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
    struct snd_soc_dai_link *dai_link = &card->dai_link[num];
    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    int i, ret;

    /* config components */
    cpu_dai->platform = platform;
    cpu_dai->card = card;
    for (i = 0; i < rtd->num_codecs; i++)
        rtd->codec_dais[i]->card = card;

    //調用cpu_dai driver的probe函數,沒有實現
    if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) {
        if (cpu_dai->driver->probe) {
            ret = cpu_dai->driver->probe(cpu_dai);
        }
        cpu_dai->probed = 1;
    }
    //調用codec_dai driver的probe函數,沒有實現
    for (i = 0; i < rtd->num_codecs; i++) {
        ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
    }

    ret = soc_post_component_init(rtd, dai_link->name);
    ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);

    if (cpu_dai->driver->compress_dai) {
        ret = soc_new_compress(rtd, num);
    } else {
        if (!dai_link->params) {
            /* create the pcm */
            ret = soc_new_pcm(rtd, num);//創建pcm相關的信息和設備節點
        } else {
            INIT_DELAYED_WORK(&rtd->delayed_work,codec2codec_close_delayed_work);
            /* link the DAI widgets */
            ret = soc_link_dai_widgets(card, dai_link, rtd);
        }
    }
    return 0;
}
static int soc_probe_codec_dai(struct snd_soc_card *card,
                   struct snd_soc_dai *codec_dai,
                   int order)
{
    int ret;
    if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
        if (codec_dai->driver->probe) {
            ret = codec_dai->driver->probe(codec_dai);
        }
        /* mark codec_dai as probed and add to card dai list */
        codec_dai->probed = 1;
    }

    return 0;
}

註冊聲卡,在這個階段會遍歷聲卡下的所有邏輯設備,主要是PCM、MIX、CONTROL創建時創建的device,
並且調用各設備的註冊回調函數。

int snd_card_register(struct snd_card *card)
{
    int err;
    if (!card->registered) {
        err = device_add(&card->card_dev);
        card->registered = true;
    }

    if ((err = snd_device_register_all(card)) < 0)
        return err;

    if (*card->id) {
        /* make a unique id name from the given string */
        char tmpid[sizeof(card->id)];
        memcpy(tmpid, card->id, sizeof(card->id));
        snd_card_set_id_no_lock(card, tmpid, tmpid);
    } else {
        /* create an id from either shortname or longname */
        const char *src;
        src = *card->shortname ? card->shortname : card->longname;
        snd_card_set_id_no_lock(card, src,
                    retrieve_id_from_card_name(src));
    }
    snd_cards[card->number] = card;
    init_info_for_card(card);
    return 0;
}
int snd_device_register_all(struct snd_card *card)
{
    struct snd_device *dev;
    int err;
    list_for_each_entry(dev, &card->devices, list) {
        err = __snd_device_register(dev);
    }
    return 0;
}
static int __snd_device_register(struct snd_device *dev)
{
    if (dev->state == SNDRV_DEV_BUILD) {
        if (dev->ops->dev_register) {
            int err = dev->ops->dev_register(dev);
        }
        dev->state = SNDRV_DEV_REGISTERED;
    }
    return 0;
}

對於pcm,就是snd_pcm_dev_register函數,該回調函數建立了和用戶空間應用程序(alsa-lib)
通信所用的設備文件節點:/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc

static int snd_pcm_dev_register(struct snd_device *device)
{
    int cidx, err;
    struct snd_pcm_substream *substream;
    struct snd_pcm_notify *notify;
    char str[16];
    struct snd_pcm *pcm;
    struct device *dev;

    pcm = device->device_data;
    //將pcm加入到全局變量snd_pcm_devices,之後會創建/proc/asound/pcm文件
    err = snd_pcm_add(pcm);,

    for (cidx = 0; cidx < 2; cidx++) {
        int devtype = -1;
        if (pcm->streams[cidx].substream == NULL || pcm->internal)
            continue;
        switch (cidx) {
        case SNDRV_PCM_STREAM_PLAYBACK:
            sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//生成pcm playback設備節點的名字
            devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
            break;
        case SNDRV_PCM_STREAM_CAPTURE:
            sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//生成pcm capture設備節點的名字
            devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
            break;
        }
        dev = pcm->dev;
        if (!dev)
            dev = snd_card_get_device_link(pcm->card);
        /* register pcm */
        err = snd_register_device_for_dev(devtype, pcm->card,
                          pcm->device,
                          &snd_pcm_f_ops[cidx],
                          pcm, str, dev);
        //返回當前註冊的pcm設備,然後設置該pcm的屬性
        dev = snd_get_device(devtype, pcm->card, pcm->device);
        if (dev) {
            err = sysfs_create_groups(&dev->kobj, pcm_dev_attr_groups);
            put_device(dev);
        }
        //進行pcm定時器的初始化
        for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
            snd_pcm_timer_init(substream);
    }

    list_for_each_entry(notify, &snd_pcm_notify_list, list)
        notify->n_register(pcm);
    return 0;
}
發佈了40 篇原創文章 · 獲贊 16 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章