`

Linux音频设备驱动-4

 
阅读更多
2008-10-03 19:07
17.5实例1:S3C2410+UDA1341 OSS驱动
17.5.1 S3C2410与UDA1341接口硬件描述
如图17.7,S3C2410处理器内置了IIS总线接口,S3C2410的IIS总线时钟信号SCK与Philip公司的UDA1341的BCK连接,字段选择连接于WS引脚。UDA1341提供两个音频通道,分别用于输入和输出,对应的引脚连接:IIS总线的音频输出IISSDO对应于UDA1341的音频输入;IIS总线的音频输入IISSDI对应于UDA1341的音频输出。UDA1341的L3接口相当于一个混音器控制接口,可以用来控制输入/输出音频信号的音量大小、低音等。L3接口的引脚L3MODE、L3DATA、L3CLOCK分别连接到S3C2410的3个GPIO来控制。


图17.7 S3C2410与UDA1341 IIS接口连接
Philips 公司的UDA1341支持IIS总线数据格式,采用位元流转换技术进行信号处理,完成声音信号的模数转换,具有可编程增益放大器和数字自动增益控制器,其低功耗、低电压的特点使其非常适合用于MD/CD、笔记本电脑等便携式设备。UDA1341对外提供2组音频信号输入接口,每组包括左右2个声道。

图17.8 UDA1341 内部结构
如图17.8所示,2组音频输入在UDA1341内部的处理存在很大差别:第一组音频信号输入后经过1个0 dB/6 dB开关后采样送入数字混音器:第二组音频信号输入后先经过可编程增益放大器(PGA),然后再进行采样,采样后的数据要再经过数字自动增益控制器(AGC)送入数字混音器。设计硬件电路时选用第二组输入音频信号,这样可以通过软件的方法实现对系统输入音量大小的调节。显然选用第二组可以通过L3总线接口控制AGC来实现。另外,选择通道2还可以通过PGA对从MIC输入的信号进行片内放大。
S3C2410与UDA1341之间的IIS接口有3种工作方式:
• 正常传输模式。该模式下使用IISCON寄存器对FIFO进行控制,CPU通过轮询方式访问FIFO寄存器,以完成对FIFO缓存传输或接收的处理。
• DMA模式。通过设置IISFCON寄存器使IIS接口工作于这种模式。在该模式下,FIFO寄存器组的控制权掌握在DMA控制器上,当FIFO满时,由DMA控制器对FIFO中的数据进行处理。DMA模式的选择由IISCON寄存器的第4和第5位控制。
• 传输/接收模式。该模式下,IIS数据线将通过双通道DMA同时接收和发送音频数据。在OSS驱动中,将使用此模式。
17.5.2注册dsp和mixer接口
如代码清单17.28,在UDA1341 OSS驱动的模块加载函数中,将完成如下工作:
• 初始化IIS接口硬件,设置L3总线对应的GPIO。
• 申请用于音频数据传输的DMA通道。
• 初始化UDA1341到恰当的工作模式。
• 注册dsp和mixer接口。
代码清单17.28 UDA1341 OSS驱动模块加载函数
1 //音频(dsp)文件操作
2 static struct file_operations smdk2410_audio_fops =
3 {
4 llseek: smdk2410_audio_llseek,
5 write: smdk2410_audio_write,
6 read: smdk2410_audio_read,
7 poll: smdk2410_audio_poll,
8 ioctl: smdk2410_audio_ioctl,
9 open: smdk2410_audio_open,
10 release: smdk2410_audio_release
11 };
12 //混音器文件操作
13 static struct file_operations smdk2410_mixer_fops =
14 {
15 ioctl: smdk2410_mixer_ioctl,
16 open: smdk2410_mixer_open,
17 release: smdk2410_mixer_release
18 };
19
20 int __init s3c2410_uda1341_init(void)
21 {
22 unsigned long flags;
23
24 local_irq_save(flags);
25
26 /* 设置IIS接口引脚GPIO */
27
28 set_gpio_ctrl(GPIO_L3CLOCK); // GPB 4: L3CLOCK, 输出
29 set_gpio_ctrl(GPIO_L3DATA); // GPB 3: L3DATA, 输出
30 set_gpio_ctrl(GPIO_L3MODE); // GPB 2: L3MODE, 输出
31
32
33 set_gpio_ctrl(GPIO_E3 | GPIO_PULLUP_EN | GPIO_MODE_IISSDI); //GPE 3: IISSDI
34 set_gpio_ctrl(GPIO_E0 | GPIO_PULLUP_EN | GPIO_MODE_IISSDI); //GPE 0: IISLRCK
35 set_gpio_ctrl(GPIO_E1 | GPIO_PULLUP_EN | GPIO_MODE_IISSCLK); //GPE 1:IISSCLK
36 set_gpio_ctrl(GPIO_E2 | GPIO_PULLUP_EN | GPIO_MODE_CDCLK); //GPE 2: CDCLK
37 set_gpio_ctrl(GPIO_E4 | GPIO_PULLUP_EN | GPIO_MODE_IISSDO); //GPE 4: IISSDO
38
39 local_irq_restore(flags);
40
41 init_uda1341();
42
43 /* 输出流采样DMA通道2 */
44 output_stream.dma_ch = DMA_CH2;
45
46 if (audio_init_dma(&output_stream, "UDA1341 out"))
47 {
48 audio_clear_dma(&output_stream);
49 printk(KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels/n");
50 return - EBUSY;
51 }
52 /* 输入流采样DMA通道1 */
53 input_stream.dma_ch = DMA_CH1;
54
55 if (audio_init_dma(&input_stream, "UDA1341 in"))
56 {
57 audio_clear_dma(&input_stream);
58 printk(KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels/n");
59 return - EBUSY;
60 }
61
62 /* 注册dsp和mixer设备接口 */
63 audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, - 1);
64 audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, - 1);
65
66 printk(AUDIO_NAME_VERBOSE " initialized/n");
67
68 return 0;
69 }
UDA1341 OSS驱动的模块卸载函数中,将完成与模块加载函数相反的工作,如代码清单17.29。
代码清单17.29 UDA1341 OSS驱动模块卸载函数
1 void __exit s3c2410_uda1341_exit(void)
2 {
3 //注销dsp和mixer设备接口
4 unregister_sound_dsp(audio_dev_dsp);
5 unregister_sound_mixer(audio_dev_mixer);
6
7 //注销DMA通道
8 audio_clear_dma(&output_stream);
9 audio_clear_dma(&input_stream); /* input */
10 printk(AUDIO_NAME_VERBOSE " unloaded/n");
11 }
17.5.3 mixer接口IO控制函数
UDA1341 OSS驱动的ioctl()函数处理多个mixer命令,如SOUND_MIXER_INFO、 SOUND_MIXER_READ_STEREODEVS、SOUND_MIXER_WRITE_VOLUME等,用于获得或设置音量和增益等信息,如代码清单17.30所示。
代码清单17.30 UDA1341 OSS驱动ioctl()函数
1 static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file,
2 unsigned int cmd, unsigned long arg)
3 {
4 int ret;
5 long val = 0;
6
7 switch (cmd)
8 {
9 case SOUND_MIXER_INFO: //获得mixer信息
10 {
11 mixer_info info;
12 strncpy(info.id, "UDA1341", sizeof(info.id));
13 strncpy(info.name, "Philips UDA1341", sizeof(info.name));
14 info.modify_counter = audio_mix_modcnt;
15 return copy_to_user((void*)arg, &info, sizeof(info));
16 }
17
18 case SOUND_OLD_MIXER_INFO:
19 {
20 _old_mixer_info info;
21 strncpy(info.id, "UDA1341", sizeof(info.id));
22 strncpy(info.name, "Philips UDA1341", sizeof(info.name));
23 return copy_to_user((void*)arg, &info, sizeof(info));
24 }
25
26 case SOUND_MIXER_READ_STEREODEVS://获取设备对立体声的支持
27 return put_user(0, (long*)arg);
28
29 case SOUND_MIXER_READ_CAPS: //获取声卡能力
30 val = SOUND_CAP_EXCL_INPUT;
31 return put_user(val, (long*)arg);
32
33 case SOUND_MIXER_WRITE_VOLUME: //设置音量
34 ret = get_user(val, (long*)arg);
35 if (ret)
36 return ret;
37 uda1341_volume = 63-(((val &0xff) + 1) *63) / 100;
38 uda1341_l3_address(UDA1341_REG_DATA0);
39 uda1341_l3_data(uda1341_volume);
40 break;
41
42 case SOUND_MIXER_READ_VOLUME: //获取音量
43 val = ((63-uda1341_volume) *100) / 63;
44 val |= val << 8;
45 return put_user(val, (long*)arg);
46
47 case SOUND_MIXER_READ_IGAIN: //获得增益
48 val = ((31-mixer_igain) *100) / 31;
49 return put_user(val, (int*)arg);
50
51 case SOUND_MIXER_WRITE_IGAIN: //设置增益
52 ret = get_user(val, (int*)arg);
53 if (ret)
54 return ret;
55 mixer_igain = 31-(val *31 / 100);
56 /* 使用mixer增益通道1 */
57 uda1341_l3_address(UDA1341_REG_DATA0);
58 uda1341_l3_data(EXTADDR(EXT0));
59 uda1341_l3_data(EXTDATA(EXT0_CH1_GAIN(mixer_igain)));
60 break;
61
62 default:
63 DPRINTK("mixer ioctl %u unknown/n", cmd);
64 return - ENOSYS;
65 }
66
67 audio_mix_modcnt++;
68 return 0;
69 }
17.5.4 dsp接口音频数据传输
OSS声卡驱动中,dsp接口的读写函数是核心中的核心,直接对应着录音和放音的流程。
OSS 的读函数存在一个与普通字符设备驱动读函数不同的地方,那就是一般而言,对于普通字符设备驱动,如果用户要求读count个字节,而实际上只有 count1字节可获得(count1< count)时,它会将这count1字节拷贝给用户后即返回count1;而dsp接口的读函数会分次拷贝,如果第1次不能满足,它会等待第2次,直到 “count1 + count2 + ... = count”为止再返回count。这种设计是合理的,因为OSS驱动应该负责音频数据的流量控制。代码清单17.31给出了UDA1341 OSS驱动的读函数实现。
代码清单17.31 UDA1341 OSS驱动的读函数
1 static ssize_t smdk2410_audio_read(struct file *file, char *buffer, size_t
2 count, loff_t *ppos)
3 {
4 const char *buffer0 = buffer;
5 audio_stream_t *s = &input_stream; //得到数据区的指针
6 int chunksize, ret = 0;
7
8 DPRINTK("audio_read: count=%d/n", count);
9
10 if (ppos != &file->f_pos)
11 return - ESPIPE;
12
13 if (!s->buffers)
14 {
15 int i;
16
17 if (audio_setup_buf(s))
18 return - ENOMEM;
19 //依次从缓存区读取数据
20 for (i = 0; i < s->nbfrags; i++)
21 {
22 audio_buf_t *b = s->buf;
23 down(&b->sem);
24 s3c2410_dma_queue_buffer(s->dma_ch, (void*)b, b->dma_addr, s->fragsize,
25 DMA_BUF_RD);
26 NEXT_BUF(s, buf);
27 }
28 }
29
30 //满足用户的所有读需求
31 while (count > 0)
32 {
33 audio_buf_t *b = s->buf;
34
35 if (file->f_flags &O_NONBLOCK) //非阻塞
36 {
37 ret = - EAGAIN;
38 if (down_trylock(&b->sem))
39 break;
40 }
41 else
42 {
43 ret = - ERESTARTSYS;
44 if (down_interruptible(&b->sem))
45 break;
46 }
47
48 chunksize = b->size;
49 //从缓存区读取数据
50 if (chunksize > count)
51 chunksize = count;
52 DPRINTK("read %d from %d/n", chunksize, s->buf_idx);
53 if (copy_to_user(buffer, b->start + s->fragsize - b->size, //调用拷贝函数
54 chunksize))
55 {
56 up(&b->sem);
57 return - EFAULT;
58 }
59 b->size -= chunksize;
60
61 buffer += chunksize;
62 count -= chunksize; //已经给用户拷贝了一部分,count减少
63 if (b->size > 0)
64 {
65 up(&b->sem);
66 break;
67 }
68 //将缓存区释放
69 s3c2410_dma_queue_buffer(s->dma_ch, (void*)b, b->dma_addr, s->fragsize,
70 DMA_BUF_RD);
71
72 NEXT_BUF(s, buf);
73 }
74
75 if ((buffer - buffer0))
76 ret = buffer - buffer0;
77
78 return ret;
79 }
OSS驱动dsp接口的写函数与读函数类似,一般来说,它也应该满足用户的所有写需求后再返回,如代码清单17.32。
代码清单17.32 UDA1341 OSS驱动的写函数
1 static ssize_t smdk2410_audio_write(struct file *file, const char *buffer,
2 size_t count, loff_t *ppos)
3 {
4 const char *buffer0 = buffer;
5 audio_stream_t *s = &output_stream;
6 int chunksize, ret = 0;
7
8 DPRINTK("audio_write : start count=%d/n", count);
9
10 switch (file->f_flags &O_ACCMODE)
11 {
12 case O_WRONLY: //只写
13 case O_RDWR: //读写
14 break;
15 default: //只读不合法
16 return - EPERM;
17 }
18 //设置DMA缓冲区
19 if (!s->buffers && audio_setup_buf(s))
20 return - ENOMEM;
21
22 count &= ~0x03;
23
24 while (count > 0) //直到满足用户的所有写需求
25 {
26 audio_buf_t *b = s->buf;
27 //非阻塞访问
28 if (file->f_flags &O_NONBLOCK)
29 {
30 ret = - EAGAIN;
31 if (down_trylock(&b->sem))
32 break;
33 }
34 else
35 {
36 ret = - ERESTARTSYS;
37 if (down_interruptible(&b->sem))
38 break;
39 }
40 //从用户空间拷贝音频数据
41 if (audio_channels == 2)
42 {
43 chunksize = s->fragsize - b->size;
44 if (chunksize > count)
45 chunksize = count;
46 DPRINTK("write %d to %d/n", chunksize, s->buf_idx);
47 if (copy_from_user(b->start + b->size, buffer, chunksize))
48 {
49 up(&b->sem);
50 return - EFAULT;
51 }
52 b->size += chunksize;
53 }
54 else
55 {
56 chunksize = (s->fragsize - b->size) >> 1;
57
58 if (chunksize > count)
59 chunksize = count;
60 DPRINTK("write %d to %d/n", chunksize *2, s->buf_idx);
61 if (copy_from_user_mono_stereo(b->start + b->size, buffer, chunksize))
62 {
63 up(&b->sem);
64 return - EFAULT;
65 }
66
67 b->size += chunksize * 2;
68 }
69
70 buffer += chunksize;
71 count -= chunksize; //已经从用户拷贝了一部分,count减少
72 if (b->size < s->fragsize)
73 {
74 up(&b->sem);
75 break;
76 }
77 //发起DMA操作
78 s3c2410_dma_queue_buffer(s->dma_ch, (void*)b, b->dma_addr, b->size,
79 DMA_BUF_WR);
80 b->size = 0;
81 NEXT_BUF(s, buf);
82 }
83
84 if ((buffer - buffer0))
85 ret = buffer - buffer0;
86
87 DPRINTK("audio_write : end count=%d/n/n", ret);
88
89 return ret;
90 }
分享到:
评论

相关推荐

    华清远见驱动教程

    -第1章、设备驱动概述 ...-第17章、Linux音频设备驱动 -第18章、LCD设备驱动 -第19章、Flash设备驱动 -第20章、USB主机与设备驱动 -第21章、PCI设备驱动 -第22章、Linux设备驱动的调试 -第23章、Linux设备驱动的移植

    Linux音频设备驱动

    对linux的音频驱动讲解的非常到为,写的非常到好,这是大神级别到

    8810音频驱动linux-android。完整资料.zip

    在音频设备驱动中ASoC被分为Machine、Platform和Codec三大部分。 Codec部分:负责音频解码,这部分代码全然无平台无关(设备原厂提供) Platform部分:包括了平台的音频DMA和音频接口的配置和控制(I2S,PCM。AC97...

    Linux设备驱动详解第二版

    第17章 Linux音频设备驱动 388 第18章 LCD设备驱动 440 第19章 Flash设备驱动 479 第20章 USB主机与设备驱动 507 第21章 PCI设备驱动 547 第4篇 Linux设备驱动调试、移植 第22章 Linux设备驱动的...

    arm linux利用alsa驱动并使用usb音频设备

    主要介绍了arm linux利用alsa驱动并使用usb音频设备的相关资料,需要的朋友可以参考下

    Linux设备驱动程序开发详解

    字符设备、块设备、TTY设备、I2C设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动的架构和框架中各个复杂数据架构和函数的关系,并讲解了Linux驱动开发的大量实例,使读者能够独立开发各类Linux...

    嵌入式Linux设备驱动开发详解

    本书特色:本书系统地介绍了嵌入式Linux设备驱动开发的相关知识和实例,主要包括: 数码管驱动程序;键盘驱动程序;A/D、D/A驱动程序;LCD驱动程序;触摸屏驱动程序;CAN总线驱动程序;I2C总线驱动程序;音频总线...

    linux设备驱动详解(宋宝华 高清 非影印版)

    字符设备、块设备、TTY设备、IC设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动的架构和框架中各个复杂数据架构和函数的关系,并讲解了Linux驱动开发的大量实例,使读者能够独立开发各类Linux...

    精通LINUX设备驱动程序开发

     第16章 Linux无线设备驱动   第17章 存储技术设备   第18章 嵌入式Linux   第19章 用户空间的驱动程序   第20章 其他设备和驱动程序   第21章 调试设备驱动程序   第22章 维护与发布   第23...

    精通Linux设备驱动程序开发

    第16章 Linux无线设备驱动 第17章 存储技术设备 第18章 嵌入式Linux 第19章 用户空间的驱动程序 第20章 其他设备和驱动程序 第21章 调试设备驱动程序 第22章 维护与发布 第23章 结束语 附录A Linux汇编 ...

    Linux 蓝牙协议栈的USB+设备驱动

    给出实现蓝牙设备驱动的重要数据结构和流程,并总结Linux 下开发蓝牙USB 设备驱动的一般方法和关键技术。 关键词:Linux 系统;蓝牙协议栈;设备驱动 USB Device Driver for Linux Bluetooth Stack LIANG Jun-xue...

    《Linux 设备驱动开发详解》第一版第一次印刷勘误

    字符设备、块设备、TTY设备、IC设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动的架构和框架中各个复杂数据架构和函数的关系,并讲解了Linux驱动开发的大量实例,使读者能够独立开发各类Linux...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

     本书是linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深入探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如pcmcia、i2c和usb等外部总线以及视频、音频、...

    精通linux设备驱动程序开发

    《精通Linux设备驱动程序开发》是Linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深人探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如PCMCIA、I2C和USB等...

    Linux设备驱动开发

    字符设备、块设备、TTY设备、I2C设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动的架构和框架中各个复杂数据架构和函数的关系,并讲解了Linux驱动开发的大量实例,使读者能够独立开发各类Linux...

    Linux_alsa.zip_alsa_linux 音频驱动

    Linux alsa 音频设备驱动文件

    \精通Linux设备驱动程序开发

    《精通Linux设备驱动程序开发》是Linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深人探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如PCMCIA、I2C和USB等...

    嵌入式Linux设备驱动开发

    字符设备、块设备、TTY设备、I2C设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动的架构和框架中各个复杂数据架构和函数的关系,并讲解了大量Linux驱动开发的大量实例,使读者能够独立开发各类...

Global site tag (gtag.js) - Google Analytics