一、mixer接口
int register_sound_mixer(struct file_operations *fops, int dev);
上述函数用于注册1 个混音器,第1 个参数fops 即是文件操作接口,第2 个参数dev 是设备编号,如果填入-1,则系统自动分配1 个设备编号。mixer 是1 个典型的字符设备,因此编码的主要工作是实现file_operations 中的open()、ioctl()等函数。mixer 接口file_operations 中的最重要函数是ioctl(),它实现混音器的不同IO 控制命令,下面的代码清单给出了1 个ioctl()的范例。
mixer()接口ioctl()函数范例:
static int mixdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
...
switch (cmd)
{
case SOUND_MIXER_READ_MIC:
...
case SOUND_MIXER_WRITE_MIC:
...
case SOUND_MIXER_WRITE_RECSRC:
...
case SOUND_MIXER_WRITE_MUTE:
...
}
...
return mixer_ioctl(codec, cmd, arg);
}
二、dsp接口
int register_sound_dsp(struct file_operations *fops, int dev);
上述函数与register_sound_mixer()类似,它用于注册1 个dsp 设备,第1 个参数fops 即是文件操作接口,第2 个参数dev 是设备编号,如果填入-1,则系统自动分配1 个设备编号。dsp 也是1 个典型的字符设备,因此编码的主要工作是实现file_operations 中的read()、write()、ioctl()等函数。dsp 接口file_operations 中的read()和write()函数非常重要,read()函数从音频控制器中获取录音数据到缓冲区并拷贝到用户空间,write()函数从用户空间拷贝音频数据到内核空间缓冲区并最终发送到音频控制器。
dsp 接口file_operations 中的ioctl()函数处理对采样率、量化精度、DMA 缓冲区块大小等参数设置IO控制命令的处理。在数据从缓冲区拷贝到音频控制器的过程中,通常会使用DMA,DMA对声卡而言非常重要。例如,在放音时,驱动设置完DMA 控制器的源数据地址(内存中DMA 缓冲区)、目的地址(音频控制器FIFO)和DMA 的数据长度,DMA 控制器会自动发送缓冲区的数据填充FIFO,直到发送完相应的数据长度后才中断一次。
在OSS 驱动中,建立存放音频数据的环形缓冲区(ring buffer)通常是值得推荐的方法。此外,在OSS 驱动中,一般会将1 个较大的DMA 缓冲区分成若干个大小相同的块(这些块也被称为“段”,即fragment),驱动程序使用DMA 每次在声音缓冲区和声卡之间搬移一个fragment。在用户空间,可以使用ioctl()系统调用来调整块的大小和个数。除了read()、write()和ioctl()外,dsp 接口的poll()函数通常也需要被实现,以向用户反馈目前能否读写DMA 缓冲区。在OSS 驱动初始化过程中,会调用register_sound_dsp()和register_sound_mixer()注册dsp 和mixer 设备;在模块卸载的时候,调用的代码如下:
OSS 驱动初始化注册dsp 和mixer设备:
static int myoss_init(void)
{
struct oss_state *s = &myoss_state;
...
//注册dsp 设备
if ((audio_dev_dsp = register_sound_dsp(&xxx_audio_fops, - 1)) < 0)
goto err_dev1;
//注册mixer 设备
if ((audio_dev_mixer = register_sound_mixer(&xxx_mixer_fops, - 1)) < 0)
goto err_dev2;
...
}
void __exit xxx_exit(void)
{
//注销dsp 和mixer 设备接口
unregister_sound_dsp(audio_dev_dsp);
unregister_sound_mixer(audio_dev_mixer);
...
}