亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1778 | 回復: 0
打印 上一主題 下一主題

writing-an-alsa-driver(編寫一個ALSA驅(qū)動)翻譯稿 第五章(2) [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-06-20 14:00 |只看該作者 |倒序瀏覽

       
       
PCM信息運行時指針
   
當打開一個一個PCM子流的時候,PCM運行時實例就會分配給這個子流。這個指針可以通過substream->runtime獲得。運行時指針擁有多種信息:hw_params和sw_params的配置的拷貝,緩沖區(qū)指針,mmap記錄,自旋鎖等等。幾乎你想控制PCM的所有信息都可以在這里得到。
    Struct
_snd_pcm_runtime {
   
    /*狀態(tài)*/
   
    struct
snd_pcm_substream *trigger_master;
   
    snd_timestamp_t
trigger_tstamp;/*觸發(fā)時間戳*/、
   
    int
overrange;
   
    snd_pcm_uframes_t
avail_max;
   
    snd_pcm_uframes_t
hw_ptr_base /*緩沖區(qū)復位時的位置*/
   
    snd_pcm_uframes_t
hw_ptr_interrupt;/*中斷時的位置*/
   
    /*硬件參數(shù)*/
   
    snd_pcm_access_t
access; /*存取模式*/
   
    snd_pcm_format_t
format; /*SNDRV_PCM_FORMAT_* */
   
    snd_pcm_subformat_t
subformat; /*子格式*/
   
    unsigned
int rate; /*rate in HZ*/
   
    unsigned
int channels; /*通道*/
   
    snd_pcm_uframe_t
period_size; /*周期大小*/
   
    unsigned
int periods /*周期數(shù)*/
   
    snd_pcm_uframes_t
buffer_size; /*緩沖區(qū)大小*/
   
    unsigned
int tick_time; /*tick time滴答時間*/
   
    snd_pcm_uframes_t
min_align; /*格式對應的最小對齊*/
   
    size_t
byte_align;
   
    unsigned
int frame_bits;
   
    unsigned
int sample_bits;
   
    unsigned
int info;
   
    unsigned
int rate_num;
   
    unsigned
int rate_den;
   
    /*軟件參數(shù)*/
   
    struct
timespec tstamp_mode; /*mmap時間戳被更新*/
   
    unsigned
int sleep_min; /*睡眠的最小節(jié)拍數(shù)*/
   
    snd_pcm_uframes_t
xfer_align; /*xfer的大小需要是成倍數(shù)的*/
   
    snd_pcm_uframes_t
start_threshold;
   
    snd_pcm_uframes_t
stop_threshold;
   
    snd_pcm_uframes_t
silence_threshold;/*silence填充閥值*/
   
    snd_pcm_uframes_t
silence_size; /*silence填充大小*/
   
    snd_pcm_uframes_t
boundary;
   
    snd_pcm_uframes_t
silenced_start;
   
    snd_pcm_uframes_t
silenced_size;
   

   
    snd_pcm_sync_id_t
sync; /*硬件同步ID*/
   
    /*mmap*/
   
    volatile
struct snd_pcm_mmap_status *status;
   
    volatile
struct snd_pcm_mmap_control *control;
   
    atomic_t
mmap_count;
   
    /*鎖/調(diào)度*/
   
    spinlock_t
lock;
   
    wait_queue_head_t
sleep;
   
    struct
timer_list tick_timer;
   
    struct
fasync_struct *fasync;
   
    /*私有段*/
   
    void
*private_data;
   
    void
(*private_free)(struct snd_pcm_runtime *runtime);
   
    /*硬件描述*/
   
    struct
snd_pcm_hardware hw;
   
    struct
snd_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);
   
    /*定時器*/
   
    unsigned
int timer_resolution; /*timer resolution*/
   
    /*DMA*/
   
    unsigned
char *dma_area;
   
    dma_addr_t
dma_addr; /*總線物理地址*/
   
    size_t
dma_bytes; /*DMA區(qū)域大小*/
   
    struct
snd_dma_buffer *dma_buffer_p; /*分配的緩沖區(qū)*/
   
    #if
defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
   
    struct
snd_pcm_oss_runtime oss;
   
    #endif   
   
};
   
snd_pcm_runtime
對于大部分的驅(qū)動程序操作集的函數(shù)來說是只讀的。僅僅PCM中間層可以改變/更新這些信息。但是硬件描述,中斷響應,DMA緩沖區(qū)信息和私有數(shù)據(jù)是例外的。此外,假如你采用標準的內(nèi)存分配函數(shù)snd_pcm_lib_malloc_pages(),就不再需要自己設定DMA緩沖區(qū)信息了。
   
下面幾章,會對上面記錄的現(xiàn)實進行解釋。
  
硬件描述
   
硬件描述(struct
snd_pcm_hardware)包含了基本硬件配置的定義。如前面所述,你需要在open的時候?qū)λ鼈冞M行定義。注意runtime實例擁有這個描述符的拷貝而不是已經(jīng)存在的描述符的指針。換句話說,在open函數(shù)中,你可以根據(jù)需要修改描述符的拷貝。例如,假如在一些聲卡上最大的通道數(shù)是1,你仍然可以使用相同的硬件描述符,同時在后面你可以改變最大通道數(shù)。
   
Struct
snd_pcm_runtime *runtime = substream->runtime;
   
....
   
runtime->hw
= snd_mychip_playback_hw; /*通用定義*/
   
if
(chip->model == VERY_OLD_ONE)
   
    runtime->hw.channels_max
= 1;

  
典型的硬件描述如下:
   
static
struct 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[color="#000000"],
        .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[color="#000000"]字段包含pcm[color="#000000"]的類型和能力。位標志在[color="#000000"]中定義,如:SNDRV_PCM_INFO_XXX[color="#000000"]。這里,你必須定義mmap[color="#000000"]是否支持和支持哪種interleaved[color="#000000"]格式。當支持mmap[color="#000000"]的時候,應當設定SNDRV_PCM_INFO_MMAP[color="#000000"]。當硬件支持interleaved[color="#000000"]或no-interleaved[color="#000000"]格式的時候,要設定SNDRV_PCM_INFO_INTERLEAVED[color="#000000"]或SNDRV_PCM_INFO_NONINTERLEAVED[color="#000000"]標志位。假如兩者都支持,你也可以都設定。
   如上面的例子,MMAP_VALID[color="#000000"]和BLOCK_TRANSFER[color="#000000"]都是針對OSS
mmap[color="#000000"]模式,通常情況它們都要設定。當然,MMAP_VALID[color="#000000"]僅僅當mmap[color="#000000"]真正被支持的時候才會被設定。
  
其他一些標志位是SNDRV_PCM_INFO_PAUSE[color="#000000"]和SNDRV_PCM_INFO_RESUME[color="#000000"]。SNDRV_PCM_INFO_PAUSE[color="#000000"]標志位意思是pcm[color="#000000"]支持“暫!辈僮,SNDRV_PCM_INFO_RESUME[color="#000000"]表示是pcm[color="#000000"]支持“掛起/[color="#000000"]恢復”操作。假如PAUSE[color="#000000"]標志位被設定,trigger函數(shù)就必須執(zhí)行一個對應的(暫停
按下/[color="#000000"]釋放)命令。就算沒有RESUME[color="#000000"]標志位,也可以被定義掛起/[color="#000000"]恢復觸發(fā)命令。
[color="#000000"]更詳細的部分請參考“電源管理”一章。
  
當PCM[color="#000000"]子系統(tǒng)能被同步(如:播放流和錄音流的開始/[color="#000000"]結(jié)束的同步)的時候,你可以設定SNDRV_PCM_INFO_SYNC_START[color="#000000"]標志位。在這種情況下,你必須在trigger[color="#000000"]函數(shù)中檢查PCM[color="#000000"]子流鏈。下面的章節(jié)會想笑介紹這個部分。
  
formats[color="#000000"]字段包含了支持格式的標志位(SNDRV_PCM_FMTBIT_XXX)[color="#000000"]。假如硬件支持超過一個的格式,需要對位標志位進行“或”運算。上面的例子就是支持16bit[color="#000000"]有符號的小端格式。
  
rates[color="#000000"]字段包含了支持的采樣率(SNDRV_PCM_RATE_XXX)[color="#000000"]。當聲卡支持多種采樣率的時候,應該附加一個CONTINUOUS[color="#000000"]標志。已經(jīng)預先定義的典型的采樣率,假如你的聲卡支持不常用的采樣率,你需要加入一個KNOT[color="#000000"]標志,同時手動的對硬件進行控制(稍后解釋)。
   rate_min[color="#000000"]和rate_max[color="#000000"]定義了最小和最大的采樣率。應該和采樣率相對應。
   channel_min[color="#000000"]和channel_max[color="#000000"]定義了最大和最小的通道,以前可能你已看到。
  
buffer_bytes_max[color="#000000"]定義了以字節(jié)為單位的最大的緩沖區(qū)大小。這里沒有buffer_bytes_min[color="#000000"]字段,因為它可以通過最小的period[color="#000000"]大小和最小的period[color="#000000"]數(shù)量計算得出。同時,period_bytes_min[color="#000000"]和定義的最小和最大的period[color="#000000"]。periods_max[color="#000000"]和periods_min[color="#000000"]定義了最大和最小的periods[color="#000000"]。
  
period[color="#000000"]信息和OSS[color="#000000"]中的fragment[color="#000000"]相對應。period[color="#000000"]定義了PCM[color="#000000"]中斷產(chǎn)生的周期。這個周期非常依賴硬件。一般來說,一個更短的周期會提供更多的中斷和更多的控制。如在錄音中,周期大小定義了輸入延遲,另外,整個緩存區(qū)大小也定義了播放的輸出延遲。
   字段fifo_size[color="#000000"]。這個主要是和硬件的FIFO[color="#000000"]大小有關(guān),但是目前驅(qū)動中或alsa-lib[color="#000000"]中都沒有使用。所以你可以忽略這個字段。
[color="#000000"]PCM[color="#000000"]配置
  
OK,[color="#000000"]讓我們再次回到PCM[color="#000000"]運行時記錄。最經(jīng)常涉及的運行時實例中的記錄就是PCM[color="#000000"]配置了。PCM[color="#000000"]可以讓應用程序通過alsa-lib[color="#000000"]發(fā)送hw_params[color="#000000"]來配置。有很多字段都是從hw_params[color="#000000"]和sw_params[color="#000000"]結(jié)構(gòu)中拷貝過來的。例如:format[color="#000000"]保持了應用程序選擇的格式類型,這個字段包含了enum[color="#000000"]值SNDRV_PCM_FORMAT_XXX[color="#000000"]。
  
其中要注意的一個就是,配置的buffer[color="#000000"]和period[color="#000000"]大小被放在運行時記錄的“frame”[color="#000000"]中。在ALSA[color="#000000"]里,1frame=channel*samples-size[color="#000000"]。為了在幀和字節(jié)之間轉(zhuǎn)換,你可以用下面的函數(shù),frames_to_bytes()[color="#000000"]和bytes_to_frames()[color="#000000"]。
       period_bytes
= frames_to_bytes(runtime,runtime->period_size);
  
同樣,許多的軟件參數(shù)(sw_params)[color="#000000"]也存放在frames[color="#000000"]字段里面。請檢查這個字段的類型。snd_pcm_uframes_t[color="#000000"]是作為表示frames[color="#000000"]的無符號整數(shù),而snd_pcm_sframes_t[color="#000000"]是作為表示frames[color="#000000"]的有符號整數(shù)。
[color="#000000"]DMA[color="#000000"]緩沖區(qū)信息
  
DMA[color="#000000"]緩沖區(qū)通過下面4[color="#000000"]個字段定義,dma_area,dma_addr,dma_bytes,dma_private[color="#000000"]。其中dma_area[color="#000000"]是緩沖區(qū)的指針(邏輯地址)。可以通過memcopy[color="#000000"]來向這個指針來操作數(shù)據(jù)。dma_addr[color="#000000"]是緩沖區(qū)的物理地址。這個字段僅僅當緩沖區(qū)是線性緩存的時候才要特別說明。dma_bytes是緩沖區(qū)的
大小。dma_private[color="#000000"]是被ALSA[color="#000000"]的DMA[color="#000000"]管理用到的。
  
如果采用ALSA[color="#000000"]的標準內(nèi)存分配函數(shù)snd_pcm_lib_mallock_pages()[color="#000000"]分配內(nèi)存,那些字段會被ALSA[color="#000000"]的中間層設定,你不能自己改變他們,可以讀取而不能寫入。而如果你想自己分配內(nèi)存,你就需要在hw_params[color="#000000"]回調(diào)里面自己管理它們。當內(nèi)存被mmap[color="#000000"]之后,你至少要設定dma_bytes[color="#000000"]和dma_area[color="#000000"]。但是如果你的驅(qū)動不支持mmap[color="#000000"],這些字段就不必一定設定.dma_addr[color="#000000"]也不是強制的,你也可以根據(jù)靈活來用dma_private[color="#000000"]。
[color="#000000"]運行狀態(tài)
  
可以通過runtime->status[color="#000000"]來獲得運行狀態(tài)。它是一個指向snd_pcm_mmap_status[color="#000000"]記錄的指針。例如,可以通過runtime->status->hw_ptr[color="#000000"]來得到當前DMA[color="#000000"]硬件指針。
  
可以通過runtime->control[color="#000000"]來查看DMA[color="#000000"]程序的指針,它是指向snd_pcm_mmap_control[color="#000000"]記錄。但是,不推薦直接存取這些數(shù)據(jù)。
私有數(shù)據(jù)
   
可以為子流分配一個記錄,讓它保存在runtime->private_data里面。通?梢栽趏pen函數(shù)中做。不要和pcm->private_data混攪了,pcm->private_data主要是在創(chuàng)建PCM的時候指向chip實例,而runtime->private_data是在PCM
open的時候指向一個動態(tài)數(shù)據(jù)。
   
Struct int
snd_xxx_open(struct snd_pcm_substream *substream)
   
{
   
    struct
my_pcm_data *data;
   
    data =
kmalloc(sizeof(*data),GFP_KERNEL);
   
    substream->runtime->private_data
= data;
   
    ....
   
}
   
上述分配的對象要在close函數(shù)中釋放。
   
中斷函數(shù)
   
transfer_ack_begin()和transfer_ack_end()將會在snd_pcm_period_elapsed()的開始和結(jié)束。
   
操作函數(shù)
   
現(xiàn)在讓我來詳細介紹每個pcm的操作函數(shù)吧(ops)。通常每個回調(diào)函數(shù)成功的話返回0,出錯的話返回一個帶錯誤碼的負值,如:-EINVAL。
   
每個函數(shù)至少要有一個snd_pcm_substream指針變量。主要是為了從給定的子流實例中得到chip記錄,你可以采用下面的宏。
   
Int xxx(){
   
    struct
mychip *chip = snd_pcm_substream_chip(substream);
   
    ....
   
}
   
   
open函數(shù)
   
static int
snd_xxx_open(struct snd_pcm_substream *substream);
   
當打開一個pcm子流的時候調(diào)用。
   
在這里,你至少要初始化runtime->hw記錄。典型應用如下:
   
static int
snd_xxx_open(struct snd_pcm_substream *substream)
   
{
   
     struct
mychip *chip = snd_pcm_substream_chip(substream);
   
     struct
snd_pcm_runtime *runtime = substream->runtime;
         
   
     runtime->hw
= snd_mychip_playback_hw;
   
     return
0;
   
}
   
其中snd_mychip_playback_hw是預先定義的硬件描述。
   
   
close函數(shù)
   
static int
snd_xxx_close(struct snd_pcm_substream *substream)
   
顯然這是在pcm子流被關(guān)閉的時候調(diào)用。
   
所有在open的時候被分配的pcm子流的私有的實例都應該在這里被釋放。
   
Static int
snd_xxx_close(struct snd_pcm_substream *substream)
   
{
   
    ...
   
    kfree(substream->runtime->private_data);
   
    ...
   
}
   
   
ioctl函數(shù)
   
這個函數(shù)主要是完成一些pcm
ioctl的特殊功能。但是通常你可以采用通用的ioctl函數(shù)snd_pcm_lib_ioctl。
     
   
hw_params函數(shù)
   
static int
snd_xxx_hw_params(struct snd_pcm_substream *substream,
   
                              struct
snd_pcm_substream *hw_params);
   
這個函數(shù)和hw_free函數(shù)僅僅在ALSA0.9.X版本出現(xiàn)。
   
當pcm子流中已經(jīng)定義了緩沖區(qū)大小,period大小,格式等的時候,應用程序可以通過這個函數(shù)來設定硬件參數(shù)。
   
很多的硬件設定都要在這里完成,包括分配內(nèi)存。
   
設定的參數(shù)可以通過params_xxx()宏得到。對于分配內(nèi)存,可以采用下面一個有用的函數(shù),
   
snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(hw_params));
   
snd_pcm_lib_malloc_pages()僅僅當DMA緩沖區(qū)已經(jīng)被預分配之后才可以用。參考“緩存區(qū)類型”一章獲得更詳細的細節(jié)。
   
注意這個和prepare函數(shù)會在初始化當中多次被調(diào)用。例如,OSSemulation可能在每次通過ioctl改變的時候都要調(diào)用這些函數(shù)。
   
因而,千萬不要對一個相同的內(nèi)存分配多次,可能會導致內(nèi)存的漏洞!而上面的幾個函數(shù)是可以被多次調(diào)用的,如果它已經(jīng)被分配了,它會先自動釋放之前的內(nèi)存。
   
另外一個需要注意的是,這些函數(shù)都不是原子的(可以被調(diào)到)。這個是非常重要的,因為trigger函數(shù)是原子的(不可被調(diào)度)。因此,mutex和其他一些和調(diào)度相關(guān)的功能函數(shù)在trigger里面都不需要了。具體參看“原子操作”一節(jié)。
   
   
hw_free函數(shù)
   
static int
snd_xxx_hw_free(struct snd_pcm_substream *substream);
   
這個函數(shù)可以是否通過hw_params分配的資源。例如:通過如下函數(shù)釋放那些通過snd_pcm_lib_malloc_pages()申請的緩存。
   
snd_pcm_lib_free_pages(substream)
   
   
這個函數(shù)要在close調(diào)用之前被調(diào)用。同時,它也可以被多次調(diào)用。它也會知道資源是否已經(jīng)被分配。
   
Prepare函數(shù)
   
static int
snd_xxx_prepare(struct snd_pcm_substream *substream);
   
   
當pcm“準備好了”的時候調(diào)用這個函數(shù)?梢栽谶@里設定格式類型,采樣率等等。和hw_params不同的是,每次調(diào)用snd_pcm_prepare()的時候都要調(diào)用prepare函數(shù)。
   
注意最近的版本prepare變成了非原子操作的了。這個函數(shù)中,你要做一些調(diào)度安全性策略。
   
在下面的函數(shù)中,你會涉及到runtime記錄的值(substream->runtime)。例如:得到當前的采樣率,格式或聲道,可以分別存取runtime->rate,runtime->format,runtime->channels。分配的內(nèi)存的地址放到runtime->dma_area中,內(nèi)存和period大小分別保存在runtime->buffer_size和runtime->period_size中。
   
要注意在每次設定的時候都有可能多次調(diào)用這些函數(shù)。

   
trigger函數(shù)
   
static int
snd_xxx_trigger(struct snd_pcm_substream *substream, int cmd);
   
當pcm開始,停止或暫停的時候都會調(diào)用這個函數(shù)。
   
具體執(zhí)行那個操作主要是根據(jù)第二個參數(shù),在中聲明了SNDRV_PCM_TRIGGER_XXX。最少START和STOP的命令必須定義的。
   
    switch(cmd){
   
    case
SNDRV_PCM_TRIGGER_START:
   
         //啟動PCM引擎
   
         break;
   
    case
SNDRV_PCM_TRIGGER_STOP:
   
         //停止PCM引擎
   
         break;
   
    default:
   
         break;
   
    }
   
當pcm支持暫停操作(在hareware表里面有這個),也必須處理PAUSE_PAUSE和PAUSE_RELEASE命令。前者是暫停命令,后者是重新播放命令。
   
假如pcm支持掛起/恢復操作,不管是全部或部分的掛起/恢復支持,都要處理SUSPEND和RESUME命令。這些命令主要是在電源狀態(tài)改變的時候需要,通常它們和STOP,START命令放到一起。具體參看“電源管理”一章。
   
如前面提到的,這個操作上原子的。不要在調(diào)用這些函數(shù)的時候進入睡眠。而trigger函數(shù)要盡量小,甚至僅僅觸發(fā)DMA。另外的工作如初始化hw_params和prepare應該在之前做好。
   
pointer函數(shù)

static
snd_pcm_uframes_t snd_xxx_pointer(struct snd_pcm_substream
*substream);
   
   
PCM中間層通過調(diào)用這個函數(shù)來獲得緩沖區(qū)中的硬件位置。返回值需要以為frames為單位(在ALSA0.5.X是以字節(jié)為單位的),范圍從0到buffer_size-1。
   
一般情況下,在中斷程序中,調(diào)用snd_pcm_period_elapsed()的時候,在pcm中間層在在更新buffer的程序中調(diào)用它。然后,pcm中間層會更新指針位置和計算可用的空間,然后喚醒那些等待的線程。
   
這個函數(shù)也是原子的。
   
Copy和silence函數(shù)
   
這些函數(shù)不是必須的,同時在大部分的情況下是被忽略的。這些函數(shù)主要應用在硬件內(nèi)存不在正常的內(nèi)存空間的時候。一些聲卡有一些沒有被影射的自己的內(nèi)存。在這種情況下,你必須把內(nèi)存?zhèn)鞯接布膬?nèi)存空間去;蛘撸彌_區(qū)在物理內(nèi)存和虛擬內(nèi)存中都是不連續(xù)的時候就需要它們了。
   
假如定義了copy和silence,就可以做copy和set-silence的操作了。更詳細的描述請參考“緩沖區(qū)和內(nèi)存管理”一章。
   
Ack函數(shù)
   
這個函數(shù)也不是必須的。當在讀寫操作的時候更新appl_ptr的時候會調(diào)用它。一些類似于emu10k1-fx和cs46xx的驅(qū)動程序會為了內(nèi)部緩存來跟蹤當前的appl_ptr,這個函數(shù)僅僅對于這個情況才會被用到。
   
這個函數(shù)也是原子的。
  
   
page函數(shù)
   
這個函數(shù)也不是必須的。這個函數(shù)主要對那些不連續(xù)的緩存區(qū)
。mmap會調(diào)用這個函數(shù)得到內(nèi)存頁的地址。后續(xù)章節(jié)“緩沖區(qū)和內(nèi)存管理”會有一些例子介紹。
   
中斷處理
   
下面的pcm工作就是PCM中斷處理了。聲卡驅(qū)動中的PCM中斷處理的作用主要是更新緩存的位置,然后在緩沖位置超過預先定義的period大小的時候通知PCM中間層。可以通過調(diào)用snd_pcm_period_elapsed()來通知。
   
聲卡有如下幾種產(chǎn)生中斷。
   
period(周期)中斷
   
這是一個很常見的類型:硬件會產(chǎn)生周期中斷。每次中斷都會調(diào)用snd_pcm_period_elapsed()。
   
snd_pcm_period_elapsed()的參數(shù)是substream的指針。因為,需要從chip實例中得到substream的指針。例如:在chip記錄中定義一個substream字段來保持當前運行的substream指針,在open函數(shù)中要設定這個字段而在close函數(shù)中要復位這個字段。
   
假如在中斷處理函數(shù)中獲得了一個自旋鎖,如果其他pcm也會調(diào)用這個鎖,那你必須要在調(diào)用snd_pcm_period_elapsed()之前釋放這個鎖。
   
典型代碼如下:
   
Example5-3.中斷函數(shù)處理#1
   
struct
irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
   
{
   
    struct
mychip *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);
   
        //如果需要的話,可以響應中斷
   
    }
   
    ....
   
    spin_unlock(&chip->lock);
   
    return
IRQ_HANDLED;
   
}
   
   
高頻率時鐘中斷
   
當硬件不再產(chǎn)生一個period(周期)中斷的時候,就需要一個固定周期的timer中斷了(例如
es1968,ymfpci驅(qū)動)。這時候,在每次中斷都要檢查當前硬件位置,同時計算已經(jīng)累積的采樣的長度。當長度超過period長度時候,需要調(diào)用
snd_pcm_period_elapsed()同時復位計數(shù)值。
   
典型代碼如下:
   
Example5-4.中斷函數(shù)處理#2
   
static
irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
   
{
   
    struct
mychip *chip = dev_id;
   
    spin_lock(&chip->lock);
   
    ....
   
    if
(pcm_irq_invoked(chip)){
   
        unsigned
int last_ptr, size;
   
        /*得到當前的硬件指針(幀為單位)*/
   
        last_ptr
= get_hw_ptr(chip);
   
        /*計算自從上次更新之后又處理的幀*/
   
        if
(last_ptr 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計數(shù)器*/
      chip->size
+= size;
   
        /*超過period的邊界?*/
   
        if
(chip->size >= runtime->period_size){
   
            /*重置size計數(shù)器*/
   
            chip->size
%= runtime->period_size;
   
            spin_unlock(&chip->lock);
   
            snd_pcm_period_elapsed(substream);
   
            spin_lock(&chip->lock);
   
        }
   
        //需要的話,要相應中斷
   
    }
   
    ....
   
    spin_unlock(&chip->lock);
   
    return
IRQ_HANDLED;
   
}
   
在調(diào)用snd_pcm_period_elapsed()的時候
   
就算超過一個period的時間已經(jīng)過去,你也不需要多次調(diào)用snd_pcm_period_elapsed(),因為pcm層會自己檢查當前的硬件指針和上次和更新的狀態(tài)。
原子操作
   
在內(nèi)核編程的時候,一個非常重要(又很難dubug)的問題就是競爭條件。Linux內(nèi)核中,一般是通過自旋鎖和信號量來解決的。通常來說,假如競爭發(fā)生在中斷函數(shù)中,中斷函數(shù)要具有原子性,你必須采用自旋鎖來包含臨界資源。假如不是發(fā)生在中斷部分,同時比較耗時,可以采用信號量。
   
如我們看到的,pcm的操作函數(shù)一些是原子的而一些不是。例如:hw_params函數(shù)不是原子的,而trigger函數(shù)是原子的。這意味著,后者調(diào)用的時候,PCM中間層已經(jīng)擁有了鎖。
在這些函數(shù)中申請的自旋鎖和信號量要做個計算。
   
在這些原子的函數(shù)中,不能那些可能調(diào)用任務切換和進入睡眠的函數(shù)。其中信號量和互斥體可能會進入睡眠,因此,在原子操作的函數(shù)中(如:trigger函數(shù))不能調(diào)用它們。如果在這種函數(shù)中調(diào)用delay,可以用udelay(),或mdelay()。
約束
   
假如你的聲卡支持不常用的采樣率,或僅僅支持固定的采樣率,就需要設定一個約束條件。
   
例如:為了把采樣率限制在一些支持的幾種之中,就需要用到函數(shù)snd_pcm_hw_constraint_list()。需要在open函數(shù)中調(diào)用它。
   
Example5-5.硬件約束示例
   
static
unsigned int rates[] =
   
        {4000,10000,22050,44100};
   
static
unsigned snd_pcm_hw_constraint_list constraints_rates = {
   
        .count
= ARRAY_SIZE(rates),
   
        .list
= rates,
   
        .mask
= 0,
   
};
   
static int
snd_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
   
         return
err;
   
    ....
   
}
   
有多種不同的約束。請參考sound/pcm.h中的完整的列表。甚至可以定義自己的約束條件。例如,假如my_chip可以管理一個單通道的子流,格式是S16_LE,另外,它還支持snd_pcm_hareware中設定的格式(或其他constraint_list)?梢栽O定一個:
   
Example5-6.為通道設定一個硬件規(guī)則
   
static int
hw_rule_format_by_channels(struct snd_pcm_hw_params *params,
   
                                      struct
snd_pcm_hw_rule *rule)
   
{
   
    struct
snd_interval *c = hw_params_interval(params,
   
                             SNDRV_PCM_HW_PARAM_CHANNELS);
   
    struct
snd_mask *f = hw_param_mask(params,SNDRV_PCM_HW_PARAM_FORMAT);
   
    struct
snd_mask fmt;
   
    snd_mask_any(&fmt);
/*初始化結(jié)構(gòu)體*/
   
    if
(c->min
   
        fmt.bits[0]
&= SNDRV_PCM_FMTBIT_S16_LE;
   
        return
snd_mask_refine(f, &fmt);
   
    }
   
    return 0;
   
}
   
之后,需要把上述函數(shù)加入到你的規(guī)則當中去:
   
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);
   
當應用程序設定聲道數(shù)量的時候會調(diào)用上面的規(guī)則函數(shù)。但是應用程序可以在設定聲道數(shù)之前設定格式。所以也需要設定對應的規(guī)則。
   
Example5-7.為通道設定一個硬件規(guī)則
   
static int
hw_rule_format_by_format(struct snd_pcm_hw_params *params,
   
                                      struct
snd_pcm_hw_rule *rule)
   
{
   
    struct
snd_interval *c = hw_param_interval(params,
   
                             SNDRV_PCM_HW_PARAM_CHANNELS);
   
    struct
snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
   
    struct
snd_interval ch;
   
    snd_interval_any(&ch);
   
    if
(f->bits[0] == SNDRV_PCM_FORMAT_S16_LE){
   
        ch.min
= ch.max = 1;
   
        ch.integer
= 1;
   
        return
snd_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);
   
這里我們不會更詳細的描述,我仍然想說“直接看源碼吧”。
               
               
               

本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u1/49088/showart_1006031.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP