免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
writing

PCM信息運(yùn)行時(shí)指針

當(dāng)打開一個(gè)一個(gè)PCM子流的時(shí)候,PCM運(yùn)行時(shí)實(shí)例就會(huì)分配給這個(gè)子流。這個(gè)指針可以通過substream->runtime獲得。運(yùn)行時(shí)指針擁有多種信息:hw_paramssw_params的配置的拷貝,緩沖區(qū)指針,mmap記錄,自旋鎖等等。幾乎你想控制PCM的所有信息都可以在這里得到。

Struct_snd_pcm_runtime {

/*狀態(tài)*/

structsnd_pcm_substream *trigger_master;

snd_timestamp_ttrigger_tstamp;/*觸發(fā)時(shí)間戳*/、

intoverrange;

snd_pcm_uframes_tavail_max;

snd_pcm_uframes_thw_ptr_base /*緩沖區(qū)復(fù)位時(shí)的位置*/

snd_pcm_uframes_thw_ptr_interrupt;/*中斷時(shí)的位置*/

/*硬件參數(shù)*/

snd_pcm_access_taccess; /*存取模式*/

snd_pcm_format_tformat; /*SNDRV_PCM_FORMAT_* */

snd_pcm_subformat_tsubformat; /*子格式*/

unsignedint rate; /*rate in HZ*/

unsignedint channels; /*通道*/

snd_pcm_uframe_tperiod_size; /*周期大小*/

unsignedint periods /*周期數(shù)*/

snd_pcm_uframes_tbuffer_size; /*緩沖區(qū)大小*/

unsignedint tick_time; /*tick time滴答時(shí)間*/

snd_pcm_uframes_tmin_align; /*格式對(duì)應(yīng)的最小對(duì)齊*/

size_tbyte_align;

unsignedint frame_bits;

unsignedint sample_bits;

unsignedint info;

unsignedint rate_num;

unsignedint rate_den;

/*軟件參數(shù)*/

structtimespec tstamp_mode; /*mmap時(shí)間戳被更新*/

unsignedint sleep_min; /*睡眠的最小節(jié)拍數(shù)*/

snd_pcm_uframes_txfer_align; /*xfer的大小需要是成倍數(shù)的*/

snd_pcm_uframes_tstart_threshold;

snd_pcm_uframes_tstop_threshold;

snd_pcm_uframes_tsilence_threshold;/*silence填充閥值*/

snd_pcm_uframes_tsilence_size; /*silence填充大小*/

snd_pcm_uframes_tboundary;

snd_pcm_uframes_tsilenced_start;

snd_pcm_uframes_tsilenced_size;

snd_pcm_sync_id_tsync; /*硬件同步ID*/

/*mmap*/

volatilestruct snd_pcm_mmap_status *status;

volatilestruct snd_pcm_mmap_control *control;

atomic_tmmap_count;

/*/調(diào)度*/

spinlock_tlock;

wait_queue_head_tsleep;

structtimer_list tick_timer;

structfasync_struct *fasync;

/*私有段*/

void*private_data;

void(*private_free)(struct snd_pcm_runtime *runtime);


/*硬件描述*/

structsnd_pcm_hardware hw;

structsnd_pcm_hw_constraints hw_constraints;

/*中斷的回調(diào)函數(shù)*/

void(*transfer_ack_begin)(struct snd_pcm_substream *substream);

void(*transfer_ack_end)(struct snd_pcm_substream *substream);

/*定時(shí)器*/

unsignedint timer_resolution; /*timer resolution*/

/*DMA*/

unsignedchar *dma_area;

dma_addr_tdma_addr; /*總線物理地址*/

size_tdma_bytes; /*DMA區(qū)域大小*/

structsnd_dma_buffer *dma_buffer_p; /*分配的緩沖區(qū)*/

#ifdefined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)

structsnd_pcm_oss_runtime oss;

#endif

};

snd_pcm_runtime對(duì)于大部分的驅(qū)動(dòng)程序操作集的函數(shù)來說是只讀的。僅僅PCM中間層可以改變/更新這些信息。但是硬件描述,中斷響應(yīng),DMA緩沖區(qū)信息和私有數(shù)據(jù)是例外的。此外,假如你采用標(biāo)準(zhǔn)的內(nèi)存分配函數(shù)snd_pcm_lib_malloc_pages(),就不再需要自己設(shè)定DMA緩沖區(qū)信息了。

下面幾章,會(huì)對(duì)上面記錄的現(xiàn)實(shí)進(jìn)行解釋。

硬件描述

硬件描述(structsnd_pcm_hardware)包含了基本硬件配置的定義。如前面所述,你需要在open的時(shí)候?qū)λ鼈冞M(jìn)行定義。注意runtime實(shí)例擁有這個(gè)描述符的拷貝而不是已經(jīng)存在的描述符的指針。換句話說,在open函數(shù)中,你可以根據(jù)需要修改描述符的拷貝。例如,假如在一些聲卡上最大的通道數(shù)是1,你仍然可以使用相同的硬件描述符,同時(shí)在后面你可以改變最大通道數(shù)。

Structsnd_pcm_runtime *runtime = substream->runtime;

....

runtime->hw= snd_mychip_playback_hw; /*通用定義*/

if(chip->model == VERY_OLD_ONE)

runtime->hw.channels_max= 1;


典型的硬件描述如下:

staticstruct snd_pcm_hardware snd_mychip_playback_hw = {

.info= (SNDRV_PCM_INFO_MMAP |

SNDRV_PCM_INFO_INTERLEAVED|

SNDRV_PCM_INFO_BLOCK_TRANSFER|

SNDRV_PCM_INFO_MMAP_VALID),

.formats= SNDRV_PCM_FORMAT_S16_LE,

.rates= SNDRV_PCM_RATE_8000_48000,

.rate_min= 8000,

.rate_max= 48000,

.channels_min= 2,

.channels_max= 2,

.buffer_bytes_max= 32768,

.period_bytes_min= 4096,

.period_bytes_max= 32768,

.periods_min= 1,

.periods_max= 1024,

};

info字段包含pcm的類型和能力。位標(biāo)志在中定義,如:SNDRV_PCM_INFO_XXX。這里,你必須定義mmap是否支持和支持哪種interleaved格式。當(dāng)支持mmap的時(shí)候,應(yīng)當(dāng)設(shè)定SNDRV_PCM_INFO_MMAP。當(dāng)硬件支持interleavedno-interleaved格式的時(shí)候,要設(shè)定SNDRV_PCM_INFO_INTERLEAVEDSNDRV_PCM_INFO_NONINTERLEAVED標(biāo)志位。假如兩者都支持,你也可以都設(shè)定。

如上面的例子,MMAP_VALIDBLOCK_TRANSFER都是針對(duì)OSSmmap模式,通常情況它們都要設(shè)定。當(dāng)然,MMAP_VALID僅僅當(dāng)mmap真正被支持的時(shí)候才會(huì)被設(shè)定。

其他一些標(biāo)志位是SNDRV_PCM_INFO_PAUSESNDRV_PCM_INFO_RESUME。SNDRV_PCM_INFO_PAUSE標(biāo)志位意思是pcm支持“暫?!辈僮鳎?/span>SNDRV_PCM_INFO_RESUME表示是pcm支持“掛起/恢復(fù)”操作。假如PAUSE標(biāo)志位被設(shè)定,trigger函數(shù)就必須執(zhí)行一個(gè)對(duì)應(yīng)的(暫停按下/釋放)命令。就算沒有RESUME標(biāo)志位,也可以被定義掛起/恢復(fù)觸發(fā)命令。

更詳細(xì)的部分請(qǐng)參考“電源管理”一章。

當(dāng)PCM子系統(tǒng)能被同步(如:播放流和錄音流的開始/結(jié)束的同步)的時(shí)候,你可以設(shè)定SNDRV_PCM_INFO_SYNC_START標(biāo)志位。在這種情況下,你必須在trigger函數(shù)中檢查PCM子流鏈。下面的章節(jié)會(huì)想笑介紹這個(gè)部分。

formats字段包含了支持格式的標(biāo)志位(SNDRV_PCM_FMTBIT_XXX)。假如硬件支持超過一個(gè)的格式,需要對(duì)位標(biāo)志位進(jìn)行“或”運(yùn)算。上面的例子就是支持16bit有符號(hào)的小端格式。

rates字段包含了支持的采樣率(SNDRV_PCM_RATE_XXX)。當(dāng)聲卡支持多種采樣率的時(shí)候,應(yīng)該附加一個(gè)CONTINUOUS標(biāo)志。已經(jīng)預(yù)先定義的典型的采樣率,假如你的聲卡支持不常用的采樣率,你需要加入一個(gè)KNOT標(biāo)志,同時(shí)手動(dòng)的對(duì)硬件進(jìn)行控制(稍后解釋)。

rate_minrate_max定義了最小和最大的采樣率。應(yīng)該和采樣率相對(duì)應(yīng)。

channel_minchannel_max定義了最大和最小的通道,以前可能你已看到。

buffer_bytes_max定義了以字節(jié)為單位的最大的緩沖區(qū)大小。這里沒有buffer_bytes_min字段,因?yàn)樗梢酝ㄟ^最小的period大小和最小的period數(shù)量計(jì)算得出。同時(shí),period_bytes_min和定義的最小和最大的periodperiods_maxperiods_min定義了最大和最小的periods。

period信息和OSS中的fragment相對(duì)應(yīng)。period定義了PCM中斷產(chǎn)生的周期。這個(gè)周期非常依賴硬件。一般來說,一個(gè)更短的周期會(huì)提供更多的中斷和更多的控制。如在錄音中,周期大小定義了輸入延遲,另外,整個(gè)緩存區(qū)大小也定義了播放的輸出延遲。

字段fifo_size。這個(gè)主要是和硬件的FIFO大小有關(guān),但是目前驅(qū)動(dòng)中或alsa-lib中都沒有使用。所以你可以忽略這個(gè)字段。


PCM配置

OK,讓我們?cè)俅位氐?/span>PCM運(yùn)行時(shí)記錄。最經(jīng)常涉及的運(yùn)行時(shí)實(shí)例中的記錄就是PCM配置了。PCM可以讓應(yīng)用程序通過alsa-lib發(fā)送hw_params來配置。有很多字段都是從hw_paramssw_params結(jié)構(gòu)中拷貝過來的。例如:format保持了應(yīng)用程序選擇的格式類型,這個(gè)字段包含了enumSNDRV_PCM_FORMAT_XXX。

其中要注意的一個(gè)就是,配置的bufferperiod大小被放在運(yùn)行時(shí)記錄的“frame”中。在ALSA里,1frame=channel*samples-size。為了在幀和字節(jié)之間轉(zhuǎn)換,你可以用下面的函數(shù),frames_to_bytes()bytes_to_frames()

period_bytes= frames_to_bytes(runtime,runtime->period_size);

同樣,許多的軟件參數(shù)(sw_params)也存放在frames字段里面。請(qǐng)檢查這個(gè)字段的類型。snd_pcm_uframes_t是作為表示frames的無符號(hào)整數(shù),而snd_pcm_sframes_t是作為表示frames的有符號(hào)整數(shù)。


DMA緩沖區(qū)信息

DMA緩沖區(qū)通過下面4個(gè)字段定義,dma_area,dma_addr,dma_bytes,dma_private。其中dma_area是緩沖區(qū)的指針(邏輯地址)。可以通過memcopy來向這個(gè)指針來操作數(shù)據(jù)。dma_addr是緩沖區(qū)的物理地址。這個(gè)字段僅僅當(dāng)緩沖區(qū)是線性緩存的時(shí)候才要特別說明。dma_bytes是緩沖區(qū)的大小。dma_private是被ALSADMA管理用到的。

如果采用ALSA的標(biāo)準(zhǔn)內(nèi)存分配函數(shù)snd_pcm_lib_mallock_pages()分配內(nèi)存,那些字段會(huì)被ALSA的中間層設(shè)定,你不能自己改變他們,可以讀取而不能寫入。而如果你想自己分配內(nèi)存,你就需要在hw_params回調(diào)里面自己管理它們。當(dāng)內(nèi)存被mmap之后,你至少要設(shè)定dma_bytesdma_area。但是如果你的驅(qū)動(dòng)不支持mmap,這些字段就不必一定設(shè)定.dma_addr也不是強(qiáng)制的,你也可以根據(jù)靈活來用dma_private


運(yùn)行狀態(tài)

可以通過runtime->status來獲得運(yùn)行狀態(tài)。它是一個(gè)指向snd_pcm_mmap_status記錄的指針。例如,可以通過runtime->status->hw_ptr來得到當(dāng)前DMA硬件指針。

可以通過runtime->control來查看DMA程序的指針,它是指向snd_pcm_mmap_control記錄。但是,不推薦直接存取這些數(shù)據(jù)。


私有數(shù)據(jù)

可以為子流分配一個(gè)記錄,讓它保存在runtime->private_data里面。通常可以在open函數(shù)中做。不要和pcm->private_data混攪了,pcm->private_data主要是在創(chuàng)建PCM的時(shí)候指向chip實(shí)例,而runtime->private_data是在PCMopen的時(shí)候指向一個(gè)動(dòng)態(tài)數(shù)據(jù)。

Struct intsnd_xxx_open(struct snd_pcm_substream *substream)

{

structmy_pcm_data *data;

data =kmalloc(sizeof(*data),GFP_KERNEL);

substream->runtime->private_data= data;

....

}

上述分配的對(duì)象要在close函數(shù)中釋放。


中斷函數(shù)

transfer_ack_begin()transfer_ack_end()將會(huì)在snd_pcm_period_elapsed()的開始和結(jié)束。

操作函數(shù)

現(xiàn)在讓我來詳細(xì)介紹每個(gè)pcm的操作函數(shù)吧(ops)。通常每個(gè)回調(diào)函數(shù)成功的話返回0,出錯(cuò)的話返回一個(gè)帶錯(cuò)誤碼的負(fù)值,如:-EINVAL。

每個(gè)函數(shù)至少要有一個(gè)snd_pcm_substream指針變量。主要是為了從給定的子流實(shí)例中得到chip記錄,你可以采用下面的宏。

Int xxx(){

structmychip *chip = snd_pcm_substream_chip(substream);

....

}

open函數(shù)

static intsnd_xxx_open(struct snd_pcm_substream *substream);

當(dāng)打開一個(gè)pcm子流的時(shí)候調(diào)用。

在這里,你至少要初始化runtime->hw記錄。典型應(yīng)用如下:

static intsnd_xxx_open(struct snd_pcm_substream *substream)

{

structmychip *chip = snd_pcm_substream_chip(substream);

structsnd_pcm_runtime *runtime = substream->runtime;

runtime->hw= snd_mychip_playback_hw;

return0;

}

其中snd_mychip_playback_hw是預(yù)先定義的硬件描述。

close函數(shù)

static intsnd_xxx_close(struct snd_pcm_substream *substream)

顯然這是在pcm子流被關(guān)閉的時(shí)候調(diào)用。

所有在open的時(shí)候被分配的pcm子流的私有的實(shí)例都應(yīng)該在這里被釋放。

Static intsnd_xxx_close(struct snd_pcm_substream *substream)

{

...

kfree(substream->runtime->private_data);

...

}

ioctl函數(shù)

這個(gè)函數(shù)主要是完成一些pcmioctl的特殊功能。但是通常你可以采用通用的ioctl函數(shù)snd_pcm_lib_ioctl。

hw_params函數(shù)

static intsnd_xxx_hw_params(struct snd_pcm_substream *substream,

structsnd_pcm_substream *hw_params);

這個(gè)函數(shù)和hw_free函數(shù)僅僅在ALSA0.9.X版本出現(xiàn)。

當(dāng)pcm子流中已經(jīng)定義了緩沖區(qū)大小,period大小,格式等的時(shí)候,應(yīng)用程序可以通過這個(gè)函數(shù)來設(shè)定硬件參數(shù)。

很多的硬件設(shè)定都要在這里完成,包括分配內(nèi)存。

設(shè)定的參數(shù)可以通過params_xxx()宏得到。對(duì)于分配內(nèi)存,可以采用下面一個(gè)有用的函數(shù),

snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(hw_params));

snd_pcm_lib_malloc_pages()僅僅當(dāng)DMA緩沖區(qū)已經(jīng)被預(yù)分配之后才可以用。參考“緩存區(qū)類型”一章獲得更詳細(xì)的細(xì)節(jié)。

注意這個(gè)和prepare函數(shù)會(huì)在初始化當(dāng)中多次被調(diào)用。例如,OSSemulation可能在每次通過ioctl改變的時(shí)候都要調(diào)用這些函數(shù)。

因而,千萬不要對(duì)一個(gè)相同的內(nèi)存分配多次,可能會(huì)導(dǎo)致內(nèi)存的漏洞!而上面的幾個(gè)函數(shù)是可以被多次調(diào)用的,如果它已經(jīng)被分配了,它會(huì)先自動(dòng)釋放之前的內(nèi)存。

另外一個(gè)需要注意的是,這些函數(shù)都不是原子的(可以被調(diào)到)。這個(gè)是非常重要的,因?yàn)?/font>trigger函數(shù)是原子的(不可被調(diào)度)。因此,mutex和其他一些和調(diào)度相關(guān)的功能函數(shù)在trigger里面都不需要了。具體參看“原子操作”一節(jié)。

hw_free函數(shù)

static intsnd_xxx_hw_free(struct snd_pcm_substream *substream);

這個(gè)函數(shù)可以是否通過hw_params分配的資源。例如:通過如下函數(shù)釋放那些通過snd_pcm_lib_malloc_pages()申請(qǐng)的緩存。

snd_pcm_lib_free_pages(substream)

這個(gè)函數(shù)要在close調(diào)用之前被調(diào)用。同時(shí),它也可以被多次調(diào)用。它也會(huì)知道資源是否已經(jīng)被分配。


Prepare函數(shù)

static intsnd_xxx_prepare(struct snd_pcm_substream *substream);

當(dāng)pcm“準(zhǔn)備好了”的時(shí)候調(diào)用這個(gè)函數(shù)??梢栽谶@里設(shè)定格式類型,采樣率等等。和hw_params不同的是,每次調(diào)用snd_pcm_prepare()的時(shí)候都要調(diào)用prepare函數(shù)。

注意最近的版本prepare變成了非原子操作的了。這個(gè)函數(shù)中,你要做一些調(diào)度安全性策略。

在下面的函數(shù)中,你會(huì)涉及到runtime記錄的值(substream->runtime)。例如:得到當(dāng)前的采樣率,格式或聲道,可以分別存取runtime->rate,runtime->format,runtime->channels。分配的內(nèi)存的地址放到runtime->dma_area中,內(nèi)存和period大小分別保存在runtime->buffer_sizeruntime->period_size中。

要注意在每次設(shè)定的時(shí)候都有可能多次調(diào)用這些函數(shù)。

trigger函數(shù)

static intsnd_xxx_trigger(struct snd_pcm_substream *substream, int cmd);

當(dāng)pcm開始,停止或暫停的時(shí)候都會(huì)調(diào)用這個(gè)函數(shù)。

具體執(zhí)行那個(gè)操作主要是根據(jù)第二個(gè)參數(shù),在中聲明了SNDRV_PCM_TRIGGER_XXX。最少STARTSTOP的命令必須定義的。

switch(cmd){

caseSNDRV_PCM_TRIGGER_START:

//啟動(dòng)PCM引擎

break;

caseSNDRV_PCM_TRIGGER_STOP:

//停止PCM引擎

break;

default:

break;

}

當(dāng)pcm支持暫停操作(在hareware表里面有這個(gè)),也必須處理PAUSE_PAUSEPAUSE_RELEASE命令。前者是暫停命令,后者是重新播放命令。

假如pcm支持掛起/恢復(fù)操作,不管是全部或部分的掛起/恢復(fù)支持,都要處理SUSPENDRESUME命令。這些命令主要是在電源狀態(tài)改變的時(shí)候需要,通常它們和STOPSTART命令放到一起。具體參看“電源管理”一章。

如前面提到的,這個(gè)操作上原子的。不要在調(diào)用這些函數(shù)的時(shí)候進(jìn)入睡眠。而trigger函數(shù)要盡量小,甚至僅僅觸發(fā)DMA。另外的工作如初始化hw_paramsprepare應(yīng)該在之前做好。

pointer函數(shù)

staticsnd_pcm_uframes_t snd_xxx_pointer(struct snd_pcm_substream*substream);

PCM中間層通過調(diào)用這個(gè)函數(shù)來獲得緩沖區(qū)中的硬件位置。返回值需要以為frames為單位(在ALSA0.5.X是以字節(jié)為單位的),范圍從0buffer_size-1。

一般情況下,在中斷程序中,調(diào)用snd_pcm_period_elapsed()的時(shí)候,在pcm中間層在在更新buffer的程序中調(diào)用它。然后,pcm中間層會(huì)更新指針位置和計(jì)算可用的空間,然后喚醒那些等待的線程。

這個(gè)函數(shù)也是原子的。


Copysilence函數(shù)

這些函數(shù)不是必須的,同時(shí)在大部分的情況下是被忽略的。這些函數(shù)主要應(yīng)用在硬件內(nèi)存不在正常的內(nèi)存空間的時(shí)候。一些聲卡有一些沒有被影射的自己的內(nèi)存。在這種情況下,你必須把內(nèi)存?zhèn)鞯接布膬?nèi)存空間去?;蛘?,緩沖區(qū)在物理內(nèi)存和虛擬內(nèi)存中都是不連續(xù)的時(shí)候就需要它們了。

假如定義了copysilence,就可以做copyset-silence的操作了。更詳細(xì)的描述請(qǐng)參考“緩沖區(qū)和內(nèi)存管理”一章。


Ack函數(shù)

這個(gè)函數(shù)也不是必須的。當(dāng)在讀寫操作的時(shí)候更新appl_ptr的時(shí)候會(huì)調(diào)用它。一些類似于emu10k1-fxcs46xx的驅(qū)動(dòng)程序會(huì)為了內(nèi)部緩存來跟蹤當(dāng)前的appl_ptr,這個(gè)函數(shù)僅僅對(duì)于這個(gè)情況才會(huì)被用到。

這個(gè)函數(shù)也是原子的。

page函數(shù)

這個(gè)函數(shù)也不是必須的。這個(gè)函數(shù)主要對(duì)那些不連續(xù)的緩存區(qū)。mmap會(huì)調(diào)用這個(gè)函數(shù)得到內(nèi)存頁(yè)的地址。后續(xù)章節(jié)“緩沖區(qū)和內(nèi)存管理”會(huì)有一些例子介紹。

中斷處理

下面的pcm工作就是PCM中斷處理了。聲卡驅(qū)動(dòng)中的PCM中斷處理的作用主要是更新緩存的位置,然后在緩沖位置超過預(yù)先定義的period大小的時(shí)候通知PCM中間層??梢酝ㄟ^調(diào)用snd_pcm_period_elapsed()來通知。

聲卡有如下幾種產(chǎn)生中斷。

period(周期)中斷

這是一個(gè)很常見的類型:硬件會(huì)產(chǎn)生周期中斷。每次中斷都會(huì)調(diào)用snd_pcm_period_elapsed()。

snd_pcm_period_elapsed()的參數(shù)是substream的指針。因?yàn)?,需要?/font>chip實(shí)例中得到substream的指針。例如:在chip記錄中定義一個(gè)substream字段來保持當(dāng)前運(yùn)行的substream指針,在open函數(shù)中要設(shè)定這個(gè)字段而在close函數(shù)中要復(fù)位這個(gè)字段。

假如在中斷處理函數(shù)中獲得了一個(gè)自旋鎖,如果其他pcm也會(huì)調(diào)用這個(gè)鎖,那你必須要在調(diào)用snd_pcm_period_elapsed()之前釋放這個(gè)鎖。

典型代碼如下:

Example5-3.中斷函數(shù)處理#1

structirqreturn_t snd_mychip_interrupt(int irq, void *dev_id)

{

structmychip *chip = dev_id;

spin_lock(&chip->lock);

....

if(pcm_irq_invoked(chip)){

spin_unlock(&chip->lock);

snd_pcm_period_elapsed(chip->substream);

spin_lock(&chip->lock);

//如果需要的話,可以響應(yīng)中斷

}

....

spin_unlock(&chip->lock);

returnIRQ_HANDLED;

}

高頻率時(shí)鐘中斷

當(dāng)硬件不再產(chǎn)生一個(gè)period(周期)中斷的時(shí)候,就需要一個(gè)固定周期的timer中斷了(例如es1968,ymfpci驅(qū)動(dòng))。這時(shí)候,在每次中斷都要檢查當(dāng)前硬件位置,同時(shí)計(jì)算已經(jīng)累積的采樣的長(zhǎng)度。當(dāng)長(zhǎng)度超過period長(zhǎng)度時(shí)候,需要調(diào)用snd_pcm_period_elapsed()同時(shí)復(fù)位計(jì)數(shù)值。

典型代碼如下:

Example5-4.中斷函數(shù)處理#2

staticirqreturn_t snd_mychip_interrupt(int irq, void *dev_id)

{

structmychip *chip = dev_id;

spin_lock(&chip->lock);

....

if(pcm_irq_invoked(chip)){

unsignedint last_ptr, size;

/*得到當(dāng)前的硬件指針(幀為單位)*/

last_ptr= get_hw_ptr(chip);

/*計(jì)算自從上次更新之后又處理的幀*/

if(last_ptr < chip->last_ptr)

{

size= runtime->buffer_size + last_ptr - chip->last_ptr

}else

{

size= last_ptr – chip->chip->last_ptr;

}

//保持上次更新的位置

chip->last_ptr= last_ptr;

/*累加size計(jì)數(shù)器*/

      chip->size+= size;

/*超過period的邊界?*/

if(chip->size >= runtime->period_size){

/*重置size計(jì)數(shù)器*/

chip->size%= runtime->period_size;

spin_unlock(&chip->lock);

snd_pcm_period_elapsed(substream);

spin_lock(&chip->lock);

}

//需要的話,要相應(yīng)中斷

}

....

spin_unlock(&chip->lock);

returnIRQ_HANDLED;

}


在調(diào)用snd_pcm_period_elapsed()的時(shí)候

就算超過一個(gè)period的時(shí)間已經(jīng)過去,你也不需要多次調(diào)用snd_pcm_period_elapsed(),因?yàn)?/font>pcm層會(huì)自己檢查當(dāng)前的硬件指針和上次和更新的狀態(tài)。


原子操作

在內(nèi)核編程的時(shí)候,一個(gè)非常重要(又很難dubug)的問題就是競(jìng)爭(zhēng)條件。Linux內(nèi)核中,一般是通過自旋鎖和信號(hào)量來解決的。通常來說,假如競(jìng)爭(zhēng)發(fā)生在中斷函數(shù)中,中斷函數(shù)要具有原子性,你必須采用自旋鎖來包含臨界資源。假如不是發(fā)生在中斷部分,同時(shí)比較耗時(shí),可以采用信號(hào)量。

如我們看到的,pcm的操作函數(shù)一些是原子的而一些不是。例如:hw_params函數(shù)不是原子的,而trigger函數(shù)是原子的。這意味著,后者調(diào)用的時(shí)候,PCM中間層已經(jīng)擁有了鎖。

在這些函數(shù)中申請(qǐng)的自旋鎖和信號(hào)量要做個(gè)計(jì)算。

在這些原子的函數(shù)中,不能那些可能調(diào)用任務(wù)切換和進(jìn)入睡眠的函數(shù)。其中信號(hào)量和互斥體可能會(huì)進(jìn)入睡眠,因此,在原子操作的函數(shù)中(如:trigger函數(shù))不能調(diào)用它們。如果在這種函數(shù)中調(diào)用delay,可以用udelay(),mdelay()


約束

假如你的聲卡支持不常用的采樣率,或僅僅支持固定的采樣率,就需要設(shè)定一個(gè)約束條件。

例如:為了把采樣率限制在一些支持的幾種之中,就需要用到函數(shù)snd_pcm_hw_constraint_list()。需要在open函數(shù)中調(diào)用它。

Example5-5.硬件約束示例

staticunsigned int rates[] =

{4000,10000,22050,44100};

staticunsigned snd_pcm_hw_constraint_list constraints_rates = {

.count= ARRAY_SIZE(rates),

.list= rates,

.mask= 0,

};

static intsnd_mychip_pcm_open(struct snd_pcm_substream *substream)

{

int err;

....

err =snd_pcm_hw_constraint_list(substream->runtime,0,

SNDRV_PCM_HW_PARAM_RATE,

&constraints_rates);

if (err <0)

returnerr;

....

}


有多種不同的約束。請(qǐng)參考sound/pcm.h中的完整的列表。甚至可以定義自己的約束條件。例如,假如my_chip可以管理一個(gè)單通道的子流,格式是S16_LE,另外,它還支持snd_pcm_hareware中設(shè)定的格式(或其他constraint_list)。可以設(shè)定一個(gè):

Example5-6.為通道設(shè)定一個(gè)硬件規(guī)則

static inthw_rule_format_by_channels(struct snd_pcm_hw_params *params,

structsnd_pcm_hw_rule *rule)

{

structsnd_interval *c = hw_params_interval(params,

SNDRV_PCM_HW_PARAM_CHANNELS);

structsnd_mask *f = hw_param_mask(params,SNDRV_PCM_HW_PARAM_FORMAT);

structsnd_mask fmt;

snd_mask_any(&fmt);/*初始化結(jié)構(gòu)體*/

if(c->min < 2){

fmt.bits[0]&= SNDRV_PCM_FMTBIT_S16_LE;

returnsnd_mask_refine(f, &fmt);

}

return 0;

}

之后,需要把上述函數(shù)加入到你的規(guī)則當(dāng)中去:

snd_pcm_hw_rule_add(substream->runtime,0, SNDRV_PCM_HW_PARAM_CHANNELS,

hw_rule_channels_by_format,0,SNDRV_PCM_HW_PARAM_FORMAT,

-1);

當(dāng)應(yīng)用程序設(shè)定聲道數(shù)量的時(shí)候會(huì)調(diào)用上面的規(guī)則函數(shù)。但是應(yīng)用程序可以在設(shè)定聲道數(shù)之前設(shè)定格式。所以也需要設(shè)定對(duì)應(yīng)的規(guī)則。

Example5-7.為通道設(shè)定一個(gè)硬件規(guī)則

static inthw_rule_format_by_format(struct snd_pcm_hw_params *params,

structsnd_pcm_hw_rule *rule)

{

structsnd_interval *c = hw_param_interval(params,

SNDRV_PCM_HW_PARAM_CHANNELS);

structsnd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);

structsnd_interval ch;

snd_interval_any(&ch);

if(f->bits[0] == SNDRV_PCM_FORMAT_S16_LE){

ch.min= ch.max = 1;

ch.integer= 1;

returnsnd_interval_refine(c, &ch);

}

return 0;

}

open函數(shù)中:

snd_pcm_hw_rule_add(substream->runtime,0, SNDRV_PCM_HW_PARAM_FORMAT,

hw_rule_channels_by_format,0,SNDRV_PCM_HW_PARAM_CHANNELS,

-1);

這里我們不會(huì)更詳細(xì)的描述,我仍然想說“直接看源碼吧”。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服