文章目录

  • 一,前言
  • 二,框架
  • 三,OSS实现
    • 3.1 OSS初始化
    • 3.2 向OSS注册音频设备
    • 3.3 OSS管理音频设备
  • 四,音频基本概念
    • 4.1 采样频率
    • 4.2 采样精度
    • 4.3 左声道/右声道
    • 4.4 IIS接口
    • 4.5 声音录制和播放
    • 4.6 控制接口
  • 五,实现WM8976G的音频设备驱动
    • 5.1 硬件电路
      • 5.1.1 WM8976G相关
      • 5.1.2 S3C2440相关
    • 5.2 构建驱动
      • 5.2.1 注册平台设备
      • 5.2.2 注册平台驱动
      • 5.2.3 probe函数分析
        • 5.2.3.1 初始化硬件资源
        • 5.2.3.2 初始化并设置音频编解码芯片
        • 5.2.3.3 初始化音频输入和输出的DMA通道
        • 5.2.3.4 向OSS核心层注册音频设备
  • 六,应用程序播放和录制

一,前言

OSS(Open Sound System(开放声音系统)),是unix平台上一个统一的音频接口。ALSA (Advanced Linux Sound Architecture(高级Linux声音体系)) 是为声卡提供驱动的Linux内核组件,替代OSS。在这里先来分析简单的OSS,后面有时间再研究下ALSA。分析OSS的基本框架,最后通过一个例子总结下OSS框架下实现一个音频设备驱动的一般步骤。

二,框架

内核目录下sound/sound_core.c文件是OSS的核心层,其向上提供了应用程序访问音频设备统一的入口,向下为不同音频设备提供了向上的注册接口。音频驱动开发针对特定的音频设备和系统硬件资源使用总线-设备-驱动模型构建音频设备驱动,在驱动probe中调用OSS提供的接口注册音频设备,并由OSS进行管理。OSS架构是基于文件系统的访问方式,对声音的操作通过对设备节点文件进行open、read、write等操作完成。

三,OSS实现

3.1 OSS初始化

在内核中配置,将sound_core.c编译进内核,内核初始化时加载,调用init_soundcore。
make menuconfig
-> Device Drivers -> Sound
-> <*> Sound card support

// linux-2.6.22.6/include/linux/major.h
#define SOUND_MAJOR     14
// linux-2.6.22.6/sound/sound_core.c
static int __init init_soundcore(void)
{// 向内核注册一个字符设备 if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {printk(KERN_ERR "soundcore: sound device already in use.\n");return -EBUSY;}// 创建一个设备类。/sys/class/soundsound_class = class_create(THIS_MODULE, "sound");if (IS_ERR(sound_class))return PTR_ERR(sound_class);return 0;
}

3.2 向OSS注册音频设备

OSS标准中有2个最基本的音频设备:mixer(混音器)和DSP(数字信号处理器),mixer的作用将多个信号组合或者叠加到一起,可用来调整音量大小和选择音源。对于不同声卡来说,其混音器的作用可能各不相同。向OSS核心层注册音频接口时创建的/dev/mixer设备节点文件是应用程序对mixer进行操作的软件接口。DSP也称编解码器,主要实现录音和放音的操作,其对应的设备节点文件是/dev/dsp,向该设备节点文件写数据即意味着激活声卡上的D/A 转换器进行放音,而向该设备读数据则意味着激活声卡上的A/D 转换器进行录音。
除了mixer和dsp音频设备外,还有多个不同的音频设备。OSS针对这些设备各下层提供了不同的注册接口。

int register_sound_mixer(const struct file_operations *fops, int dev);
int register_sound_midi(const struct file_operations *fops, int dev);
int register_sound_dsp(const struct file_operations *fops, int dev);
int register_sound_special(const struct file_operations *fops, int unit);

这些接口的本质都是调用sound_insert_unit函数向OSS管理的不同设备链表中插入音频设备,并为不同的音频设备创建不同的音频设备设备节点。参数fops是各种音频设备的操作函数,dev一般传入-1,自动分配设备号。

3.3 OSS管理音频设备

OSS通过一个指针数组管理多个不同类型的音频设备链表。驱动层下OSS注册音频设备时,即向对应的链表中插入一个sound_unit结构。

/**  Allocations**   0   *16     Mixers* 1   *8      Sequencers* 2   *16     Midi*   3   *16     DSP*    4   *16     SunDSP* 5   *16     DSP16*  6   --      sndstat (obsolete)* 7   *16     unused* 8   --      alternate sequencer (see above)*    9   *16     raw synthesizer access* 10  *16     unused* 11  *16     unused* 12  *16     unused* 13  *16     unused* 14  *16     unused* 15  *16     unused*/
static struct sound_unit *chains[SOUND_STEP];

比如注册一个dsp音频设备

register_sound_dsp(&smdk2410_audio_fops, -1) -> sound_insert_unit(&chains[3], fops, dev, 3, 131,"dsp", S_IWUSR | S_IRUSR, NULL) -> __sound_insert_unit(s, list, fops, index, low, top) -> s->unit_minor=n;s->unit_fops=fops;s->next=*list;*list=s;// 以14为主设备号,以s->unit_minor为次设备号创建音频设备,创建设备节点文件device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),s->name+6);

上面提到OSS初始化时,向内核注册了一个字符设备,其操作函数为soundcore_fops。这里面只有一个open接口,其只是作为一个中转,应用程序访问某个音频设备(open)会根据打开的音频设备的设备节点文件的次设备号(也就是创建时使用的s->unit_minor),从音频设备链表数组(chains)中找到对应类型的音频设备链表从而找到对应的操作函数。

static const struct file_operations soundcore_fops=
{/* We must have an owner or the module locking fails */.owner  = THIS_MODULE,.open    = soundcore_open,
};int soundcore_open(struct inode *inode, struct file *file)
{int chain;int unit = iminor(inode);struct sound_unit *s;const struct file_operations *new_fops = NULL;chain=unit&0x0F;if(chain==4 || chain==5)  /* dsp/audio/dsp16 */{unit&=0xF0;unit|=3;chain=3;}spin_lock(&sound_loader_lock);s = __look_for_unit(chain, unit);if (s)new_fops = fops_get(s->unit_fops);if (!new_fops) {spin_unlock(&sound_loader_lock);request_module("sound-slot-%i", unit>>4);request_module("sound-service-%i-%i", unit>>4, chain);spin_lock(&sound_loader_lock);s = __look_for_unit(chain, unit);if (s)new_fops = fops_get(s->unit_fops);}if (new_fops) {int err = 0;const struct file_operations *old_fops = file->f_op;file->f_op = new_fops;spin_unlock(&sound_loader_lock);if(file->f_op->open)err = file->f_op->open(inode,file);if (err) {fops_put(file->f_op);file->f_op = fops_get(old_fops);}fops_put(old_fops);return err;}spin_unlock(&sound_loader_lock);return -ENODEV;
}

四,音频基本概念

4.1 采样频率

对一个声音波形记录一次声音数据的频率。频率越大,对记录的声音数据还原播放出来的声音就越真实。对于入耳采样频率不需要太大,采样率在 8KHz - 96KHz之间就行,到 96KHz 声音已经很饱满了。

4.2 采样精度

一个声音数据用多少位表示。比如说8位精度,即一个声音数据用8位也就是一个字节表示;8位精度,即一个声音数据用16位也就是两个字节表示。

4.3 左声道/右声道

左右声道数据是不同的,两种数据配合还原播放出来的声音更加立体。

4.4 IIS接口

用来传输声音数据的接口,由四条线构成,I2SSCLK – 数据位时钟 , I2SLRCK – 左右声道数据采样时钟 , I2SSDI – 声音数据输入 , I2SSDO – 声音数据输出。

4.5 声音录制和播放

4.6 控制接口

IIS接口用来传输音频数据,控制接口用来传输主控对编解码芯片的控制数据,比如设置声音音量、切换声道输出、设置MIC增益等。也就通过GPIO来对编解码芯片的某些寄存器进行数据读写操作。不同的编解码芯片使用不同的传输模式,比如WM8976G提供两种传输模式,一种是三线模式,一种是IIC模式。

五,实现WM8976G的音频设备驱动

5.1 硬件电路

5.1.1 WM8976G相关

L2/GPIO2:单声道音频输入或第二个麦克风接入或者做普通GPIO口
LRC:左右声道数据采样时钟
BCLK:声音数据位时钟
ADCDAT:IIS数据输入
DACDAT:IIS数据输出
MCLK:WM8976工作时钟,由主控提供
MICBIAS:麦克风偏置电压,通过调节该偏置电压可以改善麦克风录制失真问题
LIP:麦克风输入通道
LIN:麦克风输入通道(接地)
AUXL:音频输入左声道(外接音频)
AUXR:音频输入右声道(外接音频)
CSB/GPIO1:控制接口使用3线模式时的片选脚或者做普通GPIO口
SCLK:三线模式时钟或者IIC模式时钟
SDIN:三线模式数据或者IIC模式数据
MODE:控制接口使用三线模式或IIC模式选择脚,高电平时使用三线模式
VMID:参考电压,通过调节参考电压可以改善声音播放时的噪音问题
ROUT1:音频输出通道1,右声道,可外接耳机或喇叭
LOUT1:音频输出通道1,左声道,可外接耳机或喇叭
ROUT2:音频输出通道2,右声道,可外接耳机或喇叭
LOUT2:音频输出通道2,左声道,可外接耳机或喇叭
OUT3:音频输出通道3
OUT4:音频输出通道4

5.1.2 S3C2440相关


5.2 构建驱动

通过总线-设备-驱动模型来构建音频设备驱动,在驱动的probe中初始化硬件资源、向OSS核心层注册dsp音频设备和mixer音频设备。

5.2.1 注册平台设备

系统启动初始化时,注册s3c_device_iis平台设备。加载注册过程在前面的文章中已经分析过多次,这里不再赘述。

// linux-2.6.22.6/arch/arm/plat-s3c24xx/devs.c
static struct resource s3c_iis_resource[] = {[0] = {.start = S3C24XX_PA_IIS,.end   = S3C24XX_PA_IIS + S3C24XX_SZ_IIS -1,.flags = IORESOURCE_MEM,}
};static u64 s3c_device_iis_dmamask = 0xffffffffUL;struct platform_device s3c_device_iis = {.name       = "s3c2410-iis",.id          = -1,.num_resources    = ARRAY_SIZE(s3c_iis_resource),.resource   = s3c_iis_resource,.dev              = {.dma_mask = &s3c_device_iis_dmamask,.coherent_dma_mask = 0xffffffffUL}
};// linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c
static struct platform_device *smdk2440_devices[] __initdata = {&s3c_device_usb,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c,&s3c_device_iis,&s3c2440_device_sdi,
};static void __init smdk2440_machine_init(void)
{s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));smdk_machine_init();
}MACHINE_START(S3C2440, "SMDK2440")/* Maintainer: Ben Dooks <ben@fluff.org> */.phys_io = S3C2410_PA_UART,.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,.boot_params   = S3C2410_SDRAM_PA + 0x100,.init_irq  = s3c24xx_init_irq,.map_io     = smdk2440_map_io,.init_machine    = smdk2440_machine_init,.timer     = &s3c24xx_timer,
MACHINE_END

5.2.2 注册平台驱动

// linux-2.6.22.6/sound/soc/s3c24xx/s3c2440-wm8976.c
extern struct bus_type platform_bus_type;
static struct device_driver s3c2410iis_driver = {.name = "s3c2410-iis",.bus = &platform_bus_type,.probe = s3c2410iis_probe,.remove = s3c2410iis_remove,
};static int __init s3c2410_uda1341_init(void) {memzero(&input_stream, sizeof(audio_stream_t));memzero(&output_stream, sizeof(audio_stream_t));return driver_register(&s3c2410iis_driver);
}

5.2.3 probe函数分析

总线-设备-驱动模型的设备和驱动的匹配过程,以及匹配成功后调用驱动的probe函数的过程在之前的文章中已经分析过多次,这里不再赘述。直接看驱动的probe函数,即s3c2410iis_probe。

5.2.3.1 初始化硬件资源

......
iis_base = (void *)S3C24XX_VA_IIS ;// 使能IIS模块
iis_clock = clk_get(dev, "iis");
clk_enable(iis_clock);// 设置相关引脚功能
/* GPB 4: L3CLOCK, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);
s3c2410_gpio_pullup(S3C2410_GPB4,1);
/* GPB 3: L3DATA, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP);
/* GPB 2: L3MODE, OUTPUT */
s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP);
s3c2410_gpio_pullup(S3C2410_GPB2,1);
/* GPE 3: I2SSDI */
s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI);
s3c2410_gpio_pullup(S3C2410_GPE3,0);
/* GPE 0: I2SLRCK */
s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);
s3c2410_gpio_pullup(S3C2410_GPE0,0);
/* GPE 1: I2SSCLK */
s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);
s3c2410_gpio_pullup(S3C2410_GPE1,0);
/* GPE 2: CDCLK */
s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);
s3c2410_gpio_pullup(S3C2410_GPE2,0);
/* GPE 4: I2SSDO */
s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO);
s3c2410_gpio_pullup(S3C2410_GPE4,0);// 初始化IIS模块
init_s3c2410_iis_bus();......

5.2.3.2 初始化并设置音频编解码芯片

.......
init_wm8976();
.......static void init_wm8976(void)
{uda1341_volume = 57;uda1341_boost = 0;/* software reset */wm8976_write_reg(0, 0);/* OUT2的左/右声道打开* 左/右通道输出混音打开* 左/右DAC打开*/wm8976_write_reg(0x3, 0x6f);wm8976_write_reg(0x1, 0x1f);//biasen,BUFIOEN.VMIDSEL=11b  wm8976_write_reg(0x2, 0x185);//ROUT1EN LOUT1EN, inpu PGA enable ,ADC enablewm8976_write_reg(0x6, 0x0);//SYSCLK=MCLK  wm8976_write_reg(0x4, 0x10);//16bit        wm8976_write_reg(0x2B,0x10);//BTL OUTPUT  wm8976_write_reg(0x9, 0x50);//Jack detect enable  wm8976_write_reg(0xD, 0x21);//Jack detect  wm8976_write_reg(0x7, 0x01);//Jack detect
}

5.2.3.3 初始化音频输入和输出的DMA通道

.......
output_stream.dma_ch = DMACH_I2S_OUT;
if (audio_init_dma(&output_stream, "UDA1341 out")) {audio_clear_dma(&output_stream,&s3c2410iis_dma_out);printk( KERN_WARNING AUDIO_NAME_VERBOSE": unable to get DMA channels\n" );return -EBUSY;
}input_stream.dma_ch = DMACH_I2S_IN;
if (audio_init_dma(&input_stream, "UDA1341 in")) {audio_clear_dma(&input_stream,&s3c2410iis_dma_in);printk( KERN_WARNING AUDIO_NAME_VERBOSE": unable to get DMA channels\n" );return -EBUSY;
}
.......

5.2.3.4 向OSS核心层注册音频设备

注册之后由OSS核心层管理并操作音频设备,就是第三节分析的一样。

......
audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1);
audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);
......

六,应用程序播放和录制

待定

《Linux驱动:使用音频设备驱动框架-OSS构建音频设备驱动》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. 开源人脸识别seetaface入门教程(一)
  2. redis入门(02)redis的常见问题
  3. 016. Remove me test
  4. 查询SQLSERVER执行过的SQL记录
  5. ZooKeeper管理员指南
  6. 圣思园java se培训总结(58-)(java1.5新特性,可变参数,包装类)
  7. WinServer2012 R2忘记密码的解决方案+远程连接另一种莫名其妙故障
  8. MySQL免安装版配置部署
  9. CCS 报警告 #10247-D
  10. 华硕计算机电源已连接未充电,笔记本电池显示“电源已接通,未充电”
  11. 核桃的营养价值,核桃的功效与作用
  12. OA办公系统审批流程是什么?
  13. 一篇关于职业选择的好文章
  14. 科思创为全新概念车丰田LQ提供可持续解决方案
  15. Global and Local Enhancement Networks for Paired and Unpaired Image Enhancement
  16. 艾永亮:酒瓶中的战争,谁是下一瓶被拿起的葡萄酒
  17. 将谷歌浏览器 Chrome 已安装的插件打包成 crx 给其它电脑离线安装 +【怎么安装Chrome插件】安装Chrome第三方插件
  18. arduino 天下第一(暴论) -- 智能猫眼与 SDDC 连接器移植到 arduino 上
  19. DDoS 攻击与防护(一):如何识别 DDoS 攻击?DDoS 防护 ADS 服务有哪些?
  20. Shell全解析(一):Shell脚本

热门文章

  1. 蚂蚁金服:AlipayHK上线跨境线下支付新功能
  2. C. Equalize(贪心)
  3. AD原理图编译错误:Details Duplicate pins in component Pin ×× and Pin ××
  4. 软件测试面试题及答案,不给答案的面试题都是耍流氓
  5. [Course] Advanced Computer Programming, Homework, week 2
  6. android开发笔记之多媒体—图形图像处理
  7. 在安卓手机上玩PC上的steam游戏
  8. 【剑指 Offer_15】二进制中1的个数_PythonJava_逐位相与解法
  9. 论技术、业务和商业的关系
  10. AST学习笔记 至少入个大门