编写声卡驱动(框架)

在前面两篇文章中,我们分别讲了嵌入式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);

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

声卡学习笔记

分享几篇关于韦东山声卡驱动的学习笔记&#xff0c;作者写得非常详细。 ALSA驱动框架&#xff1a;https://blog.csdn.net/qingkongyeyue/article/details/52328991 ASoC驱动框架&#xff1a;https://blog.csdn.net/qingkongyeyue/article/details/52349120 ASoC驱动重要结构…

路由器、交换机、集线器的区别

https://blog.csdn.net/weibo1230123/article/details/82779040

$PATH环境变量的作用

echo $PATH 显示当前PATH环境变量&#xff0c;该变量的值由一系列以冒号分隔的目录名组成&#xff0c;如&#xff1a;/usr/local/bin:/bin:/usr/bin。(冒号:是路径分隔符) 在执行一个程序的时候如果没有PATH的话&#xff0c;就需要写出路径名&#xff08;绝对或者相对&#xf…

dmesg

https://blog.csdn.net/zm_21/article/details/31760569

进程上下文与中断上下文的理解

一.什么是内核态和用户态 内核态&#xff1a;在内核空间执行&#xff0c;通常是驱动程序&#xff0c;中断相关程序&#xff0c;内核调度程序&#xff0c;内存管理及其操作程序。 用户态&#xff1a;用户程序运行空间。 二.什么是进程上下文与中断上下文 1.进程上下文&#xf…

GDB调试教程:1小时玩转Linux gdb命令

原文链接&#xff1a;http://c.biancheng.net/gdb/ GDB 入门教程 本教程以下面的代码为例&#xff0c;在 Linux 系统下来讲解 GBD 的调试流程&#xff1a; int main (void) {unsigned long long int n, sum;n 1;sum 0;while (n < 100){sum sum n;n n 1;}return 0; …

shell将命令执行的结果赋值给 变量

https://blog.csdn.net/lemontree1945/article/details/79126819/

Linux下shell脚本指定程序运行时长

https://www.cnblogs.com/yychuyu/p/12626798.html

vim编辑器如何删除一行或者多行内容

http://blog.itpub.net/69955379/viewspace-2681334/

C++经典问题:如果对象A中有对象成员B,对象B没有默认构造函数,那么对象A必须在初始化列表中初始化对象B?

对象成员特点总结&#xff1a; &#xff08;1&#xff09;实例化对象A时&#xff0c;如果对象A有对象成员B,那么先执行对象B的构造函数&#xff0c;再执行A的构造函数。 &#xff08;2&#xff09;如果对象A中有对象成员B,那么销毁对象A时&#xff0c;先执行对象A的析构函数&…

JZ2440用U-Boot给Nand-Flash烧写程序时报错:NAND write: incorrect device type in bootloader ‘bootloader‘ is not

JZ2440开发板使用问题&#xff0c;U-Boot烧写程序到Nand Flash时报错&#xff1a;NAND write: incorrect device type in bootloader bootloader is not a number 这是因为分区名中u-boot&#xff0c;不是bootloader&#xff0c;而cmd_menu.c里用的是bootloader 可以执行&#…

韦东山衔接班——4.4_构建根文件系统之构建根文件系统

文章地址&#xff1a; https://blog.csdn.net/gongweidi/article/details/100086289?biz_id102&utm_term%E9%9F%A6%E4%B8%9C%E5%B1%B1%E8%A1%94%E6%8E%A5%E7%8F%AD&utm_mediumdistribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-100086289&…

C++中const char *p和char const *p

const char *p;他的意思是p指向的目标空间的内容不可变化 例如定义char cA; p&c;则c的内容不可以变化.如cB;等一些企图改变变量c的值的做法都不行. 然而p仍然是动态的,就是它还可以指向别的空间,被赋予新的地址值,只是被他指向的目标空间的内容不可变化,如上面的c值始终为A…

qt 分割字符串的两种方法

https://blog.csdn.net/a724699769/article/details/62216435

【YOLO系列】YOLOv3代码详解(五):utils.py脚本

前言 以下内容仅为个人在学习人工智能中所记录的笔记&#xff0c;先将目标识别算法yolo系列的整理出来分享给大家&#xff0c;供大家学习参考。 本文仅对YOLOV3代码中关键部分进行了注释&#xff0c;未掌握基础代码的铁汁可以自己百度一下。 若文中内容有误&#xff0c;希望大家…

内核的Makefile与Kconfig关系解析

在子目录下的Kconfig里添加make menuconfig的选项&#xff08;如图一&#xff09;&#xff0c;并默认设置为y&#xff0c;make menuconfig的菜单里就会有该项并默认为选上状态&#xff0c;make menuconfig配置完之后在.config文件里就有该选项&#xff0c;并等于y&#xff08;如…

C语言extern的用法

在x.c文件里定义如&#xff1a;int x 100; 在x.h文件里声明如&#xff1a;extern int x; 然后在main.c里 #include "x.h"即可 或者直接在main.c里 extern int x; 而不使用#include "x.h"&#xff08;此时x.h里当然也不用extern int x;&#xff09;也…

C语言中.和->区别

结构体变量用 . 运算符来访问结构体的成员 struct A { int a; int b; };A object; object.a 1;指向结构体的指针用->来访问其指向的结构体的成员 A *point malloc(sizeof(struct A)); point->a 1;

Qt中定时器使用的两种方法

https://blog.csdn.net/qq_28877125/article/details/88389559

【深度】韦东山:一文看看尽linux对中断处理的前世今生

https://blog.csdn.net/thisway_diy/article/details/104848034