Linux音频OSS的buffer分析

Linux音频OSS的buffer分析,第1张

统一术语

1.      JZ                                            君正4760b mips

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

3.      APP                                         应用程序

整体使用框图

Linux音频OSS的buffer分析,第2张

Buff分配流程

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

fragsize= JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;  

fragstotal= JZCODEC_RW_BUFFER_TOTAL;  

audio_init_endpoint(&out_endpoint,fragsize, fragstotal);  

audio_init_endpoint(&in_endpoint,fragsize, fragstotal);  

fragsize= JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE;fragstotal= JZCODEC_RW_BUFFER_TOTAL;audio_init_endpoint(&out_endpoint,fragsize, fragstotal);audio_init_endpoint(&in_endpoint,fragsize, fragstotal);

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

staTIc audio_pipe out_endpoint = {  

.mem                 = 0,  

.savenode         = 0,  

.fragsize  = 0,  

.fragstotal        = 0,  

.trans_state    = 0,  

};  

staTIc audio_pipe in_endpoint= {  

.mem                 = 0,  

.savenode         = 0,  

.fragsize  = 0,  

.fragstotal        = 0,  

.trans_state    = 0,  

};  

staTIc audio_pipe out_endpoint = {.mem = 0,.savenode = 0,.fragsize = 0,.fragstotal = 0,.trans_state = 0,};staTIc audio_pipe in_endpoint= {.mem = 0,.savenode = 0,.fragsize = 0,.fragstotal = 0,.trans_state = 0,};

void audio_init_endpoint(audio_pipe*endpoint, unsigned int pagesize,  

unsigned int count)  

{  

audio_resizemem_endpoint(endpoint,pagesize, count);  

spin_lock_init(&endpoint->lock);  

init_waitqueue_head(&endpoint->q_full);  

endpoint->avialable_couter= 0;  

endpoint->filter= NULL;  

if(endpoint == &in_endpoint) {  

init_audio_audiodma(endpoint,CODEC_RMODE);  

//INIT_WORK(&endpoint->work, audio_in_endpoint_work);   

endpoint->handle= handle_in_endpoint_work;  

}  

if(endpoint == &out_endpoint) {  

init_audio_audiodma(endpoint,CODEC_WMODE);  

//INIT_WORK(&endpoint->work, audio_out_endpoint_work);   

endpoint->handle= handle_out_endpoint_work;  

}  

}  

int audio_resizemem_endpoint(audio_pipe*endpoint, unsigned int pagesize,   

unsigned int count)  

{  

intret = init_audio_node(&endpoint->mem, pagesize, count,(int*)&endpoint->  

fragmem_start);  

if(ret) {  

endpoint->fragsize= pagesize;  

endpoint->fragstotal= count;  

endpoint->memsize= ret;  

}  

returnret;  

}  

void audio_init_endpoint(audio_pipe*endpoint, unsigned int pagesize,unsigned int count){audio_resizemem_endpoint(endpoint,pagesize, count);spin_lock_init(&endpoint->lock);init_waitqueue_head(&endpoint->q_full);endpoint->avialable_couter= 0;endpoint->filter= NULL;if(endpoint == &in_endpoint) {init_audio_audiodma(endpoint,CODEC_RMODE);//INIT_WORK(&endpoint->work, audio_in_endpoint_work);endpoint->handle= handle_in_endpoint_work;}if(endpoint == &out_endpoint) {init_audio_audiodma(endpoint,CODEC_WMODE);//INIT_WORK(&endpoint->work, audio_out_endpoint_work);endpoint->handle= handle_out_endpoint_work;}}int audio_resizemem_endpoint(audio_pipe*endpoint, unsigned int pagesize,unsigned int count){intret = init_audio_node(&endpoint->mem, pagesize, count,(int*)&endpoint->fragmem_start);if(ret) {endpoint->fragsize= pagesize;endpoint->fragstotal= count;endpoint->memsize= ret;}returnret;}

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

JZCODEC_RW_BUFFER_SIZE           // 1 * 4096   

JZCODEC_RW_BUFFER_TOTAL;      // 4个buff   

unsignedint     fact;                   //分配物理页的order   

audio_node      *pbuff;               //代表1个audio node   

audio_head      *phead;             //代表整个audiobuff 链表   

unsignedint     *mem;              //分配得到整个audiobuff的虚拟地址   

structlist_head *audio_wfree;       //freebuff 链表   

structlist_head *audio_wuse;        //usebuff 链表   

int    memsize;                                      //链表头+节点+audiobuff占用总空间   

int    datasize;                             //audiobuff占用的总空间   

int    headlistsize;                       //链表头+4个节点占用总空间  

JZCODEC_RW_BUFFER_SIZE // 1 * 4096JZCODEC_RW_BUFFER_TOTAL; // 4个buffunsignedint fact; //分配物理页的orderaudio_node *pbuff; //代表1个audio nodeaudio_head *phead; //代表整个audiobuff 链表unsignedint *mem; //分配得到整个audiobuff的虚拟地址structlist_head *audio_wfree; //freebuff 链表structlist_head *audio_wuse; //usebuff 链表int memsize; //链表头+节点+audiobuff占用总空间int datasize; //audiobuff占用的总空间int headlistsize; //链表头+4个节点占用总空间

//Alloc memory first, to avail fail   

datasize   = ALIGN_PAGE_SIZE(pagesize * count);  

headlistsize      = ALIGN_PAGE_SIZE(count *sizeof(audio_node) + sizeof(audio_head));  

memsize           = headlistsize + datasize;  

fact           = get_order(memsize);  

mem= (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, fact);  

//Alloc memory first, to avail faildatasize = ALIGN_PAGE_SIZE(pagesize * count);headlistsize = ALIGN_PAGE_SIZE(count *sizeof(audio_node) + sizeof(audio_head));memsize = headlistsize + datasize;fact = get_order(memsize);mem= (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, fact);

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

//Free old buffer   

if(*memory) {  

phead       = (audio_head *)*memory;  

fact  = phead->fact;  

free_pages((unsignedlong)*memory, fact);  

*memory = NULL;  

}  

*memory= mem;  

//Free old bufferif(*memory) {phead = (audio_head *)*memory;fact = phead->fact;free_pages((unsignedlong)*memory, fact);*memory = NULL;}*memory= mem;

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

phead                = (audio_head *)*memory;  

phead->fact     = fact;  

phead->listsize         = headlistsize;  

phead->datasize      = datasize;  

audio_wuse     = &(phead->use);  

audio_wfree    = &(phead->free);  

INIT_LIST_HEAD(audio_wuse);  

INIT_LIST_HEAD(audio_wfree);  

phead = (audio_head *)*memory;phead->fact = fact;phead->listsize = headlistsize;phead->datasize = datasize;audio_wuse = &(phead->use);audio_wfree = &(phead->free);INIT_LIST_HEAD(audio_wuse);INIT_LIST_HEAD(audio_wfree);

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

pbuff= (audio_node *)((unsigned int)*memory + sizeof(audio_head));  

*fragmem_start= (int)((unsigned int)*memory + headlistsize);  

for(i = 0; i < count; i++) {  

pbuff->pBuf      = (unsigned int)*memory + headlistsize +pagesize * i;  

pbuff->phyaddr         = (unsigned int)virt_to_phys((void*)pbuff->pBuf);  

pbuff->start     = 0;  

pbuff->end        = 0;  

DEBUG  

pbuff->pBufID  = i;  

DPRINT_Q("audio_notebuffer[%d] = %x\n", i, (unsigned int)pbuff->pBuf);  

list_add(&pbuff->list,audio_wfree);  

pbuff++;  

}  

pbuff= (audio_node *)((unsigned int)*memory + sizeof(audio_head));*fragmem_start= (int)((unsigned int)*memory + headlistsize);for(i = 0; i < count; i++) {pbuff->pBuf = (unsigned int)*memory + headlistsize +pagesize * i;pbuff->phyaddr = (unsigned int)virt_to_phys((void*)pbuff->pBuf);pbuff->start = 0;pbuff->end = 0;#ifdef Q_DEBUGpbuff->pBufID = i;#endifDPRINT_Q("audio_notebuffer[%d] = %x\n", i, (unsigned int)pbuff->pBuf);list_add(&pbuff->list,audio_wfree);pbuff++;}

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

没有结点。

if (ret) {  

endpoint->fragsize =pagesize;  

endpoint->fragstotal =count;  

endpoint->memsize = ret;  

}  

if (ret) {endpoint->fragsize =pagesize;endpoint->fragstotal =count;endpoint->memsize = ret;}

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

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

if ((ch =jz_request_aic_dma(DMA_ID_I2S_TX,"audio dac", jz_i2s_dma_irq,IRQF_DISABLED,   

endpoint)) < 0) {  

printk(KERN_ERR "%s:can't reqeust DMA DAC channel.\n", __FUNCTION__);  

return -1;  

}  

chan->io   = i;  

chan->dev_id   = dev_id;  

chan->dev_str = dev_str;  

chan->fifo_addr        = CPHYSADDR(AIC_DR);  

switch (dev_id) {  

case DMA_ID_AIC_TX:  

chan->mode     = DMA_AIC_TX_CMD_UNPACK | DMA_MODE_WRITE;  

chan->source   = DMAC_DRSR_RS_AICOUT;  

break;  

case DMA_ID_AIC_RX:  

chan->mode     = DMA_32BIT_RX_CMD | DMA_MODE_READ;  

chan->source   = DMAC_DRSR_RS_AICIN;  

break;  

default:  

printk("JZ AIC: %s:%d,need fix !!!\n", __FUNCTION__, __LINE__);  

BUG_ON(1);  

}  

if ((ch =jz_request_aic_dma(DMA_ID_I2S_TX,"audio dac", jz_i2s_dma_irq,IRQF_DISABLED,endpoint)) < 0) {printk(KERN_ERR "%s:can't reqeust DMA DAC channel.\n", __FUNCTION__);return -1;}chan->io = i;chan->dev_id = dev_id;chan->dev_str = dev_str;chan->fifo_addr = CPHYSADDR(AIC_DR);switch (dev_id) {case DMA_ID_AIC_TX:chan->mode = DMA_AIC_TX_CMD_UNPACK | DMA_MODE_WRITE;chan->source = DMAC_DRSR_RS_AICOUT;break;case DMA_ID_AIC_RX:chan->mode = DMA_32BIT_RX_CMD | DMA_MODE_READ;chan->source = DMAC_DRSR_RS_AICIN;break;default:printk("JZ AIC: %s:%d,need fix !!!\n", __FUNCTION__, __LINE__);BUG_ON(1);}

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

播放流程分析

先上图来说话:

Linux音频OSS的buffer分析,第3张

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

while(count >= pout_endpoint->fragsize) {  

bat_cnt= endpoint_put_userdata(pout_endpoint,  

&(buffer[usecount]),  

pout_endpoint->fragsize);  

//Prepare data success.   

if(bat_cnt > 0) {  

usecount+= bat_cnt;  

count-= bat_cnt;  

DPRINT("bat_cnt= %d\n", bat_cnt);  

}  

//Perhaps non node is avialable.   

elseif (bat_cnt == 0) {  

DPRINT("bat_cnt== 0\n");  

break;  

}  

//Error occured.   

else{  

//break and handle prepared data.   

if(usecount > 0) {  

DPRINT("bat_cnt< 0, usecount > 0\n");  

break;  

}  

//Has not prepared any data and return error when prepared data.   

else{  

DPRINT("bat_cnt< 0, usecount == 0\n");  

returnbat_cnt;  

}  

}  

}  

while(count >= pout_endpoint->fragsize) {bat_cnt= endpoint_put_userdata(pout_endpoint,&(buffer[usecount]),pout_endpoint->fragsize);//Prepare data success.if(bat_cnt > 0) {usecount+= bat_cnt;count-= bat_cnt;DPRINT("bat_cnt= %d\n", bat_cnt);}//Perhaps non node is avialable.elseif (bat_cnt == 0) {DPRINT("bat_cnt== 0\n");break;}//Error occured.else{//break and handle prepared data.if(usecount > 0) {DPRINT("bat_cnt< 0, usecount > 0\n");break;}//Has not prepared any data and return error when prepared data.else{DPRINT("bat_cnt< 0, usecount == 0\n");returnbat_cnt;}}}

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

DPRINT("<<<);  

node= endpoint_get_outnode(endpoint);  

if(!node)  

return0;  

if(copy_from_user((void *)node->pBuf, buffer, count)) {  

printk("JZI2S: copy_from_user failed !\n");  

return-EFAULT;  

}  

LEAVE();  

returnendpoint_post_outnode(endpoint,node,count);  

DPRINT("<<

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

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

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

录音流程分析              

由于播放和录音基本上是雷同的,故在此不做分析了



 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/dianzi/2610973.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-10
下一篇 2022-08-10

发表评论

登录后才能评论

评论列表(0条)

保存