統(tǒng)一術(shù)語
1.??????JZ?????? ???????????????????????????????????? 君正4760b mips
2.??????Audio buff ?????????????????????????? 就是整個list_head+ list_node *4 + 整個音頻區(qū)
3.??????APP???????????????????????????????????????? 應(yīng)用程序
整體使用框圖
Buff分配流程
首先在probe JZ mixer設(shè)備的時候進行初始化DMA及緩沖buff. ?在函數(shù)init_jz_i2s中調(diào)用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函數(shù),在這里先表一下in_endpoint和out_ endpoint結(jié)構(gòu)體,?初始化為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分析內(nèi)存的分配, 其中調(diào)用init_audio_node進行實質(zhì)的
分配, 先介紹一下這里的變量意義,了解了這些意義,分析起來就得心應(yīng)手了,迫不及待
了。
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;??????????????????????????????????????//鏈表頭+節(jié)點+audiobuff占用總空間 ??
int????datasize;?????????????????????????????//audiobuff占用的總空間 ??
int????headlistsize;???????????????????????//鏈表頭+4個節(jié)點占用總空間??
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; //鏈表頭+節(jié)點+audiobuff占用總空間int datasize; //audiobuff占用的總空間int headlistsize; //鏈表頭+4個節(jié)點占用總空間
//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分配連續(xù)的物理地址空間,這里
注意一下,返回的是物理地址。
//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?
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個結(jié)點,wuse中
沒有結(jié)點。
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結(jié)構(gòu)賦值。
Audio buff分配好后,再返回到audio_init_endpoint對DMA進行初始化, 這里根據(jù)傳
入的是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);}
調(diào)用jz_request_aic_dma函數(shù)注冊DMA中斷,填寫chan的模式與地址填充,最后啟動
AIC DMA的時鐘,再返回到init_audio_replaydma中,初始化寄存器,DMA就初始化OK了。
至此整個AUDIO BUFF就初始化完成了。
播放流程分析
先上圖來說話:
首先進入jz_audio_write函數(shù), 里邊有兩個分支,根據(jù)模式進行區(qū)分的,第1種是使用
mmap方式傳輸?shù)?,由于我們要分析audio buff, 就不關(guān)注mmap方式了,各位如有想了解
的話,可以發(fā)與我討論, dyronchina@gmail.com, 下邊就直接進入audiobuff 方式的
jz_audio_write_data函數(shù)分析。
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_cnt0,?usecount?>?0\n");??
break;??
}??
//Has?not?prepared?any?data?and?return?error?when?prepared?data. ??
else{??
DPRINT("bat_cnt0,?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),先來整體分析這個函數(shù),然后
再進入內(nèi)部分析細節(jié),bat_cnt 根據(jù)返回值進行處理,返回0表明沒有wfree節(jié)點,就直接
跳出,進行播放錄音了。
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上刪除掉當(dāng)
前節(jié)點, 播放完成后就返回結(jié)點,這樣就形成了一個環(huán)形緩沖區(qū)。
最后返回到j(luò)z_audio_write_data, 最后如果當(dāng)前while 循環(huán)結(jié)束后還有剩余的數(shù)據(jù),就
錄音流程分析??????????????
由于播放和錄音基本上是雷同的,故在此不做分析了
重新做一次播放。
?
評論
查看更多