Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2123224
  • 博文数量: 288
  • 博客积分: 10594
  • 博客等级: 上将
  • 技术积分: 3469
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-27 19:27
文章分类

全部博文(288)

文章存档

2012年(4)

2011年(30)

2010年(40)

2009年(32)

2008年(71)

2007年(79)

2006年(32)

分类: LINUX

2011-10-14 09:51:18

ADUIO OSS 的buff 分析

接上文的OSS驱动分析,现在分析OSS中的播放与录音缓冲, 
作者:dyronchina@gmail.com,欢迎大家来讨论


统一术语

1.      JZ                                            君正4760b mips

2.      Audio buff                            就是整个list_head+ list_node *4 + 整个音频区

3.      APP                                         应用程序


      整体使用框图

Buff分配流程

     首先在probe JZ mixer设备的时候进行初始化DMA及缓冲buff.  在函数init_jz_i2s中调
用audio_init_endpoint进行实始化。

  1. fragsize= JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;  
  2. fragstotal= JZCODEC_RW_BUFFER_TOTAL;  
  3.   
  4. audio_init_endpoint(&out_endpoint,fragsize, fragstotal);  
  5. audio_init_endpoint(&in_endpoint,fragsize, fragstotal);  

         进入audio_init_endpoint函数,在这里先表一下in_endpoint和out_ endpoint结构体, 初始化为0.

  1. static audio_pipe out_endpoint = {  
  2.          .mem                 = 0,  
  3.          .savenode         = 0,  
  4.          .fragsize  = 0,  
  5.          .fragstotal        = 0,  
  6.          .trans_state    = 0,  
  7. };  
  8.    
  9. static audio_pipe in_endpoint= {  
  10.          .mem                 = 0,  
  11.          .savenode         = 0,  
  12.          .fragsize  = 0,  
  13.          .fragstotal        = 0,  
  14.          .trans_state    = 0,  
  15. };  
  16.    
  1. void audio_init_endpoint(audio_pipe*endpoint, unsigned int pagesize,  
  2.                          unsigned int count)  
  3. {  
  4.          audio_resizemem_endpoint(endpoint,pagesize, count);  
  5.          spin_lock_init(&endpoint->lock);  
  6.          init_waitqueue_head(&endpoint->q_full);  
  7.          endpoint->avialable_couter= 0;  
  8.          endpoint->filter= NULL;  
  9.    
  10.          if(endpoint == &in_endpoint) {  
  11.                    init_audio_audiodma(endpoint,CODEC_RMODE);  
  12.                    //INIT_WORK(&endpoint->work, audio_in_endpoint_work);  
  13.                    endpoint->handle= handle_in_endpoint_work;  
  14.          }  
  15.          if(endpoint == &out_endpoint) {  
  16.                    init_audio_audiodma(endpoint,CODEC_WMODE);  
  17.                    //INIT_WORK(&endpoint->work, audio_out_endpoint_work);  
  18.                    endpoint->handle= handle_out_endpoint_work;  
  19.          }  
  20. }  
  21.    
  22. int audio_resizemem_endpoint(audio_pipe*endpoint, unsigned int pagesize,   
  23.                              unsigned int count)  
  24. {  
  25.          intret = init_audio_node(&endpoint->mem, pagesize, count,(int*)&endpoint->  
  26.                   fragmem_start);  
  27.          if(ret) {  
  28.                    endpoint->fragsize= pagesize;  
  29.                    endpoint->fragstotal= count;  
  30.                    endpoint->memsize= ret;  
  31.          }  
  32.          returnret;  
  33. }  

    我们进入audio_resizemem_endpoint分析内存的分配, 其中调用init_audio_node进行实质的
分配, 先介绍一下这里的变量意义,了解了这些意义,分析起来就得心应手了,迫不及待
了。

  1. JZCODEC_RW_BUFFER_SIZE           // 1 * 4096  
  2.  JZCODEC_RW_BUFFER_TOTAL;      // 4个buff  
  3.   
  4.          unsignedint     fact;                   //分配物理页的order  
  5.          audio_node      *pbuff;               //代表1个audio node  
  6.          audio_head      *phead;             //代表整个audiobuff 链表  
  7.          unsignedint     *mem;              //分配得到整个audiobuff的虚拟地址  
  8.          structlist_head *audio_wfree;       //freebuff 链表  
  9.          structlist_head *audio_wuse;        //usebuff 链表  
  10.          int    memsize;                                      //链表头+节点+audiobuff占用总空间  
  11.          int    datasize;                             //audiobuff占用的总空间  
  12.          int    headlistsize;                       //链表头+4个节点占用总空间  

  1. //Alloc memory first, to avail fail  
  2. datasize   = ALIGN_PAGE_SIZE(pagesize * count);  
  3. headlistsize      = ALIGN_PAGE_SIZE(count *sizeof(audio_node) + sizeof(audio_head));  
  4. memsize           = headlistsize + datasize;  
  5. fact           = get_order(memsize);  
  6.   
  7. mem= (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, fact);  

         首先获得整个audiobuff的大小 datasize, 再获得list_head + list_node * 4的大小
headlistsize, 然后计算总大小memsize, 用__get_free_pages分配连续的物理地址空间,这里
注意一下,返回的是物理地址。

  1. //Free old buffer  
  2. if(*memory) {  
  3.           phead       = (audio_head *)*memory;  
  4.           fact  = phead->fact;  
  5.           free_pages((unsignedlong)*memory, fact);  
  6.           *memory = NULL;  
  7. }  
  8. *memory= mem;  

         这里检查一下是不是有分配audiobuff, 如果有就释放掉,再将新分配的地址赋值给memory. 
在mixer的ioctl中SNDCTL_DSP_SETFRAGMENT也会重新分配大小的。

  1. phead                = (audio_head *)*memory;  
  2. phead->fact     = fact;  
  3. phead->listsize         = headlistsize;  
  4. phead->datasize      = datasize;  
  5.   
  6. audio_wuse     = &(phead->use);  
  7. audio_wfree    = &(phead->free);  
  8. INIT_LIST_HEAD(audio_wuse);  
  9. INIT_LIST_HEAD(audio_wfree);  

         接下来就是给listhead赋值,然后初始化audio_wuse和audio_wfree链表。

  1. pbuff= (audio_node *)((unsigned int)*memory + sizeof(audio_head));  
  2. *fragmem_start= (int)((unsigned int)*memory + headlistsize);  
  3. for(i = 0; i < count; i++) {  
  4.           pbuff->pBuf      = (unsigned int)*memory + headlistsize +pagesize * i;  
  5.           pbuff->phyaddr         = (unsigned int)virt_to_phys((void*)pbuff->pBuf);  
  6.           pbuff->start     = 0;  
  7.           pbuff->end        = 0;  
  8. DEBUG  
  9.           pbuff->pBufID  = i;  
  10.   
  11.           DPRINT_Q("audio_notebuffer[%d] = %x\n", i, (unsigned int)pbuff->pBuf);  
  12.           list_add(&pbuff->list,audio_wfree);  
  13.           pbuff++;  
  14. }  

     在这里将audio_wfree 链表全部初始化,注意此时wfree链表中有4个结点,wuse中

没有结点。

  1. if (ret) {  
  2.           endpoint->fragsize =pagesize;  
  3.           endpoint->fragstotal =count;  
  4.           endpoint->memsize = ret;  
  5. }  

     接下来返回到audio_resizemem_endpoint, 将endpoint结构赋值。

     Audio buff分配好后,再返回到audio_init_endpoint对DMA进行初始化, 这里根据传
入的是play或者record进行初始化,大致上是雷同的,这里只分析 replay的初始化

  1. if ((ch =jz_request_aic_dma(DMA_ID_I2S_TX,"audio dac", jz_i2s_dma_irq,IRQF_DISABLED,   
  2.     endpoint)) < 0) {  
  3.           printk(KERN_ERR "%s:can't reqeust DMA DAC channel.\n", __FUNCTION__);  
  4.           return -1;  
  5. }  
  6.   
  7. chan->io   = i;  
  8. chan->dev_id   = dev_id;  
  9. chan->dev_str = dev_str;  
  10. chan->fifo_addr        = CPHYSADDR(AIC_DR);  
  11.   
  12. switch (dev_id) {  
  13. case DMA_ID_AIC_TX:  
  14.           chan->mode     = DMA_AIC_TX_CMD_UNPACK | DMA_MODE_WRITE;  
  15.           chan->source   = DMAC_DRSR_RS_AICOUT;  
  16.           break;  
  17. case DMA_ID_AIC_RX:  
  18.           chan->mode     = DMA_32BIT_RX_CMD | DMA_MODE_READ;  
  19.           chan->source   = DMAC_DRSR_RS_AICIN;  
  20.           break;  
  21. default:  
  22.           printk("JZ AIC: %s:%d,need fix !!!\n", __FUNCTION__, __LINE__);  
  23.           BUG_ON(1);  
  24. }  

     调用jz_request_aic_dma函数注册DMA中断,填写chan的模式与地址填充,最后启动
AIC DMA的时钟,再返回到init_audio_replaydma中,初始化寄存器,DMA就初始化OK了。
至此整个AUDIO BUFF就初始化完成了。

播放流程分析

         先上图来说话:

        

          首先进入jz_audio_write函数, 里边有两个分支,根据模式进行区分的,第1种是使用
mmap方式传输的,由于我们要分析audio buff, 就不关注mmap方式了,各位如有想了解
的话,可以发与我讨论, , 下边就直接进入audiobuff 方式的
jz_audio_write_data函数分析。

  1. while(count >= pout_endpoint->fragsize) {  
  2.           bat_cnt= endpoint_put_userdata(pout_endpoint,  
  3.                                                &(buffer[usecount]),  
  4.                                                pout_endpoint->fragsize);  
  5.           //Prepare data success.  
  6.           if(bat_cnt > 0) {  
  7.                    usecount+= bat_cnt;  
  8.                    count-= bat_cnt;  
  9.                    DPRINT("bat_cnt= %d\n", bat_cnt);  
  10.           }  
  11.           //Perhaps non node is avialable.  
  12.           elseif (bat_cnt == 0) {  
  13.                    DPRINT("bat_cnt== 0\n");  
  14.                    break;  
  15.           }  
  16.           //Error occured.  
  17.           else{  
  18.                    //break and handle prepared data.  
  19.                    if(usecount > 0) {  
  20.                             DPRINT("bat_cnt< 0, usecount > 0\n");  
  21.                             break;  
  22.                    }  
  23.                    //Has not prepared any data and return error when prepared data.  
  24.                    else{  
  25.                             DPRINT("bat_cnt< 0, usecount == 0\n");  
  26.                             returnbat_cnt;  
  27.                    }  
  28.           }  
  29. }  

         首先检查传入音频块的大小,必须大于fragsize(4096),先来整体分析这个函数,然后
再进入内部分析细节,bat_cnt 根据返回值进行处理,返回0表明没有wfree节点,就直接
跳出,进行播放录音了。

  1. DPRINT("<<<);  
  2. node= endpoint_get_outnode(endpoint);  
  3. if(!node)  
  4.           return0;  
  5. if(copy_from_user((void *)node->pBuf, buffer, count)) {  
  6.           printk("JZI2S: copy_from_user failed !\n");  
  7.           return-EFAULT;  
  8. }  
  9. LEAVE();  
  10. returnendpoint_post_outnode(endpoint,node,count);  

          用endpoint_get_outnode获得一个wfreenode,获得一个后就从wfree list上删除掉当

前节点, 播放完成后就返回结点,这样就形成了一个环形缓冲区。

         最后返回到jz_audio_write_data, 最后如果当前while 循环结束后还有剩余的数据,就
重新做一次播放。

录音流程分析              

         由于播放和录音基本上是雷同的,故在此不做分析了,各位看官如想了解的话,欢迎与
我讨论,


         本文完。

阅读(3786) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~