Linux audio(OSS)子系统分析

本文深入探讨Linux OSS(开放声音系统)音频子系统的内部结构与工作原理,包括DSP与MIXER设备的注册过程及用户如何通过设备节点访问各厂商提供的设备操作函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux audio(OSS)子系统分析

简介

         在linux声卡的驱动中存在两种架构,一种是OSS(开放声音系统),一种是ALSA(先
进Linux声音架构)。OSS是一个商业声卡驱动程序,需要花钱购买。一般我们现在使用的
是ALSA的声音架构。 但是有些厂商提供了OSS架构的代码,一时让我无从下手,我就
遇到了这样的问题,故在此留下自己分析的过程,难免有疏漏,欢迎大家指正并与我交流,
http://blog.youkuaiyun.com/dyron.     

OOS(Open Sound System),官网:www.opensound.com 

         在这里主要介绍两个设备,DSP与MIXER.

         DSP: 用来采样和播放的文件,对该设备写操作就是播放,对该设备读就是录音操作。
这个设备可以有多个。对该设备操作时应注意一次写入数据块的大小,如果数据块过大会引
起设备的block操作。属于应用范畴的内容不在此介绍了。

         MIXER:  应用程序对混音器的软件接口。混音器电路通常由两个部分组成:input mixer
和ouput mixer.  该设备的大部分操作都是由ioctl实现的, mixer设备允许同时多个用
户同时访问。 主要设备的参数有Rate ,Channel, Format. Volume, Standby等

主要参数

         Oss系统的主要文件:

[cpp]  view plain copy
  1. sound/sound_core.c  
  2. include/linux/sound.h  
  3. sound/sound_core.h  

       Oss系统的主要结构介绍:

[cpp]  view plain copy
  1. structsound_unit  
  2. {  
  3.        int unit_minor;  
  4.        const struct file_operations *unit_fops;  
  5.        struct sound_unit *next;  
  6.        char name[32];  
  7. };  

     每个sound设备都会对应这样一个结构,unit_minor对应子设备号, unit_fops对应操作
函数,这个是由各厂商应现,在注册设备的时候传入。Next指向下一个同类形设备,name 
就不需多说了。

[cpp]  view plain copy
  1. sound_class              /sys/class/sound类  
  2. static struct sound_unit*chains[SOUND_STEP];  
  3. /*整个OSS系统的链表数组,数组的每一项代表一种声卡设备,如下表所示:*/  
  4. *       0       *16            Mixers  
  5.  *     1       *8              Sequencers  
  6.  *     2       *16            Midi  
  7.  *     3       *16            DSP  
  8.  *     4       *16            SunDSP  
  9.  *     5       *16            DSP16  
  10.  *     6       --                sndstat (obsolete)  
  11.  *     7       *16            unused  
  12.  *     8       --                alternate sequencer (see above)  
  13.  *     9       *16            raw synthesizer access  
  14.  *     10     *16            unused  
  15.  *     11     *16            unused  
  16.  *     12     *16            unused  
  17.  *     13     *16            unused  
  18.  *     14     *16            unused  
  19.  *     15     *16            unused  

 OSS系统的注册过程:

         本节描述soundclass的注册过程,与及实例说明dsp与mixer设备的注册过程,用户层
是如何通过设备节点访问到各厂商提供设备的操作函数的。

[cpp]  view plain copy
  1. static int __init init_soundcore(void)  
  2. {  
  3.          intrc;  
  4.    
  5.          rc= init_oss_soundcore();  
  6.          if(rc)  
  7.                    returnrc;  
  8.          /*注意这里的sound_class是全局函数,用于保存sound类*/  
  9.          sound_class= class_create(THIS_MODULE, "sound"); //注册/sys/class/sound类  
  10.          if(IS_ERR(sound_class)) {  
  11.                    cleanup_oss_soundcore();  
  12.                    returnPTR_ERR(sound_class);  
  13.          }  
  14.    
  15.          sound_class->devnode= sound_devnode;  
  16.    
  17.          return0;  
  18. }  

      在系统启动阶段,kernel会调用到module_init,进入init_soundcore,初始化
init_oss_soundcore, 并注册sound类,这样在/sys/class/下就有了sound类了, sound_devnode()
也决定了相应的设备节点也将会出现在/dev/snd/下面。 

[cpp]  view plain copy
  1. static void __exit cleanup_soundcore(void)  
  2. {  
  3.          cleanup_oss_soundcore();  
  4.          class_destroy(sound_class);  
  5. }  
  6.    
  7. module_init(init_soundcore);  
  8. module_exit(cleanup_soundcore);  

         cleanup_soundcore函数对应于init_soundcore,主要用于清除oss_soundcore, 并销毁

sound_class类。

         下面主要介绍一下,mixer与dsp设备的注册过程, 其它设备雷同: 

[cpp]  view plain copy
  1. int register_sound_dsp(const structfile_operations *fops, int dev)  
  2. {  
  3.          returnsound_insert_unit(&chains[3], fops, dev, 3, 131,  
  4.                                       "dsp", S_IWUSR | S_IRUSR, NULL);  
  5. }  
  6.    
  7. EXPORT_SYMBOL(register_sound_dsp);<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>  

     register_sound_dsp是注册dsp设备的函数,需要传入dsp设备的fops, 就是dsp设备的
具体操作方法实现。register_sound_dsp调用sound_insert_unit函数实现注册, 这时传入
的chains[3]就是在声卡数组链表的3上注册dsp设备,如果注册mixer设备就是chains[0]了。

[cpp]  view plain copy
  1. static intsound_insert_unit(struct sound_unit **list, const struct file_operations   
  2. *fops,int index, int low, int top, const char *name, umode_t mode, struct device *dev)  
  3. {  
  4.          struct sound_unit *s =kmalloc(sizeof(*s), GFP_KERNEL); //分配声卡设备内存  
  5.          int r;  
  6.          ……………………….  
  7.          r = __sound_insert_unit(s, list, fops,index, low, top); //注册声卡设备到list上  
  8.          spin_unlock(&sound_loader_lock);  
  9.          ………………………  
  10.          device_create(sound_class, dev,MKDEV(SOUND_MAJOR, s->unit_minor),  
  11.                          NULL, s->name+6);  
  12.          //调用device_create广播设备信息到userspace,udev创建  
  13.    

     现在就进入分析__sound_insert_unit,分析这里就有点技巧了,大家C基本功可得过关,
分得清指针数组和数组指针,指针数组就是存在一个数组,数组里全是指针变量,在32位
arm上,大于约等于max*int, 数组指针就是1个指向数组的指针,在32位arm上就是4byte.

[cpp]  view plain copy
  1. static int__sound_insert_unit(struct sound_unit * s, struct sound_unit **list,   
  2. conststruct file_operations *fops, int index, int low, int top)  
  3. {    //传入的参数依次为: s,&chains[3], fops, dev, 3, 131  
  4.          ……………………………..  
  5.          if (index < 0) {   /* first free */  //index 传入为 -1, 故进入此分支  
  6.                    while (*list &&(*list)->unit_minor<n) //*list 就等于chains[3]值是否为空,为空  
  7.                             list=&((*list)->next);  
  8.                    while(n<top)  
  9.                    {  
  10.                             /* Found a hole ? */  
  11.                             if(*list==NULL ||(*list)->unit_minor>n) //因为第一次注册list为空,跳出  
  12.                                      break;  
  13.                             list=&((*list)->next);  
  14.                             n+=SOUND_STEP;  
  15.                    }  
  16.    
  17.                    if(n>=top)  
  18.                             return -ENOENT;  
  19.          } else {  
  20.                    ……………………………..  
  21.          }         
  22.           
  23.          s->unit_minor=n;                        //直接赋值退出  
  24.          s->unit_fops=fops;  
  25.             
  26.          s->next=*list;  
  27.          *list=s;  
  28.    

         接下来返回到sound_insert_unit, 通过__register_chrdev将s, 注册到系统上,这
里/dev/snd/下就出现dsp名字了,但奇怪的时,这里传入的fops不是s->fops,而是系统公
用的全局soundcore_fops, 在打开dsp时接着分析。

[cpp]  view plain copy
  1. r = __register_chrdev(SOUND_MAJOR,s->unit_minor, 1, s->name,  
  2.                                   &soundcore_fops);  

打开设备流程

         由于注册的时候传入的是soundcore_fops, 故打开设备时会进入soundcore_fops的
open函数。Soundcore_fops结构体如下.

[cpp]  view plain copy
  1. static const struct file_operationssoundcore_fops =  
  2. {  
  3.          /*We must have an owner or the module locking fails */  
  4.          .owner     = THIS_MODULE,  
  5.          .open        = soundcore_open,  
  6. };  
  7.    

         在这里进入soundcore_open函数, 现在我们就跟进去分析。

[cpp]  view plain copy
  1. static int soundcore_open(struct inode*inode, struct file *file)  
  2. {  
  3.          …………………  
  4.          if(s)  
  5.                    new_fops= fops_get(s->unit_fops);         
  6.          /*在这里获得__sound_insert_unit注册的fops, 就是我们在注册dsp时传入的fops. */  
  7.    
  8.          if(preclaim_oss && !new_fops) {   //这里的new_fops是有值的,所有不会进入此分支  
  9.                    spin_unlock(&sound_loader_lock);  
  10.          …………..  
  11.          if(new_fops) {  
  12.                    …………………..  
  13.                    conststruct file_operations *old_fops = file->f_op;  
  14.                    file->f_op= new_fops;              //在这里偷天换日,将系统的fops转换为s->fops  
  15.                    spin_unlock(&sound_loader_lock);  
  16.                    if(file->f_op->open)  
  17.                             err= file->f_op->open(inode,file);  
  18.                    if(err) {  
  19.                             fops_put(file->f_op);  
  20.                             file->f_op= fops_get(old_fops);  
  21.                    }  
  22.                    fops_put(old_fops);  
  23.                    unlock_kernel();  
  24.                    returnerr;  
  25.          }  
  26.    

         在这里就发现在open设置时,系统将设备的fops转换为dsp注册时的fops, 偷天换
日啊, linux kernel还是蛮神奇的。

 

         到这里,全文就结束了, 难免有疏漏,欢迎大家拍砖:http://blog.youkuaiyun.com/dyron

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值