导读: 在linux声卡的驱动中存在两种架构,一种是OSS(开放声音系统),一种是ALSA(先进Linux声音架构)。OSS是一个商业声卡驱动程序,需要花钱购买。一般我们现在使用的是ALSA的声音架构。OOS(Open Sound System),在这里主要介绍两个设备,DSP与MIXER.DSP: 用来采样和播放的文件,对该设备写操作就是播放,对该设备读就是录音操作。
简介
在linux声卡的驱动中存在两种架构,一种是OSS(开放声音系统),一种是ALSA(先进Linux声音架构)。OSS是一个商业声卡驱动程序,需要花钱购买。一般我们现在使用的是ALSA的声音架构。OOS(Open Sound System),在这里主要介绍两个设备,DSP与MIXER.DSP: 用来采样和播放的文件,对该设备写操作就是播放,对该设备读就是录音操作。
这个设备可以有多个。对该设备操作时应注意一次写入数据块的大小,如果数据块过大会引起设备的block操作。属于应用范畴的内容不在此介绍了。
MIXER: 应用程序对混音器的软件接口。混音器电路通常由两个部分组成:input mixer和ouput mixer. 该设备的大部分操作都是由ioctl实现的, mixer设备允许同时多个用户同时访问。 主要设备的参数有Rate ,Channel, Format. Volume, Standby等
主要参数
Oss系统的主要文件:
sound/sound_core.c
include/linux/sound.h
sound/sound_core.h
sound/sound_core.cinclude/linux/sound.hsound/sound_core.h
Oss系统的主要结构介绍:
structsound_unit
{
int unit_minor;
const struct file_operations *unit_fops;
struct sound_unit *next;
char name[32];
};
structsound_unit{int unit_minor;const struct file_operations *unit_fops;struct sound_unit *next;char name[32];};
每个sound设备都会对应这样一个结构,unit_minor对应子设备号, unit_fops对应操作
函数,这个是由各厂商应现,在注册设备的时候传入。Next指向下一个同类形设备,name
就不需多说了。
sound_class /sys/class/sound类
static struct sound_unit*chains[SOUND_STEP];
/*整个OSS系统的链表数组,数组的每一项代表一种声卡设备,如下表所示:*/
* 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
sound_class /sys/class/sound类static struct sound_unit*chains[SOUND_STEP];/*整个OSS系统的链表数组,数组的每一项代表一种声卡设备,如下表所示:*/* 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
OSS系统的注册过程:
本节描述soundclass的注册过程,与及实例说明dsp与mixer设备的注册过程,用户层
是如何通过设备节点访问到各厂商提供设备的操作函数的。
static int __init init_soundcore(void)
{
intrc;
rc= init_oss_soundcore();
if(rc)
returnrc;
/*注意这里的sound_class是全局函数,用于保存sound类*/
sound_class= class_create(THIS_MODULE, “sound”); //注册/sys/class/sound类
if(IS_ERR(sound_class)) {
cleanup_oss_soundcore();
returnPTR_ERR(sound_class);
}
sound_class-》devnode= sound_devnode;
return0;
}
static int __init init_soundcore(void){intrc;rc= init_oss_soundcore();if(rc)returnrc;/*注意这里的sound_class是全局函数,用于保存sound类*/sound_class= class_create(THIS_MODULE, “sound”); //注册/sys/class/sound类if(IS_ERR(sound_class)) {cleanup_oss_soundcore();returnPTR_ERR(sound_class);}sound_class-》devnode= sound_devnode;return0;}
在系统启动阶段,kernel会调用到module_init,进入init_soundcore,初始化
init_oss_soundcore, 并注册sound类,这样在/sys/class/下就有了sound类了, sound_devnode()
也决定了相应的设备节点也将会出现在/dev/snd/下面。
static void __exit cleanup_soundcore(void)
{
cleanup_oss_soundcore();
class_destroy(sound_class);
}
module_init(init_soundcore);
module_exit(cleanup_soundcore);
static void __exit cleanup_soundcore(void){cleanup_oss_soundcore();class_destroy(sound_class);}module_init(init_soundcore);module_exit(cleanup_soundcore);
cleanup_soundcore函数对应于init_soundcore,主要用于清除oss_soundcore, 并销毁
sound_class类。
下面主要介绍一下,mixer与dsp设备的注册过程, 其它设备雷同:
int register_sound_dsp(const structfile_operations *fops, int dev)
{
returnsound_insert_unit(&chains[3], fops, dev, 3, 131,
“dsp”, S_IWUSR | S_IRUSR, NULL);
}
EXPORT_SYMBOL(register_sound_dsp);“font-family: Arial, Verdana, sans-serif; white-space: normal; ”》
int register_sound_dsp(const structfile_operations *fops, int dev){returnsound_insert_unit(&chains[3], fops, dev, 3, 131,“dsp”, S_IWUSR | S_IRUSR, NULL);}EXPORT_SYMBOL(register_sound_dsp);
register_sound_dsp是注册dsp设备的函数,需要传入dsp设备的fops, 就是dsp设备的
具体操作方法实现。register_sound_dsp调用sound_insert_unit函数实现注册, 这时传入
的chains[3]就是在声卡数组链表的3上注册dsp设备,如果注册mixer设备就是chains[0]了。
static intsound_insert_unit(struct sound_unit **list, const struct file_operations
*fops,int index, int low, int top, const char *name, umode_t mode, struct device *dev)
{
struct sound_unit *s =kmalloc(sizeof(*s), GFP_KERNEL); //分配声卡设备内存
int r;
………………………。
r = __sound_insert_unit(s, list, fops,index, low, top); //注册声卡设备到list上
spin_unlock(&sound_loader_lock);
………………………
device_create(sound_class, dev,MKDEV(SOUND_MAJOR, s-》unit_minor),
NULL, s-》name+6);
//调用device_create广播设备信息到userspace,udev创建
static intsound_insert_unit(struct sound_unit **list, const struct file_operations*fops,int index, int low, int top, const char *name, umode_t mode, struct device *dev){struct sound_unit *s =kmalloc(sizeof(*s), GFP_KERNEL); //分配声卡设备内存int r;……………………….r = __sound_insert_unit(s, list, fops,index, low, top); //注册声卡设备到list上spin_unlock(&sound_loader_lock);………………………device_create(sound_class, dev,MKDEV(SOUND_MAJOR, s-》unit_minor),NULL, s-》name+6);//调用device_create广播设备信息到userspace,udev创建
现在就进入分析__sound_insert_unit,分析这里就有点技巧了,大家C基本功可得过关,
分得清指针数组和数组指针,指针数组就是存在一个数组,数组里全是指针变量,在32位
arm上,大于约等于max*int, 数组指针就是1个指向数组的指针,在32位arm上就是4byte.
static int__sound_insert_unit(struct sound_unit * s, struct sound_unit **list,
conststruct file_operations *fops, int index, int low, int top)
{ //传入的参数依次为: s,&chains[3], fops, dev, 3, 131
……………………………。.
if (index 《 0) { /* first free */ //index 传入为 -1, 故进入此分支
while (*list &&(*list)-》unit_minor//*list 就等于chains[3]值是否为空,为空
list=&((*list)-》next);
while(n
{
/* Found a hole ? */
if(*list==NULL ||(*list)-》unit_minor》n) //因为第一次注册list为空,跳出
break;
list=&((*list)-》next);
n+=SOUND_STEP;
}
if(n》=top)
return -ENOENT;
} else {
……………………………。.
}
s-》unit_minor=n; //直接赋值退出
s-》unit_fops=fops;
s-》next=*list;
*list=s;
static int__sound_insert_unit(struct sound_unit * s, struct sound_unit **list,conststruct file_operations *fops, int index, int low, int top){ //传入的参数依次为: s,&chains[3], fops, dev, 3, 131……………………………。.if (index 《 0) { /* first free */ //index 传入为 -1, 故进入此分支while (*list &&(*list)-》unit_minornext);while(nunit_minor》n) //因为第一次注册list为空,跳出break;list=&((*list)-》next);n+=SOUND_STEP;}if(n》=top)return -ENOENT;} else {……………………………。.}s-》unit_minor=n; //直接赋值退出s-》unit_fops=fops;s-》next=*list;*list=s;
接下来返回到sound_insert_unit, 通过__register_chrdev将s, 注册到系统上,这
里/dev/snd/下就出现dsp名字了,但奇怪的时,这里传入的fops不是s-》fops,而是系统公
用的全局soundcore_fops, 在打开dsp时接着分析。
r = __register_chrdev(SOUND_MAJOR,s-》unit_minor, 1, s-》name,
&soundcore_fops);
r = __register_chrdev(SOUND_MAJOR,s-》unit_minor, 1, s-》name,&soundcore_fops);
打开设备流程
由于注册的时候传入的是soundcore_fops, 故打开设备时会进入soundcore_fops的
open函数。Soundcore_fops结构体如下。
static const struct file_operationssoundcore_fops =
{
/*We must have an owner or the module locking fails */
.owner = THIS_MODULE,
.open = soundcore_open,
};
static const struct file_operationssoundcore_fops ={/*We must have an owner or the module locking fails */.owner = THIS_MODULE,.open = soundcore_open,};
在这里进入soundcore_open函数, 现在我们就跟进去分析。
static int soundcore_open(struct inode*inode, struct file *file)
{
…………………
if(s)
new_fops= fops_get(s-》unit_fops);
/*在这里获得__sound_insert_unit注册的fops, 就是我们在注册dsp时传入的fops. */
if(preclaim_oss && !new_fops) { //这里的new_fops是有值的,所有不会进入此分支
spin_unlock(&sound_loader_lock);
…………。.
if(new_fops) {
…………………。.
conststruct file_operations *old_fops = file-》f_op;
file-》f_op= new_fops; //在这里偷天换日,将系统的fops转换为s-》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);
unlock_kernel();
returnerr;
}
static int soundcore_open(struct inode*inode, struct file *file){…………………if(s)new_fops= fops_get(s-》unit_fops);/*在这里获得__sound_insert_unit注册的fops, 就是我们在注册dsp时传入的fops. */if(preclaim_oss && !new_fops) { //这里的new_fops是有值的,所有不会进入此分支spin_unlock(&sound_loader_lock);…………。.if(new_fops) {…………………。.conststruct file_operations *old_fops = file-》f_op;file-》f_op= new_fops; //在这里偷天换日,将系统的fops转换为s-》fopsspin_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);unlock_kernel();returnerr;}
在这里就发现在open设置时,系统将设备的fops转换为dsp注册时的fops, 偷天换
日啊, linux kernel还是蛮神奇的。