在前面两篇文章中,我们分别讲了嵌入式Linux系统声卡注册的过程和调用的过程:

https://blog.csdn.net/qq_37659294/article/details/104748747

https://blog.csdn.net/qq_37659294/article/details/104802868

讲了那么多,我们最终的目的无非就是想写一个声卡驱动,然后给上层的APP使用而已,在之前的文章中可以看出内核里面关于声卡这部分是非常复杂的,但实际上我们写驱动的时候,只需要实现和硬件相关的那几个结构体,如cpu_dai等。然后借助内核的ASOC框架把我们的驱动注册进去而已。下面是我们编写的驱动:

一、machine部分:

①我们构造一个snd_soc_card结构体myalsa_card,它的dai_link指定了要使用那些cpu_dai、codec_dai...然后我们模仿内核自带的声卡驱动,调用platform_set_drvdata把myalsa_card保存在platform_device中(《ASOC注册过程》machine部分的第①点有介绍)。

②构造一个名为"soc-audio"的平台设备,然后注册它,因为内核中有同名的平台驱动,所以调用了相应的probe函数,即我们在《ASOC注册过程》machine部分的第①点有介绍到的soc_probe函数。这个函数就会根据我们构造的myalsa_card结构体里的信息,完成我们声卡的所有注册任务。

至此,我们的machine部分的驱动就编写完成了。

#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/module.h>#include <sound/soc.h>/* 参考sound\soc\samsung\s3c24xx_uda134x.c*//** 1. 分配注册一个名为soc-audio的平台设备* 2. 这个平台设备有一个私有数据 snd_soc_card*    snd_soc_card里有一项snd_soc_dai_link*    snd_soc_dai_link被用来决定ASOC各部分的驱动*/static struct snd_soc_ops s3c2440_uda1341_ops = {//.hw_params = s3c24xx_uda134x_hw_params,
};static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {.name = "100ask_UDA1341",.stream_name = "100ask_UDA1341",.codec_name = "uda1341-codec",.codec_dai_name = "uda1341-iis",.cpu_dai_name = "s3c2440-iis",.ops = &s3c2440_uda1341_ops,.platform_name   = "s3c2440-dma",
};static struct snd_soc_card myalsa_card = {.name = "S3C2440_UDA1341",.owner = THIS_MODULE,.dai_link = &s3c2440_uda1341_dai_link,.num_links = 1,
};static void asoc_release(struct device * dev)
{
}static struct platform_device asoc_dev = {.name         = "soc-audio",.id       = -1,.dev = { .release = asoc_release, },
};static int s3c2440_uda1341_init(void)
{platform_set_drvdata(&asoc_dev, &myalsa_card);platform_device_register(&asoc_dev);    return 0;
}static void s3c2440_uda1341_exit(void)
{platform_device_unregister(&asoc_dev);
}module_init(s3c2440_uda1341_init);
module_exit(s3c2440_uda1341_exit);MODULE_LICENSE("GPL");

二、platform部分

1.cpu_dai

构造一个snd_soc_dai_driver结构体变量s3c2440_i2s_dai,s3c2440_i2s_dai里面的函数是我们需要自己实现的关于硬件的操作函数

static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct snd_soc_dai *dai)
{/* 根据params设置IIS控制器 *//* 配置GPIO用于IIS */...return 0;
}static int s3c2440_i2s_trigger(struct snd_pcm_substream *substream, int cmd,struct snd_soc_dai *dai)
{  /* 硬件相关的操作 */
}static const struct snd_soc_dai_ops s3c2440_i2s_dai_ops = {.hw_params = s3c2440_i2s_hw_params,.trigger   = s3c2440_i2s_trigger,
};#define S3C24XX_I2S_RATES \(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)static struct snd_soc_dai_driver s3c2440_i2s_dai = {.playback = {.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.capture = {.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.ops = &s3c2440_i2s_dai_ops,
};

分别构造一个平台设备和平台驱动它们的名字必须相同,且必须和machine部分的dai_lnk指定的名字相同),然后就调用它们的probe函数,

static struct platform_device s3c2440_iis_dev = {.name         = "s3c2440-iis",.id       = -1,.dev = { .release = s3c2440_iis_release, },
};
struct platform_driver s3c2440_iis_drv = {.probe       = s3c2440_iis_probe,.remove        = s3c2440_iis_remove,.driver       = {.name   = "s3c2440-iis",}
};

③在这个probe函数里调用snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);将第①步构造的结构体变量放入链表dai_list,并把它命名为第②步提到的平台设备的名字。machine部分就是根据dai_link指定的名字,在dai_list中把s3c2440_i2s_dai找出来。

static int s3c2440_iis_probe(struct platform_device *pdev)
{return snd_soc_register_dai(&pdev->dev, &s3c2440_i2s_dai);
}
static int s3c2440_iis_remove(struct platform_device *pdev)
{snd_soc_unregister_dai(&pdev->dev);return 0;
}

至此,cpu_dai部分的驱动框架编写也已经完成,具体的硬件操作函数需要根据不同的硬件来编写。(dma、codec_dai和这个类似,下面将不再赘述,直接贴出代码示意)

2.dma

/* 参考 sound\soc\samsung\dma.c*/static struct snd_pcm_ops s3c2440_dma_ops = {/* 需要我们自己编写的,关于硬件的函数 */.open     = s3c2440_dma_open,.close      = s3c2440_dma_close,.ioctl     = snd_pcm_lib_ioctl,.hw_params = s3c2440_dma_hw_params,.prepare    = s3c2440_dma_prepare,.trigger    = s3c2440_dma_trigger,.pointer = s3c2440_dma_pointer,
};static struct snd_soc_platform_driver s3c2440_dma_platform = {/* 需要我们自己编写的,关于硬件的函数 */.ops     = &s3c2440_dma_ops,.pcm_new    = dma_new,.pcm_free    = dma_free_dma_buffers,
};static int s3c2440_dma_probe(struct platform_device *pdev)
{return snd_soc_register_platform(&pdev->dev, &s3c2440_dma_platform);
}
static int s3c2440_dma_remove(struct platform_device *pdev)
{return snd_soc_unregister_platform(&pdev->dev);
}static void s3c2440_dma_release(struct device * dev)
{
}static struct platform_device s3c2440_dma_dev = {.name         = "s3c2440-dma",.id       = -1,.dev = { .release = s3c2440_dma_release, },
};
struct platform_driver s3c2440_dma_drv = {.probe       = s3c2440_dma_probe,.remove        = s3c2440_dma_remove,.driver       = {.name   = "s3c2440-dma",    //必须和dai_link里面的platform_name相同}
};static int s3c2440_dma_init(void)
{platform_device_register(&s3c2440_dma_dev);platform_driver_register(&s3c2440_dma_drv);return 0;
}static void s3c2440_dma_exit(void)
{platform_device_unregister(&s3c2440_dma_dev);platform_driver_unregister(&s3c2440_dma_drv);
}module_init(s3c2440_dma_init);
module_exit(s3c2440_dma_exit);

三、codec部分


/* 参考 sound\soc\codecs\uda134x.c*//* 1. 构造一个snd_soc_dai_driver* 2. 构造一个snd_soc_codec_driver* 3. 注册它们*/static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {/* 硬件相关的函数 */.probe = uda1341_soc_probe,.reg_cache_size = sizeof(uda1341_reg),.reg_word_size = sizeof(u8),.reg_cache_default = uda1341_reg,.reg_cache_step = 1,.read  = uda1341_read_reg_cache,.write = uda1341_write_reg,  /* 写寄存器 */
};static const struct snd_soc_dai_ops uda1341_dai_ops = {/* 硬件相关的操作 */.hw_params   = uda1341_hw_params,
};static struct snd_soc_dai_driver uda1341_dai = {.name = "uda1341-iis",        //必须和dai_link里面的codec_dai_name相同/* playback capabilities */.playback = {.stream_name = "Playback",.channels_min = 1,.channels_max = 2,.rates = UDA134X_RATES,.formats = UDA134X_FORMATS,},/* capture capabilities */.capture = {.stream_name = "Capture",.channels_min = 1,.channels_max = 2,.rates = UDA134X_RATES,.formats = UDA134X_FORMATS,},/* pcm operations */.ops = &uda1341_dai_ops,
};/* 通过注册平台设备、平台驱动来实现对snd_soc_register_codec的调用**/static void uda1341_dev_release(struct device * dev)
{
}static int uda1341_probe(struct platform_device *pdev)
{return snd_soc_register_codec(&pdev->dev,&soc_codec_dev_uda1341, &uda1341_dai, 1);
}static int uda1341_remove(struct platform_device *pdev)
{return snd_soc_unregister_codec(&pdev->dev);
}static struct platform_device uda1341_dev = {.name         = "uda1341-codec",        //必须和dai_link里面的codec_name相同.id       = -1,.dev = { .release = uda1341_dev_release, },
};
struct platform_driver uda1341_drv = {.probe       = uda1341_probe,.remove        = uda1341_remove,.driver       = {.name   = "uda1341-codec",}
};static int uda1341_init(void)
{platform_device_register(&uda1341_dev);platform_driver_register(&uda1341_drv);return 0;
}static void uda1341_exit(void)
{platform_device_unregister(&uda1341_dev);platform_driver_unregister(&uda1341_drv);
}module_init(uda1341_init);
module_exit(uda1341_exit);

编写声卡驱动(框架)相关推荐

  1. ASOC声卡驱动框架

    硬件平台mini2440开发板(S3C2440+UDA1341) linux版本:linux-3.4.99 ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好 ...

  2. linux声卡驱动框架-ALSA简介

    ALSA(即Advanced Linux Sound Architecture), 是目前Linux的主流音频体系结构, 提供了音频和MIDI的支持, 其架构图如下所示 在内核设备驱动层,ALSA提供 ...

  3. linux驱动编写(声卡驱动之asoc移植)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] Linux下面的声卡驱动很复杂,根本不是一篇博客能够说清楚的.所以,本片文章的目的就是让同学门快 ...

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

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

  5. 基于驱动框架编写驱动代码

    前言:基于上一个博文对 linux驱动的认知的了解,那么现在开始我们正式来进入学习基于驱动框架来编写驱动代码,那么接下来我们先来看看一个最简单的驱动框架代码 一.字符设备驱动框架代码与应用层代码 驱动 ...

  6. ALSA声卡笔记2---ASoC驱动框架

    1.简单了解一下ASOC 在嵌入式系统里面的声卡驱动为ASOC(ALSA System on Chip) ,它是在ALSA 驱动程序上封装的一层   分为3大部分,Machine,Platform和C ...

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

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

  8. 08.音频系统:第003课_Linux音频驱动程序:第001节_alsa音频驱动框架

    在上小节我们分析了Adndroid系统音频的框架,这么一个复杂的系统我们怎么去学习呢?我们从下往上学,先分析音频的驱动程序,看看linux系统中驱动程序是怎么编写的,他的结构是怎么样的,然后在琢磨Ti ...

  9. 嵌入式linux ASoC架构声卡驱动开发

    嵌入式linux ASoC架构声卡驱动开发 文章目录 嵌入式linux ASoC架构声卡驱动开发 需求分析 ASoC架构下声卡驱动代码结构 codec驱动 snd_soc_register_codec ...

最新文章

  1. 股市币市:数据分析与交易所最新公告(20190228)
  2. SpringBoot自定义参数验证器
  3. 在C语言中,存在的内存的连续性的声明
  4. 3.6 判断两个链表是否相交
  5. 永洪Desktop安装后启动提示缺少libv8_libbase.dll文件解决办法
  6. 手机被锁在耳机模式了
  7. String | 263. Ugly Number
  8. htc one m7 linux驱动,HTC One M7官方RUU固件包(可救砖)
  9. linux的系统移植——交叉编译工具集
  10. 简单配置nginx使之支持pathinfo
  11. PHP-php://(类型)访问各个输入/输出流以及全局变量$HTTP_RAW_POST_DATA讲解
  12. sap gui java_不喜欢SAP GUI?那试试用Eclipse进行ABAP开发吧
  13. linux 内核编程视频
  14. Netmeeting使用方法
  15. 夏书祥-淘宝考试最新答案
  16. ArcGIS Server 发布地图服务遇到的问题
  17. android 获取视频码率和缓存大小,android - 使用MediaCodec和MediaMuxer录制视频,但比特率和帧率不正确 - 堆栈内存溢出...
  18. unbalanced calls to begin/end appearance transitions for uiviewcontroller的解决方法
  19. [JZOJ4949]仙人球
  20. android 电子签名设备,Android 电子签名制作

热门文章

  1. 实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2
  2. .net 笔记尝试(二)
  3. 调整Tomcat上的参数提高性能[转]
  4. html网页加入一个按钮,在html网页设计中,一个“登录”按钮怎么编写代码?
  5. 教师计算机网络培训工作总结,教师培训工作的自我总结
  6. php 出错处理,PHP 错误处理机制
  7. python将json转换为excel_使用python将Excel转换为JSON_python_酷徒编程知识库
  8. java 布隆过滤器_什么是布隆过滤器(Bloom Filter)?
  9. centos 获取硬件序列号_如何在 Linux 上查找硬件规格
  10. 牛客网与leetcode刷题(高频题中简单or中等的)