使用者空間驅動的定時器

作者:

Ivan Orlov <ivan.orlov0322@gmail.com>

前言

本文件描述了使用者空間驅動的定時器:虛擬 ALSA 定時器,可以透過使用者空間應用程式使用 IOCTL 呼叫建立和控制。 當我們需要將音訊流與我們沒有匯出 ALSA 定時器的定時器源(例如 PTP 時鐘)同步,以及當使用 snd-aloop 同步透過兩個虛擬音效卡裝置的音訊流時(例如,當我們有一個網路應用程式將幀傳送到一個 snd-aloop 裝置,而另一個聲音應用程式監聽 snd-aloop 的另一端)時,此類定時器可能很有用。

啟用使用者空間驅動的定時器

可以使用 CONFIG_SND_UTIMER 配置選項在核心中啟用使用者空間驅動的定時器。 它依賴於 CONFIG_SND_TIMER 選項,因此也應該啟用它。

使用者空間驅動的定時器 API

使用者空間應用程式可以透過在 /dev/snd/timer 裝置檔案描述符上執行 SNDRV_TIMER_IOCTL_CREATE ioctl 呼叫來建立使用者空間驅動的 ALSA 定時器。 snd_timer_uinfo 結構應作為 ioctl 引數傳遞

struct snd_timer_uinfo {
    __u64 resolution;
    int fd;
    unsigned int id;
    unsigned char reserved[16];
}

resolution 欄位以納秒為單位設定虛擬定時器的所需解析度。 resolution 欄位僅提供有關虛擬定時器的資訊,但不影響定時本身。id 欄位被 ioctl 覆蓋,並且您在呼叫後在此欄位中獲得的識別符號可用作定時器子裝置編號,以便將定時器傳遞給 snd-aloop 核心模組或其他使用者空間應用程式。 系統中一次最多可以有 128 個使用者空間驅動的定時器,因此 id 值範圍為 0 到 127。

除了覆蓋 snd_timer_uinfo 結構外,ioctl 還會將定時器檔案描述符儲存在 snd_timer_uinfo 結構的 fd 欄位中,該描述符可用於觸發定時器。 為定時器分配檔案描述符可確保定時器只能由建立它的程序觸發。 然後可以使用定時器檔案描述符上的 SNDRV_TIMER_IOCTL_TRIGGER ioctl 呼叫來觸發定時器。

因此,建立和觸發定時器的示例程式碼如下

static struct snd_timer_uinfo utimer_info = {
    /* Timer is going to tick (presumably) every 1000000 ns */
    .resolution = 1000000ULL,
    .id = -1,
};

int timer_device_fd = open("/dev/snd/timer",  O_RDWR | O_CLOEXEC);

if (ioctl(timer_device_fd, SNDRV_TIMER_IOCTL_CREATE, &utimer_info)) {
    perror("Failed to create the timer");
    return -1;
}

...

/*
 * Now we want to trigger the timer. Callbacks of all of the
 * timer instances binded to this timer will be executed after
 * this call.
 */
ioctl(utimer_info.fd, SNDRV_TIMER_IOCTL_TRIGGER, NULL);

...

/* Now, destroy the timer */
close(timer_info.fd);

有關建立和觸發定時器的更詳細示例,請參見 utimer ALSA 自檢。

使用者空間驅動的定時器和 snd-aloop

當同步虛擬聲音環回兩端的兩個聲音應用程式時,使用者空間驅動的定時器可以輕鬆地與 snd-aloop 模組一起使用。 例如,如果其中一個應用程式從網路接收聲音幀並將它們傳送到 snd-aloop pcm 裝置,而另一個應用程式偵聽另一個 snd-aloop pcm 裝置上的幀,那麼當透過網路接收到新的資料週期時,ALSA 中間層應該啟動資料事務,而不是在經過一定數量的 jiffies 時,這是有意義的。 使用者空間驅動的 ALSA 定時器可用於實現此目的。

要使用使用者空間驅動的 ALSA 定時器作為 snd-aloop 的定時器源,請將以下字串作為 snd-aloop timer_source 引數傳遞

# modprobe snd-aloop timer_source="-1.4.<utimer_id>"

其中 utimer_id 是您使用 SNDRV_TIMER_IOCTL_CREATE 建立的定時器的 id,4 是使用者空間驅動的定時器裝置編號 (SNDRV_TIMER_GLOBAL_UDRIVEN)。

與 snd-aloop 一起使用的使用者空間驅動的 ALSA 定時器的 resolution 應計算為 1000000000ULL / frame_rate * period_size,因為每次新的幀週期準備就緒時,定時器都會計時。

之後,每次您使用 SNDRV_TIMER_IOCTL_TRIGGER 觸發定時器時,新的資料週期將從一個 snd-aloop 裝置傳輸到另一個裝置。