alsa声卡驱动一:声卡的创建

Linux ALSA声卡驱动之二:Platform 1.Platform驱动在ASoC中的作用 ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。在具体实现上,ASoC有把Platform驱动分为两个部分:snd_soc_platform_drive... 阅读详情

对于Alsa的介绍跟大概框架我这里就不做纤细的总结了,需要了解可以去看
http://blog.youkuaiyun.com/droidphone/article/details/6271122
这个博客的文章,写得很好,我也是在对他文章的阅读跟源代码的阅读做出属于自己的总结。

声卡的创建流程

这里写图片描述
图片是一个声卡的简单的创建流程,最主要要关心的是snd_card跟PCM的创建跟注册。
由PCM的创建,我们可以看出PCM是挂在声卡snd_card下的,用下图来看一下snd_card的框架。
这里写图片描述
由图片我们可以很清楚看出来,一个声卡驱动主要需要实现的,就做snd_card、pcm、挂在pcm下的plaback跟capture。
接下来以ASOC的框架来了解一下声卡创建的过程。

ASOC

ASoC–ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。在ASoc出现之前,内核对于SoC中的音频已经有部分的支持,不过会有一些局限性:
1、Codec驱动与SoC CPU的底层耦合过于紧密,这种不理想会导致代码的重复,例如,仅是wm8731的驱动,当时Linux中有分别针对4个平台的驱动代码。
2、音频事件没有标准的方法来通知用户,例如耳机、麦克风的插拔和检测,这些事件在移动设备中是非常普通的,而且通常都需要特定于机器的代码进行重新对音频路劲进行配置。
3、当进行播放或录音时,驱动会让整个codec处于上电状态,这对于PC没问题,但对于移动设备来说,这意味着浪费大量的电量。同时也不支持通过改变过取样频率和偏置电流来达到省电的目的。
ASoC正是为了解决上述种种问题而提出的,目前已经被整合至内核的代码树中:sound/soc。ASoC不能单独存在,他只是建立在标准ALSA驱动上的一个它必须和标准的ALSA驱动框架相结合才能工作。(来至http://blog.youkuaiyun.com/droidphone/article/details/7165482)

硬件架构

音频系统可以被划分为三个部分:1.板载硬件(Machine),2.Soc(Platform),3Codec;
1.Machine:指具体的某款开发板或者开发板,未platform以及code提供载体。
2.platform:指具体的某个Soc平台,主要跟音频驱动中,需要用的Soc的硬件资源有关,比如DMA,I2S,时钟等。将platform抽象出来的好处是,这样可以在不同的machine中使用而不需要改动。
3.codec:根据字面不难理解就是一个解码器,包含了I2S接口,A/D,D/A转化等。codec跟platform一样都是重用的部件,可以在不同的machine中使用不用改动。
这里写图片描述
图片为machine、platform、codec之间的关系。
我们以omapl138开发板为例,解码芯片tlv320aic3x。
我们先从裸机驱动方向思考:
soc(omapl138):需要做的工作EDMA、MCASP、时钟这方面的初始化,这部分工作在ASOC相当于platform,platform主要工作也是做这方面的初始化跟一些操作,其中还包含一个cpu_dai结构跟codec进行数据交互。
codec(tlv320aic3x):主要工作是通过i2c对芯片进行初始化跟配置,这部分工作也是相当于ASOC中codec的工作,对解码芯片进行初始化配置以及一些相对于的操作,以及包含一个codec_dai跟soc进行数据交互。
现在我们来思考这两部分是怎么关联起来的,其实这部分是在Machine完成的了。以我用的开发板为例子,machine在文件/sound/soc/davinci的davinci-evm.c中。

static int __init evm_init(void)
{
    struct snd_soc_card *evm_snd_dev_data;
    int index;
    int ret;

    if (machine_is_davinci_evm()) {
        evm_snd_dev_data = &dm6446_snd_soc_card_evm;
        index = 0;
    } else if (machine_is_davinci_dm355_evm()) {
        evm_snd_dev_data = &dm355_snd_soc_card_evm;
        index = 1;
    } else if (machine_is_davinci_dm365_evm()) {
        evm_snd_dev_data = &dm365_snd_soc_card_evm;
        index = 0;
    } else if (machine_is_davinci_dm6467_evm()) {
        evm_snd_dev_data = &dm6467_snd_soc_card_evm;
        index = 0;
    } else if (machine_is_davinci_da830_evm()) {
        evm_snd_dev_data = &da830_snd_soc_card;
        index = 1;
    } else if (machine_is_davinci_da850_evm()) {
        evm_snd_dev_data = &da850_snd_soc_card;
        index = 0;
    } else if (machine_is_davinci_da850_sdi()) {
#ifdef CONFIG_MACH_DAVINCI_DA850_SDI
        evm_snd_dev_data = &da850_sdi_snd_soc_card;
        index = 0;
#endif
    } else if (machine_is_omapl138_lcdkboard()) {
        evm_snd_dev_data = &da850_snd_soc_card;
        index = 0;
    } else
        return -EINVAL;

    evm_snd_device = platform_device_alloc("soc-audio", index);
    if (!evm_snd_device)
        return -ENOMEM;

    platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
    ret = platform_device_add(evm_snd_device);
    if (ret)
        platform_device_put(evm_snd_device);

    return ret;
}

static void __exit evm_exit(void)
{
    platform_device_unregister(evm_snd_device);
}

module_init(evm_init);
module_exit(evm_exit);

ret = platform_device_add(evm_snd_device);添加了一个platform_device,就一定会有platform_driver的存在,对应的platform_driver在/sound/soc/soc_core.c中。
platform_set_drvdata(evm_snd_device, evm_snd_dev_data);给evm_snd_device添加 了一个专用数据evm_snd_dev_data,用的开发板是davinci_da850,进入看,存在这样一个数据结构:

static struct snd_soc_card da850_snd_soc_card = {
    .name = "DA850/OMAP-L138 EVM",
    .owner = THIS_MODULE,
    .dai_link = &da850_evm_dai,
    .num_links = 1,
};

其中.dai_link = &da850_evm_dai就是我们machine跟据名字将platform跟codec联系起来的,进入&da850_evm_dai中

static struct snd_soc_dai_link da850_evm_dai = {
    .name = "TLV320AIC3X",
    .stream_name = "AIC3X",
    .cpu_dai_name= "davinci-mcasp.0",
    .codec_dai_name = "tlv320aic3x-hifi",
    .codec_name = "tlv320aic3x-codec.1-0018",
    .platform_name = "davinci-pcm-audio",
    .init = evm_aic3x_init,
    .ops = &evm_ops,
};

由结构体成员我们可以看到,我们上面提到的几个部分,codec、codec_dai、platform、cpu_dai的名字都在上面,就是通过名字的方式将这四个部分整合到machine中的。
接下来我们去看一下machine文件的platform_device对应的platform_driver,

static struct platform_driver soc_driver = {
    .driver     = {
        .name       = "soc-audio",
        .owner      = THIS_MODULE,
        .pm     = &snd_soc_pm_ops,
    },
    .probe      = soc_probe,
    .remove     = soc_remove,
};

进入.probe = soc_probe回调函数,

static int soc_probe(struct platform_device *pdev)
{
    struct snd_soc_card *card = platform_get_drvdata(pdev);
    int ret = 0;

    /*
     * no card, so machine driver should be registering card
     * we should not be here in that case so ret error
     */
    if (!card)
        return -EINVAL;

    /* Bodge while we unpick instantiation */
    card->dev = &pdev->dev;

    ret = snd_soc_register_card(card);
    if (ret != 0) {
        dev_err(&pdev->dev, "Failed to register card\n");
        return ret;
    }

    return 0;
}

card->dev = &pdev->dev;将card->dev 指向platf_device的私有数据,即上面提到的struct snd_soc_dai_link da850_evm_dai这个结构体,后面的声卡创建跟PCM创建跟这个结构体有这密切关系。
我们进入声卡的注册函数snd_soc_register_card(card);但其他的部分我们先不进行了解直接进入snd_soc_instantiate_cards()再进入snd_soc_instantiate_card(card)中:
这个函数的第一步是通过函数soc_bind_dai_link对card_link进行名字的关联

    struct snd_soc_dai_link *dai_link = &card->dai_link[num];
    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
    struct snd_soc_codec *codec;
    struct snd_soc_platform *platform;
    struct snd_soc_dai *codec_dai, *cpu_dai;
    const char *platform_name;

    if (rtd->complete)
        return 1;
    dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);

    /* do we already have the CPU DAI for this link ? */
    if (rtd->cpu_dai) {
        goto find_codec;
    }
    /* no, then find CPU DAI from registered DAIs*/
    list_for_each_entry(cpu_dai, &dai_list, list) {
        if (dai_link->cpu_dai_of_node) {
            if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
                continue;
        } else {
            if (strcmp(cpu_dai->name, dai_link->cpu_dai_name))
                continue;
        }

        rtd->cpu_dai = cpu_dai;
        goto find_codec;
    }

从list_for_each_entry(cpu_dai, &dai_list, list)取出cpu_dai,再跟card_link的cpu_dai_name进行匹配,如果匹配上将cpu_dai放入rtd->cpu_dai,既将匹配到的cpu_dai赋值给card的成员snd_soc_pcm_runtime的cpu_dai。后面的codec、codec_dai、platform是通过一样的方式进行匹配的。经过这个过程,card的成员snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
返回snd_soc_instantiate_card中
第二步、通过

ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
            card->owner, 0, &card->snd_card);

进行声卡的创建,对这个函数是不是很熟悉,就是最前面提到的声卡创建步骤的第一步;
第三步:调用各个子结构的probe函数,对soc跟cedec进行相对应的初始化

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_dai_link(card, i, order);
            if (ret < 0) {
                pr_err("asoc: failed to instantiate card %s: %d\n",
                   card->name, ret);
                goto probe_dai_err;
            }
        }
    }

soc_probe_dai_link()函数中做了比较多的事情,把他展开继续讨论:

static int soc_probe_dai_link(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_codec *codec = rtd->codec;
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
    int ret;

    dev_dbg(card->dev, "probe %s dai link %d late %d\n",
            card->name, num, order);

    /* config components */
    codec_dai->codec = codec;
    cpu_dai->platform = platform;
    codec_dai->card = card;
    cpu_dai->card = card;

    /* set default power off timeout */
    rtd->pmdown_time = pmdown_time;

    /* probe the cpu_dai */
    if (!cpu_dai->probed &&
            cpu_dai->driver->probe_order == order) {
        if (!try_module_get(cpu_dai->dev->driver->owner))
            return -ENODEV;

        if (cpu_dai->driver->probe) {
            ret = cpu_dai->driver->probe(cpu_dai);
            if (ret < 0) {
                printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",
                        cpu_dai->name);
                module_put(cpu_dai->dev->driver->owner);
                return ret;
            }
        }
        cpu_dai->probed = 1;
        /* mark cpu_dai as probed and add to card dai list */
        list_add(&cpu_dai->card_list, &card->dai_dev_list);
    }

    /* probe the CODEC */
    if (!codec->probed &&
            codec->driver->probe_order == order) {
        ret = soc_probe_codec(card, codec);
        if (ret < 0)
            return ret;
    }

    /* probe the platform */
    if (!platform->probed &&
            platform->driver->probe_order == order) {
        ret = soc_probe_platform(card, platform);
        if (ret < 0)
            return ret;
    }

    /* probe the CODEC DAI */
    if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
        if (codec_dai->driver->probe) {
            ret = codec_dai->driver->probe(codec_dai);
            if (ret < 0) {
                printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",
                        codec_dai->name);
                return ret;
            }
        }

        /* mark codec_dai as probed and add to card dai list */
        codec_dai->probed = 1;
        list_add(&codec_dai->card_list, &card->dai_dev_list);
    }

    /* complete DAI probe during last probe */
    if (order != SND_SOC_COMP_ORDER_LAST)
        return 0;

    ret = soc_post_component_init(card, codec, num, 0);
    if (ret)
        return ret;

    ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
    if (ret < 0)
        printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");

    /* create the pcm */
    ret = soc_new_pcm(rtd, num);
    if (ret < 0) {
        printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);
        return ret;
    }

    /* add platform data for AC97 devices */
    if (rtd->codec_dai->driver->ac97_control)
        snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);

    return 0;
}

分别调用cpu_dai、codec、platform、codec_dai的probe函数,这些函数主要工作也是进行相对于的初始化,这里不作探讨,留到后面章节的总结再说。函数最后ret = soc_new_pcm(rtd, num),进行PCM设备的创建,进入函数

int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
    struct snd_soc_codec *codec = rtd->codec;
    struct snd_soc_platform *platform = rtd->platform;
    struct snd_soc_dai *codec_dai = rtd->codec_dai;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
    struct snd_pcm *pcm;
    char new_name[64];
    int ret = 0, playback = 0, capture = 0;

    soc_pcm_ops->open   = soc_pcm_open;
    soc_pcm_ops->close  = soc_pcm_close;
    soc_pcm_ops->hw_params  = soc_pcm_hw_params;
    soc_pcm_ops->hw_free    = soc_pcm_hw_free;
    soc_pcm_ops->prepare    = soc_pcm_prepare;
    soc_pcm_ops->trigger    = soc_pcm_trigger;
    soc_pcm_ops->pointer    = soc_pcm_pointer;

    /* check client and interface hw capabilities */
    snprintf(new_name, sizeof(new_name), "%s %s-%d",
            rtd->dai_link->stream_name, codec_dai->name, num);

    if (codec_dai->driver->playback.channels_min)
        playback = 1;
    if (codec_dai->driver->capture.channels_min)
        capture = 1;

    dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
    ret = snd_pcm_new(rtd->card->snd_card, new_name,
            num, playback, capture, &pcm);
    if (ret < 0) {
        printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
        return ret;
    }

    /* DAPM dai link stream work */
    INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);

    rtd->pcm = pcm;
    pcm->private_data = rtd;
    if (platform->driver->ops) {
        soc_pcm_ops->mmap = platform->driver->ops->mmap;
        soc_pcm_ops->pointer = platform->driver->ops->pointer;
        soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
        soc_pcm_ops->copy = platform->driver->ops->copy;
        soc_pcm_ops->silence = platform->driver->ops->silence;
        soc_pcm_ops->ack = platform->driver->ops->ack;
        soc_pcm_ops->page = platform->driver->ops->page;
    }

    if (playback)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);

    if (capture)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);

    if (platform->driver->pcm_new) {
        ret = platform->driver->pcm_new(rtd);
        if (ret < 0) {
            pr_err("asoc: platform pcm constructor failed\n");
            return ret;
        }
    }

    pcm->private_free = platform->driver->pcm_free;
    printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
        cpu_dai->name);
    return ret;
}

主要工作实行soc_pcm_ops成员函数

    soc_pcm_ops->open   = soc_pcm_open;
    soc_pcm_ops->close  = soc_pcm_close;
    soc_pcm_ops->hw_params  = soc_pcm_hw_params;
    soc_pcm_ops->hw_free    = soc_pcm_hw_free;
    soc_pcm_ops->prepare    = soc_pcm_prepare;
    soc_pcm_ops->trigger    = soc_pcm_trigger;
    soc_pcm_ops->pointer    = soc_pcm_pointer;

创建一个PCM:

ret = snd_pcm_new(rtd->card->snd_card, new_name,
            num, playback, capture, &pcm);

这一步就是最早说的声卡驱动创建步骤的第四步。
回到snd_soc_init_codec_cache中
第四步:对声卡进行注册

ret = snd_card_register(card->snd_card);

到此我们队ASOC框架下的声卡创建过程也大概探讨完了,这对我们对于我们做一个声卡驱动需要做的工作内容理解有很大帮助。下面是Asoc数据结构的静态关系图。
这里写图片描述
来自于:http://blog.youkuaiyun.com/droidphone/article/details/7165482
根据上面所说,我们可以大概知道,一个声卡的驱动主要工作是:
1.实现codec实例
2.实现codec_dai实例
3.实现platform实例
4.实现cpu_dai实例
5.machine通过名字对各个部分进行关联。
后面内容对这几个部分再进行了解。

有任何错误的地方,还望指出。

安装ALSA声卡驱动程序 1.下载下面4个软件包alsa-driver-1.0.22.tar.bz2alsa-lib-1.0.22.tar.bz2alsa-utils-1.0.22.tar.bz2alsaconf-0.4.3b.tar.gz   其中,前三个可以到http://www.alsa-project.org/main/index.php/Main_Page下载,alsaconf则到http:/ 阅读详情

相关推荐

安卓声卡驱动:1.ALSA简介

一 概述 安卓内核采用的是Linux内核,所以安卓也沿用了Linux内核内置的音频子系统。 Linux内核的音频子系统全称是Advanced Linux Sound Architecture,简称ALSA。 ALSA官网为https://www.alsa-project.org/wiki/Main_Page,在官网上有一些编程手册以及API介绍,也可以直接到Linux内核源码的 Documentation/sound 目录下去查阅编程信息 ALSA的版本是随同L

baidu_26866585的博客 2765

IMX6ul下tlv320aic3x音频驱动调试

原理图: tlv320aic3x音频芯片 功率放大器 移植分为三步: 1.codec 驱动 sound/soc/codecs/tlv320aic3x.c 2.平台驱动 sound/soc/imx/imx-tlv320aic3x.c 3.添加板文件   arch/arm/mach-mx6/board-mx6q_sabres

zhuwei852159448的博客 5274

基于imx6ul下调试tlv320aic3x声卡

因为这次声卡芯片是tlv320aic3101,从网上搜相关的资料可谓是少之又少,基本上都是有linux2.6版本的,基本上都是没有用到设备树DTB去适配,看了一下内核源码linux4.1.15只有sound/soc/codecs有tlv320aic3x.c和tlv320aic3x.h这两个文件,而sound/soc/fsl/就没有驱动tlv320aic3x的驱动文件。需要自己从网上去下载或者参照imx-sgtl5000.c去重新写一个。内核版本:linux4.1.15。核心板:imx6ul。

qq_45147279的博客 1331

Linux 内核 ASoC DMA 引擎驱动程序

Linux 内核 ASoC 框架,在概念上将嵌入式音频系统拆分为多个可复用的组件驱动程序,包括 Codec 类驱动程序、平台类驱动程序和机器类驱动程序。在实现上,机器类驱动程序用和结构描述,属于平台类驱动程序的 DMA 引擎驱动程序由结构描述,codec 类驱动程序和 I2S 等驱动程序,由和等结构描述。除平台类驱动程序外的各种驱动程序都通过 component 抽象组织在一起,即这些驱动程序都作为注册给 Linux 内核 ASoC 框架,Linux 内核 ASoC 框架为它们各自创建结构对象,并保存在。

tq08g2z的专栏 1470

Linux ALSA驱动框架(一)--ALSA架构简介--声卡的创建

(1)ALSA简介(1)Native ALSA Application:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制ALSA Library API:alsa 用户库接口,常见有 tinyalsa、alsa-libALSA CORE:alsa 核心层,向上提供逻辑设备(PCM/CTL/MIDI/TIMER/…)系统调用,向下驱动...

sinat_37817094的博客 4万+

日志异常检测详细整理,看这一篇就够了(附各项目代码链接)

对于不用的日志键,用它们的参数值向量训练一个参数值异常检测模型来检测有这些参数值反映的系统性能异常。与标准的GAN框架一样,D和G的参数是基于D的输出来更新的,因此鉴别器D可以被训练为尽可能敏感地将正确的标签分配给真实和虚假序列,而生成器将被训练为尽可能聪明地在经过足够多轮的迭代后欺骗鉴别器,即误导D将真实标签分配给虚假序列。LR:逻辑回归是有监督的模型,将处理后的特征矩阵以及标记作为模型的输入,得到训练后的模型,用此模型预测新出现的特征向量,以此判断特征向量是否为异常的,进而判断日志异常。

qq_20368489的博客 1万+

IMX6Q下tlv320aic3x音频驱动移植

imx6q下tlv320aic3x音频驱动移植

一梦十年 5805

SAP UI5 和 Fiori 的一些小技巧合集 - 持续更新中

本文介绍笔者从事 SAP UI5 和 Fiori 开发多年以来,积累的一些经验和小技巧。会保持更新。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 913

Linux ALSA声卡驱动之六:ASoC架构中的Machine

前面一节的内容我们提到,ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结

DroidPhone的专栏 7万+

SAP RAP 里的 EML 全解:从语言定位到事务语义,再到真实案例的落地方式

摘要:EML(Entity Manipulation Language)是ABAP RAP模型的核心组件,作为ABAP的子集用于操作业务对象(BO)。它通过声明式语法(READ/MODIFY/EXECUTE/COMMIT)统一访问BO的行为与数据,并由RAP运行时自动处理校验、锁定、草稿管理等生命周期。EML将业务语义融入代码,确保与Fiori应用共享同一套校验逻辑,并通过FAILED/REPORTED/MAPPED管道传递错误与消息。其事务控制(IN LOCAL MODE与COMMIT ENTITIES)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 528

仿真学习系列(三十):Sigrity设计文件(Altium Designer和Allegro)导入

很多刚接触 Sigrity 的工程师都会产生疑问:Sigrity 功能这么多,是不是门槛很高?我能学得会吗? 其实,Sigrity 的核心并不在“操作难”,而是在“理解仿真背后的机理”。一旦掌握基本流程,你会发现它并没有想象中那么复杂,反而能大大提升你的高速设计能力。本节内容,就是从最简单的一步开始 —— 把你的设计文件正确导入 Sigrity。

案例文章,高速信号 524

alsa架构下的声卡驱动开发一:声卡的创建与注册

我们直入主题,想要在alsa结构上完成驱动开发,其实就是依赖三板斧,首先这第一板斧就是需要先创建声卡,这里直接调用现成的函数就行,这里以一个platform driver的probe函数为例。int err;return err;

st13772178063的博客 1014

开源项目-智能算法中台管理系统

智能算法中台管理系统,系统主要有样本中心,算法中心,模型中心等模块

u010479989的博客 3001

Linux ALSA声卡驱动之二:声卡的创建

声明:本博内容均由http://blog.youkuaiyun.com/droidphone原创,转载请注明出处,谢谢! 1. struct snd_card   1.1. snd_card是什么 snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_c

DroidPhone的专栏 9万+

【微服务】SpringBoot 整合 FastExcel 实现导入导出操作详解

SpringBoot 整合 FastExcel 实现导入导出操作详解

congge 4235

nRF52832——大量数据传输时导致蓝牙断开连接,且无法被搜索到的解决方案(广播参数的设置、程序设计方法)

有时蓝牙通信单次发送数据量较大,且连续发送多次的时候,你会发现蓝牙就会出现异常,可能运行30min,40min,或者1h后,蓝牙就不能传输数据了,且无法被搜索到,用主机扫描蓝牙时可能会提示类似这样的DEBUG信息:bta_gattc_mark_bg_conn unable to find the bg connection mask for bd_addr=xx:xx:xx:xx:xx:xx。

数字世界 万物互联 2006

Linux ALSA声卡驱动之一:ALSA架构简介

声明:本博内容均由http://blog.youkuaiyun.com/droidphone原创,转载请注明出处,谢谢! 一.  概述     ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org/。

DroidPhone的专栏 15万+

金三银四!风控建模岗面试题库大全:从理论到实战避坑指南

风控建模面试大全

阿黎逸阳的博客 767

Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

一、Machine 简介 Machine是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体,用于描述一块电路板,它指明此块电路板上用的是哪个Platform和哪...

肖岩 5809

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Linux ALSA 之二:ALSA 声卡与设备

ALSA 声卡与设备

年少生而为人 7592

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

ALSA声卡驱动(二)声卡创建

文章目录 ALSA声卡驱动(一)初识alsa 目录文章目录1、声卡实例1.1、struct snd_card1.2、struct snd_devide2、声卡的创建流程2.1、创建一个声卡实例2.2、创建声卡芯片的专用数据2.3、设置Driver和ID和名字2.4、创建声卡的功能部件(逻辑设备)2.5、注册声卡3、snd_card_create()和snd_card_new()4、snd_card_register()总结 1、声卡实例 1.1、struct snd_card   snd_card可以说是整

Mr_zhangsq的博客 1555

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

上一篇: i2c子系统学习总结
下一篇: alsa声卡驱动二:ASoC框架中的Codec
BERLIN23
博客等级 码龄14年 7粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值