一、ALSA驱动框架介绍

1、ALSA介绍

ALSA(Advanced Linux Sound Architecture-高级linux声音架构),目前已经成为了linux的主流音频体系结构,ALSA在内核部分提供alsa-driver对音频驱动进行耦合和管理,在用户空间空间提供alsa-lib,应用开发人员可以使用alsa-lib接口控制声卡。

2、音频播放流程

一个音频文件的播放流程如下图,由dma将音频文件的内容从内存搬运到IIS控制器,后传送到codec音频解码芯片,将数字信号转换为模拟信号输出给耳机或者喇叭

3、alsa设备文件介绍

目录:/dev/snd

controlC0:用于声卡的控制,例如通道选择,混音,麦克风的控制等;

midiC0D0:用于播放midi音频;

pcmC0D0c : 用于录音的pcm设备;

pcmC0D0p :用于播放的pcm设备;

seq :音序器;

timer :定时器;

二、CPU_DAI驱动分析

1、CPU_DAI相关的重要数据,便于对cpu_dai驱动进行理解

struct snd_soc_dai {const char *name;  //描述dai的名称int id; struct device *dev;   /* driver ops */struct snd_soc_dai_driver *driver;    //指向snd_soc_dai_driver,这个结构里描述了cpu_dai的支持类型和各种对cpu_dai进行操作的api/* DAI runtime info */unsigned int capture_active:1;      /* stream is in use */unsigned int playback_active:1;       /* stream is in use */unsigned int symmetric_rates:1;unsigned int symmetric_channels:1;unsigned int symmetric_samplebits:1;unsigned int active;unsigned char probed:1;struct snd_soc_dapm_widget *playback_widget;struct snd_soc_dapm_widget *capture_widget;/* DAI DMA data */void *playback_dma_data;    //cpu_dai播放时使用的dmavoid *capture_dma_data;    //cpu_dai录制时使用的dma/* Symmetry data - only valid if symmetry is being enforced */unsigned int rate;unsigned int channels;unsigned int sample_bits;/* parent platform/codec */struct snd_soc_codec *codec;    //指向所关联的codec设备struct snd_soc_component *component;     //指向所关联的component/* CODEC TDM slot masks and params (for fixup) */unsigned int tx_mask;unsigned int rx_mask;struct list_head list;
};
struct snd_soc_dai_driver {/* DAI description */const char *name;    unsigned int id;unsigned int base;struct snd_soc_dobj dobj;/* DAI driver callbacks */int (*probe)(struct snd_soc_dai *dai);int (*remove)(struct snd_soc_dai *dai);int (*suspend)(struct snd_soc_dai *dai);    //休眠接口int (*resume)(struct snd_soc_dai *dai);    //唤醒接口/* compress dai */int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);/* Optional Callback used at pcm creation*/int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,struct snd_soc_dai *dai);/* DAI is also used for the control bus */bool bus_control;/* ops */const struct snd_soc_dai_ops *ops;    //对cpu_dai进行操作的api接口集合const struct snd_soc_cdai_ops *cops;/* DAI capabilities */struct snd_soc_pcm_stream capture;    //描述播放模式dai支持的参数struct snd_soc_pcm_stream playback;    //描述录制模式dai支持的参数unsigned int symmetric_rates:1;unsigned int symmetric_channels:1;unsigned int symmetric_samplebits:1;/* probe ordering - for components with runtime dependencies */int probe_order;int remove_order;
};
struct snd_soc_dai_ops {/** DAI clocking configuration, all optional.* Called by soc_card drivers, normally in their hw_params.*///设置时钟int (*set_sysclk)(struct snd_soc_dai *dai,int clk_id, unsigned int freq, int dir);//设置锁相环int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,unsigned int freq_in, unsigned int freq_out);//设置时钟分频系数int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);/** DAI format configuration* Called by soc_card drivers, normally in their hw_params.*/int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);int (*xlate_tdm_slot_mask)(unsigned int slots,unsigned int *tx_mask, unsigned int *rx_mask);int (*set_tdm_slot)(struct snd_soc_dai *dai,unsigned int tx_mask, unsigned int rx_mask,int slots, int slot_width);int (*set_channel_map)(struct snd_soc_dai *dai,unsigned int tx_num, unsigned int *tx_slot,unsigned int rx_num, unsigned int *rx_slot);int (*get_channel_map)(struct snd_soc_dai *dai,unsigned int *tx_num, unsigned int *tx_slot,unsigned int *rx_num, unsigned int *rx_slot);int (*set_tristate)(struct snd_soc_dai *dai, int tristate);int (*set_sdw_stream)(struct snd_soc_dai *dai,void *stream, int direction);/** DAI digital mute - optional.* Called by soc-core to minimise any pops.*/int (*digital_mute)(struct snd_soc_dai *dai, int mute);int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);/** ALSA PCM audio operations - all optional.* Called by soc-core during audio PCM operations.*/int (*startup)(struct snd_pcm_substream *,struct snd_soc_dai *);void (*shutdown)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*hw_params)(struct snd_pcm_substream *,struct snd_pcm_hw_params *, struct snd_soc_dai *);int (*hw_free)(struct snd_pcm_substream *,struct snd_soc_dai *);int (*prepare)(struct snd_pcm_substream *,struct snd_soc_dai *);/** NOTE: Commands passed to the trigger function are not necessarily* compatible with the current state of the dai. For example this* sequence of commands is possible: START STOP STOP.* So do not unconditionally use refcounting functions in the trigger* function, e.g. clk_enable/disable.*/int (*trigger)(struct snd_pcm_substream *, int,struct snd_soc_dai *);int (*bespoke_trigger)(struct snd_pcm_substream *, int,struct snd_soc_dai *);/** For hardware based FIFO caused delay reporting.* Optional.*/snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,struct snd_soc_dai *);
};

2、DTS-NODE

i2s1_8ch: i2s@fe410000 {compatible = "rockchip,rk3568-i2s-tdm";reg = <0x0 0xfe410000 0x0 0x1000>;interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru MCLK_I2S1_8CH_TX>, <&cru MCLK_I2S1_8CH_RX>, <&cru HCLK_I2S1_8CH>;clock-names = "mclk_tx", "mclk_rx", "hclk";dmas = <&dmac1 2>, <&dmac1 3>;dma-names = "tx", "rx";resets = <&cru SRST_M_I2S1_8CH_TX>, <&cru SRST_M_I2S1_8CH_RX>;reset-names = "tx-m", "rx-m";rockchip,cru = <&cru>;rockchip,grf = <&grf>;#sound-dai-cells = <0>;pinctrl-names = "default";pinctrl-0 = <&i2s1m0_sclktx&i2s1m0_sclkrx&i2s1m0_lrcktx&i2s1m0_lrckrx&i2s1m0_sdi0&i2s1m0_sdi1&i2s1m0_sdi2&i2s1m0_sdi3&i2s1m0_sdo0&i2s1m0_sdo1&i2s1m0_sdo2&i2s1m0_sdo3>;status = "disabled";
};

3、rockchip_i2s_tdm_probe分析

cpu_dai驱动从dts获取到cpu_dai硬件信息后进入static int rockchip_i2s_tdm_probe(struct platform_device *pdev)函数,进行一系列cpu_dai的硬件初始化工作

static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
{...//初始化snd_soc_dai_driver结构,dai的支持配置和各种用于控制dai的api接口rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai);//从设备树获取bclk_fs值,没有默认64i2s_tdm->bclk_fs = 64;if (!of_property_read_u32(node, "rockchip,bclk-fs", &val)) {if ((val >= 32) && (val % 2 == 0))i2s_tdm->bclk_fs = val;}//从设备树中读取各种初始化cpu_dai相关的设置保存到i2s_tdm,例如I2S的时钟参数等of_property_read_u32(node, ..., &val);devm_reset_control_get(&pdev->dev, ...);devm_clk_get(&pdev->dev, "hclk")devm_clk_get(&pdev->dev, "mclk_tx");devm_clk_get(&pdev->dev, "mclk_rx")//获取cpu_dai的寄存器地址,并初始化cpu_daiplatform_get_resource(pdev, IORESOURCE_MEM, 0);devm_ioremap_resource(&pdev->dev, res);//初始化regmap   devm_regmap_init_mmio(&pdev->dev, regs, &rockchip_i2s_tdm_regmap_config);//TX/RX的dma参数获取i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;i2s_tdm->playback_dma_data.maxburst = 8;i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR;i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;i2s_tdm->capture_dma_data.maxburst = 8;//初始化cpu_dai的tx和rx通路rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node);rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node);i2s_tdm->soc_data->init(&pdev->dev, res->start);//注册component组件devm_snd_soc_register_component(&pdev->dev,&rockchip_i2s_tdm_component,soc_dai, 1);- >snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);//获取dma    devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);->snd_soc_add_platform(dev, &pcm->platform,&dmaengine_pcm_platform);return 0;
}

snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai)后续分析

snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai)->snd_soc_component_initialize(cmpnt, cmpnt_drv, dev); //初始化component//注册了一个dai,并填充snd_soc_dai_driver数据snd_soc_register_dais(cmpnt, dai_drv, num_dai, true); //将component加入component_list链表snd_soc_component_add(cmpnt);

该函数被传入两个关键参数struct snd_soc_component_driver cmpnt_drv、struct snd_soc_dai_driver dai_drv,定义如下

static const struct snd_soc_component_driver rockchip_i2s_component = {.name = DRV_NAME,
};
struct snd_soc_dai_driver rockchip_i2s_tdm_dai = {.probe = rockchip_i2s_tdm_dai_probe,    .playback = {.stream_name = "Playback",.channels_min = 2,.channels_max = 16,.rates = SNDRV_PCM_RATE_8000_192000,.formats = (SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE |SNDRV_PCM_FMTBIT_S24_LE |SNDRV_PCM_FMTBIT_S32_LE),},.capture = {.stream_name = "Capture",.channels_min = 2,.channels_max = 16,.rates = SNDRV_PCM_RATE_8000_192000,.formats = (SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE |SNDRV_PCM_FMTBIT_S24_LE |SNDRV_PCM_FMTBIT_S32_LE),},//对cpu_dai的api操作集合,如dai的时钟配置、格式配置等.ops = &rockchip_i2s_tdm_dai_ops,
};

snd_soc_add_platform(dev, &pcm->platform,&dmaengine_pcm_platform)后续分析

dmaengine_pcm_request_chan_of(pcm, dev, config);//根据name去dts中找资源,申请对应的DMA通道chan = dma_request_slave_channel_reason(dev, name);return of_dma_request_slave_channel(dev->of_node, name);//注册platform到ASoC Coreret = snd_soc_add_platform(dev, &pcm->platform,&dmaengine_pcm_platform);

platform中需要完成音频数据管理和音频数据的dma搬运,其中就涉及到了dma相关的操作,snd_soc_add_platform的目的就是完成dma通道的申请,并将pcm和dma关联起来

三、CODEC驱动分析

1、CODEC相关的重要数据

static const struct regmap_config rk817_codec_regmap_config = {.name = "rk817-codec",.reg_bits = 8,    //寄存器地址位数(必选项).val_bits = 8,    //寄存器值位数(必选项).reg_stride = 1,.max_register = 0x4f,    //最大寄存器值.cache_type = REGCACHE_FLAT,.volatile_reg = rk817_volatile_register,    .writeable_reg = rk817_codec_register,    //寄存器是否可写.readable_reg = rk817_codec_register,   //寄存器是否可写读.reg_defaults = rk817_reg_defaults,    //默认寄存器配置参数.num_reg_defaults = ARRAY_SIZE(rk817_reg_defaults),
};

2、DTS-NODE

rk809_codec: codec {#sound-dai-cells = <0>;compatible = "rockchip,rk809-codec", "rockchip,rk817-codec";clocks = <&cru I2S1_MCLKOUT>;clock-names = "mclk";assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>;assigned-clock-rates = <12288000>;assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>;pinctrl-names = "default","spk_gpio";pinctrl-0 = <&i2s1m0_mclk>;pinctrl-1 = <&spk_ctl_gpio>;hp-volume = <20>;spk-volume = <3>;//mic-in-differential;capture-volume = <0>;io-channels = <&saradc 4>;hp-det-adc-value = <1000>;spk-ctl-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;status = "okay";
};

3、rk817_platform_probe分析

codec_dai驱动从dts获取到硬件信息后进入static int rk817_platform_probe(struct platform_device *pdev)函数,进行一系列codec_dai的硬件初始化工作

static int rk817_platform_probe(struct platform_device *pdev)
{...rk817_codec_data = devm_kzalloc(&pdev->dev,sizeof(struct rk817_codec_priv),GFP_KERNEL);    //给rk817_codec_data申请空间platform_set_drvdata(pdev, rk817_codec_data); //rk817_codec_data绑定pdev//从dts节点中获取adc通道、gpio节点、volume值等rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data); //regmap初始化rk817_codec_data->regmap = devm_regmap_init_i2c(rk817->i2c,&rk817_codec_regmap_config);//mclk获取rk817_codec_data->mclk = devm_clk_get(&pdev->dev, "mclk");//componen注册devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817,rk817_dai, ARRAY_SIZE(rk817_dai));...
}

devm_snd_soc_register_component后续分析:初始化snd_soc_component的实例,然后注册dai,最终将注册的dai放入component->dai_list中,然后将分配的component放入到component_list链表中,遍历全局的component链表可以找到cpu_dai,codec_dai

devm_snd_soc_register_component->snd_soc_register_component->snd_soc_add_component->snd_soc_component_initialize    //component初始化snd_soc_register_dais    //dai注册snd_soc_component_add    //component加入全局链表

四、MACHINE(simple-card.c)驱动分析

1、MACHINE相关的重要数据

struct snd_soc_dai_link {/* config - must be set by machine driver */const char *name;         /* Codec name */const char *stream_name;        /* Stream name *//** You MAY specify the link's CPU-side device, either by device name,* or by DT/OF node, but not both. If this information is omitted,* the CPU-side DAI is matched using .cpu_dai_name only, which hence* must be globally unique. These fields are currently typically used* only for codec to codec links, or systems using device tree.*/const char *cpu_name;struct device_node *cpu_of_node;/** You MAY specify the DAI name of the CPU DAI. If this information is* omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node* only, which only works well when that device exposes a single DAI.*/const char *cpu_dai_name;/** You MUST specify the link's codec, either by device name, or by* DT/OF node, but not both.*/const char *codec_name;struct device_node *codec_of_node;/* You MUST specify the DAI name within the codec */const char *codec_dai_name;struct snd_soc_dai_link_component *codecs;unsigned int num_codecs;/** You MAY specify the link's platform/PCM/DMA driver, either by* device name, or by DT/OF node, but not both. Some forms of link* do not need a platform.*/const char *platform_name;struct device_node *platform_of_node;int id;   /* optional ID for machine driver link identification */const struct snd_soc_pcm_stream *params;unsigned int num_params;unsigned int dai_fmt;           /* format to set on init */enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM *//* codec/machine specific init - e.g. add machine controls */int (*init)(struct snd_soc_pcm_runtime *rtd);/* optional hw_params re-writing for BE and FE sync */int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,struct snd_pcm_hw_params *params);/* machine stream operations */const struct snd_soc_ops *ops;const struct snd_soc_compr_ops *compr_ops;/* Mark this pcm with non atomic ops */bool nonatomic;/* For unidirectional dai links */unsigned int playback_only:1;unsigned int capture_only:1;/* Keep DAI active over suspend */unsigned int ignore_suspend:1;/* Symmetry requirements */unsigned int symmetric_rates:1;unsigned int symmetric_channels:1;unsigned int symmetric_samplebits:1;/* Do not create a PCM for this DAI link (Backend link) */unsigned int no_pcm:1;/* This DAI link can route to other DAI links at runtime (Frontend)*/unsigned int dynamic:1;/* This DAI link can be reconfigured at runtime (Backend) */unsigned int dynamic_be:1;/** This DAI can support no host IO (no pcm data is* copied to from host)*/unsigned int no_host_mode:2;/* DPCM capture and Playback support */unsigned int dpcm_capture:1;unsigned int dpcm_playback:1;/* DPCM used FE & BE merged format */unsigned int dpcm_merged_format:1;/* DPCM used FE & BE merged channel */unsigned int dpcm_merged_chan:1;/* DPCM used FE & BE merged rate */unsigned int dpcm_merged_rate:1;/* pmdown_time is ignored at stop */unsigned int ignore_pmdown_time:1;/* Do not create a PCM for this DAI link (Backend link) */unsigned int ignore:1;struct list_head list; /* DAI link list of the soc card */struct snd_soc_dobj dobj; /* For topology *//* this value determines what all ops can be started asynchronously */enum snd_soc_async_ops async_ops;
};
struct snd_soc_card {const char *name;const char *long_name;const char *driver_name;char dmi_longname[80];char topology_shortname[32];struct device *dev;struct snd_card *snd_card;struct module *owner;struct mutex mutex;struct mutex dapm_mutex;struct mutex dapm_power_mutex;bool instantiated;bool topology_shortname_created;int (*probe)(struct snd_soc_card *card);int (*late_probe)(struct snd_soc_card *card);int (*remove)(struct snd_soc_card *card);/* the pre and post PM functions are used to do any PM work before and* after the codec and DAI's do any PM work. */int (*suspend_pre)(struct snd_soc_card *card);int (*suspend_post)(struct snd_soc_card *card);int (*resume_pre)(struct snd_soc_card *card);int (*resume_post)(struct snd_soc_card *card);/* callbacks */int (*set_bias_level)(struct snd_soc_card *,struct snd_soc_dapm_context *dapm,enum snd_soc_bias_level level);int (*set_bias_level_post)(struct snd_soc_card *,struct snd_soc_dapm_context *dapm,enum snd_soc_bias_level level);int (*add_dai_link)(struct snd_soc_card *,struct snd_soc_dai_link *link);void (*remove_dai_link)(struct snd_soc_card *,struct snd_soc_dai_link *link);long pmdown_time;/* CPU <--> Codec DAI links  */struct snd_soc_dai_link *dai_link;  /* predefined links only */int num_links;  /* predefined links only */struct list_head dai_link_list; /* all links */int num_dai_links;struct list_head rtd_list;int num_rtd;/* optional codec specific configuration */struct snd_soc_codec_conf *codec_conf;int num_configs;/** optional auxiliary devices such as amplifiers or codecs with DAI* link unused*/struct snd_soc_aux_dev *aux_dev;int num_aux_devs;struct list_head aux_comp_list;const struct snd_kcontrol_new *controls;int num_controls;/** Card-specific routes and widgets.* Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.*/const struct snd_soc_dapm_widget *dapm_widgets;int num_dapm_widgets;const struct snd_soc_dapm_route *dapm_routes;int num_dapm_routes;const struct snd_soc_dapm_widget *of_dapm_widgets;int num_of_dapm_widgets;const struct snd_soc_dapm_route *of_dapm_routes;int num_of_dapm_routes;bool fully_routed;struct work_struct deferred_resume_work;/* lists of probed devices belonging to this card */struct list_head component_dev_list;struct list_head widgets;struct list_head paths;struct list_head dapm_list;struct list_head dapm_dirty;/* attached dynamic objects */struct list_head dobj_list;/* Generic DAPM context for the card */struct snd_soc_dapm_context dapm;struct snd_soc_dapm_stats dapm_stats;struct snd_soc_dapm_update *update;#ifdef CONFIG_DEBUG_FSstruct dentry *debugfs_card_root;struct dentry *debugfs_pop_time;
#endifu32 pop_time;void *drvdata;
};

2、DTS-NODE

    rk809_sound: rk809-sound {status = "okay";compatible = "simple-audio-card";simple-audio-card,format = "i2s";simple-audio-card,name = "rockchip,rk809-codec";simple-audio-card,mclk-fs = <256>;simple-audio-card,widgets ="Microphone", "Mic Jack","Headphone", "Headphone Jack";simple-audio-card,routing ="Mic Jack", "MICBIAS1","IN1P", "Mic Jack","Headphone Jack", "HPOL","Headphone Jack", "HPOR";simple-audio-card,cpu {sound-dai = <&i2s1_8ch>;};simple-audio-card,codec {sound-dai = <&rk809_codec>;};};

3、asoc_simple_card_probe分析

codec_dai驱动从dts获取到cpu_dai和codec_dai的连接关系和节点后,进入asoc_simple_card_probe,进行声卡的注册和初始化工作

static int asoc_simple_card_probe(struct platform_device *pdev)
{struct snd_soc_dai_link *dai_link;struct snd_soc_card *card;...//初始化snd_soc_cardcard = simple_priv_to_card(priv);card->owner       = THIS_MODULE;card->dev     = dev;card->dai_link        = priv->dai_link;card->num_links     = num;card->probe       = asoc_simple_soc_card_probe;//从DTS中获取cpu_dai\codec_dai节点、widgets、routing等信息,填充snd_soc_dai_linkasoc_simple_card_parse_of(priv);->asoc_simple_card_of_parse_widgets(card, PREFIX); asoc_simple_card_of_parse_routing(card, PREFIX, 1);asoc_simple_card_dai_link_of(node, priv, 0, true);//实例化和填充card,绑定dailink,创建音频节点pcmC0D0P,并完成cpu_dai和codec的probe devm_snd_soc_register_card(dev, card);snd_soc_register_card(card);->snd_soc_instantiate_card(card); ...
}

devm_snd_soc_register_card(dev, card)的分析流程如下:

    devm_snd_soc_register_card(dev, card);snd_soc_register_card(card);->snd_soc_instantiate_card(card); ->soc_bind_dai_link(card, &card->dai_link[i]);snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card); //绑定完成后申请一个声卡card->probe(card); //仅初始化一次声卡soc_probe_link_components(card, rtd, order); //probe 该声卡dai_link上的所有components组件soc_probe_link_dais(card, rtd, order); //probe 该声卡所有dai_link->//建立playback stream/capture stream,相应的substream也同时建立,ops绑定snd_pcm_dev_register,回调函数会在声卡的 注册阶段被调用soc_new_pcm(rtd, num); // 设置操作该pcm的控制/操作接口函数snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);snd_card_register(card->snd_card);   //注册声卡,遍历声卡下的所有逻辑设备,调用各设备的注册回调函数,对于pcm,就是上面提到的snd_pcm_dev_register函数,该回调函数建立了和用户空间应用程序(alsa-lib)通信所用的设备文件节点:/dev/snd/pcmCxxDxxp 和 /dev/snd/pcmCxxDxxc

五、ALSA-Probe的流程

下图是板子内核启动到probe完成阶段的流程图,可以看到ALSA初始化音频的整个流程是怎么初始化声卡,和配置一些基本参数的

六、Tinyalsa pcm_open的调用流程

先看下pcm的fops,接口如下,定义在pcm_native.c

const struct file_operations snd_pcm_f_ops[2] = {{.owner =       THIS_MODULE,.write =       snd_pcm_write,.write_iter =        snd_pcm_writev,.open =         snd_pcm_playback_open,.release =       snd_pcm_release,.llseek =      no_llseek,.poll =          snd_pcm_playback_poll,.unlocked_ioctl =    snd_pcm_playback_ioctl,.compat_ioctl =     snd_pcm_ioctl_compat,.mmap =           snd_pcm_mmap,.fasync =     snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,},{.owner =      THIS_MODULE,.read =            snd_pcm_read,.read_iter =      snd_pcm_readv,.open =          snd_pcm_capture_open,.release =        snd_pcm_release,.llseek =      no_llseek,.poll =          snd_pcm_capture_poll,.unlocked_ioctl = snd_pcm_capture_ioctl,.compat_ioctl =  snd_pcm_ioctl_compat,.mmap =           snd_pcm_mmap,.fasync =     snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,}
};

substream是pcm ops的下一层,绝大部分任务都是在substream中处理,substream的ops定义在soc-pcm.c,定义如下

int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{.../* ASoC PCM operations */if (rtd->dai_link->dynamic) {rtd->ops.open        = dpcm_fe_dai_open;rtd->ops.hw_params   = dpcm_fe_dai_hw_params;rtd->ops.prepare    = dpcm_fe_dai_prepare;rtd->ops.trigger  = dpcm_fe_dai_trigger;rtd->ops.hw_free  = dpcm_fe_dai_hw_free;rtd->ops.close        = dpcm_fe_dai_close;rtd->ops.pointer    = soc_pcm_pointer;rtd->ops.ioctl        = soc_pcm_ioctl;} else {rtd->ops.open       = soc_pcm_open;rtd->ops.hw_params   = soc_pcm_hw_params;rtd->ops.prepare    = soc_pcm_prepare;rtd->ops.trigger  = soc_pcm_trigger;rtd->ops.hw_free  = soc_pcm_hw_free;rtd->ops.close        = soc_pcm_close;rtd->ops.pointer    = soc_pcm_pointer;rtd->ops.ioctl        = soc_pcm_ioctl;}...if (playback)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);if (capture)snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);...
}

下图使用tinyalsa测试,可以看到alsa-driver在pcm_open阶段做了哪些工作,流程是什么

七、Tinyalsa pcm_write的调用流程

下图使用tinyalsa测试,可以看到alsa-driver在pcm_write阶段做了哪些工作,流程是什么

至此整个ALSA-DRIVER框架的内容就分析完了,可能有一些细节的东西内容太多导致没法去描述,有需求的话,可以自行根据需求分析

ALSA驱动框架分析相关推荐

  1. Linux ALSA音频框架分析五:HDA Driver分析

    Linux ALSA音频框架分析五:HDA Driver分析 一 概述 HDA(High Definition Audio)是intel设计的用来取代AC97的音频标准,硬件架构上由hda dodec ...

  2. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  3. framebuffer驱动详解2——framebuffer驱动框架分析

    以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 一.framebuffer驱动框架总览 1.驱动框架部分 (1)drivers/video/fbmem.c(主要的文件) 创建graphic ...

  4. LCD 设备驱动框架分析及核心结构

    Linux 下很多东西都是和结构体相关,举个例子,时钟大家都知道吧,Linux 下对应时钟的东西就有好几个结构体,所以你要是想明白Linux 下那些东西,对结构体要有所了解,LCD 是基础的驱动设备, ...

  5. Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)

    <DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...

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

    (1)ALSA简介 (1) Native ALSA Application:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音.录音.控制 ALSA ...

  7. Linux PCIe驱动框架分析(第二章)

    目录 项目背景 1. 概述 2. 数据结构 3. 流程分析 3.1 设备驱动模型 3.2 初始化 3.2.1 pci_bus_match 3.2.2 pci_device_probe 3.3 枚举 项 ...

  8. I2C驱动框架分析(3):DW_I2C驱动分析

    I2C驱动框架分析(1):I2C重要概念与数据结构 I2C驱动框架分析(2):I2C框架源码分析 I2C驱动框架分析(3):DW_I2C驱动分析 第三章:DW_I2C驱动 其驱动文件在drivers/ ...

  9. linux内核usb驱动框架,基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析

    基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析 发布时间:2014-07-18 16:47:31来源:红联作者:linux08071151 driver/usb/host/ ...

最新文章

  1. pycharm 添加数据库驱动
  2. python数据库编程(1):潦草的开篇
  3. spring boot使用外置tomcat部署需要排除的依赖
  4. 电脑键盘中英文按键有哪些?有什么作用?
  5. 企业级低代码平台,Jeecgboot-Vue3 v1.0.0 版本正式发布
  6. java 建立缓存_java动态缓存——创建一个简单的缓存
  7. C#中 JSON 序列化 与 反序列化
  8. CentOS 7.6 配置VNC远程访问服务器图形界面
  9. 基于SVP的格密码计算复杂性问题
  10. 多通道ECG心率监测系统
  11. 计算机显示正常 但打印出来乱码,打印出乱码字体怎么办
  12. php 抽奖系统源码下载,魔众砸金蛋抽奖系统PHP源码 v2.0.0
  13. win7系统创建打印服务器,图文分享win7下添加打印服务器端口
  14. U3D性能优化之MeshBaker(带光照)
  15. python编写模拟硬币投掷_python实现简单随机模拟——抛呀抛硬币
  16. Lucene.Net的简单练习
  17. 【openh264】libfreerdp 编解码 CQP VBR
  18. win11账户锁定了怎么解除?
  19. BCG与阿里研究院等联合揭秘中国互联网经济:成功的关键是什么?
  20. 中科图新地图软件(LocaSpaceViewer)的各种卫片的偏移对比

热门文章

  1. java 网站服务器ip地址,java服务器ip地址
  2. 什么软件专业测试电脑,测验电脑性能 用什么软件
  3. 计算机网络知识汇总(超详细)
  4. DEDECMS后台Sitemap模板
  5. windows优化大师怎么用_手机内存不够?最好用的存储空间清理工具分享给你
  6. 双系统如何远程切换设置
  7. Unity 3D追踪效果的实现 目标箭头指引
  8. 热备双机中备机处理业务异常故障引发的思考
  9. PSP 视频缩略图 一点就生成 -- PSP Falcon
  10. Android-WIFI笔记整理(二)