通用計數器介面

簡介

計數器裝置廣泛應用於各個行業。這些裝置的普遍存在需要一個通用的介面以及統一的互動和公開標準。該驅動程式 API 旨在透過引入一個通用的計數器介面,解決現有計數器裝置驅動程式中重複程式碼的問題。通用計數器介面使驅動程式能夠支援並公開計數器裝置中常見的一組元件和功能。

理論

計數器裝置在設計上可能差異很大,但無論某些裝置是正交編碼器計數器還是累加計數器,所有計數器裝置都包含一組核心元件。所有計數器裝置共享的這組核心元件構成了通用計數器介面的精髓。

計數器有三個核心元件

  • 訊號 (Signal):由計數器評估的資料流。

  • 突觸 (Synapse):將訊號和評估觸發器與計數關聯起來。

  • 計數 (Count):連線的突觸效應的累積。

訊號 (SIGNAL)

訊號代表一個數據流。這是由計數器評估的輸入資料,用於確定計數資料;例如,旋轉編碼器的正交訊號輸出線。並非所有計數器裝置都提供使用者對訊號資料的訪問,因此對於驅動程式而言,公開是可選的。

當訊號資料可供使用者訪問時,通用計數器介面提供以下可用訊號值

  • SIGNAL_LOW:訊號線處於低電平狀態。

  • SIGNAL_HIGH:訊號線處於高電平狀態。

一個訊號可以與一個或多個計數關聯。

突觸 (SYNAPSE)

突觸代表訊號與計數的關聯。訊號資料影響相應的計數資料,突觸代表這種關係。

突觸動作模式指定了觸發相應計數的計數功能評估以更新計數資料的訊號資料條件。通用計數器介面提供以下可用動作模式

  • 無 (None):訊號不觸發計數功能。在脈衝-方向計數功能模式下,此訊號被評估為方向。

  • 上升沿 (Rising Edge):低電平狀態轉換為高電平狀態。

  • 下降沿 (Falling Edge):高電平狀態轉換為低電平狀態。

  • 雙沿 (Both Edges):任何狀態轉換。

計數器定義為一組輸入訊號,這些訊號與計數資料相關聯,而計數資料是根據相應計數功能評估關聯輸入訊號狀態而生成的。在通用計數器介面的上下文中,計數器由多個計數 (Counts) 組成,每個計數都與一組訊號 (Signals) 相關聯,其各自的突觸 (Synapse) 例項代表關聯計數的計數功能更新條件。

一個突觸將一個訊號與一個計數關聯起來。

計數 (COUNT)

計數代表連線的突觸效應的累積;即,一組訊號的計數資料。通用計數器介面將計數資料表示為自然數。

計數具有表示計數資料更新行為的計數功能模式。通用計數器介面提供以下可用計數功能模式

  • 增加 (Increase):累積計數遞增。

  • 減少 (Decrease):累積計數遞減。

  • 脈衝-方向 (Pulse-Direction):訊號 A 上的上升沿更新相應的計數。訊號 B 的輸入電平決定方向。

  • 正交 (Quadrature):一對正交編碼訊號被評估以確定位置和方向。以下正交模式可用

    • x1 A:如果方向是向前,正交對訊號 A 上的上升沿更新相應的計數;如果方向是向後,正交對訊號 A 上的下降沿更新相應的計數。正交編碼決定方向。

    • x1 B:如果方向是向前,正交對訊號 B 上的上升沿更新相應的計數;如果方向是向後,正交對訊號 B 上的下降沿更新相應的計數。正交編碼決定方向。

    • x2 A:正交對訊號 A 上的任何狀態轉換都會更新相應的計數。正交編碼決定方向。

    • x2 B:正交對訊號 B 上的任何狀態轉換都會更新相應的計數。正交編碼決定方向。

    • x4:任一正交對訊號上的任何狀態轉換都會更新相應的計數。正交編碼決定方向。

一個計數具有一組或多組關聯的突觸。

範例

最基本的計數器裝置可以表示為透過單個突觸與單個訊號關聯的單個計數。例如,一個計數器裝置,它僅僅累積源輸入線上的上升沿計數

        Count                Synapse        Signal
        -----                -------        ------
+---------------------+
| Data: Count         |    Rising Edge     ________
| Function: Increase  |  <-------------   / Source \
|                     |                  ____________
+---------------------+

在此示例中,訊號是具有脈衝電壓的源輸入線,而計數是重複遞增的持久計數數值。訊號透過突觸與相應的計數關聯。增加功能由突觸指定的訊號資料條件觸發——在本例中是電壓輸入線上的上升沿條件。總而言之,計數器裝置的存在和行為由相應的計數、訊號和突觸元件恰當地表示:上升沿條件觸發累積計數資料上的增加功能。

計數器裝置不限於單個訊號;實際上,理論上許多訊號甚至可以與單個計數關聯。例如,正交編碼器計數器裝置可以根據兩條輸入線的狀態來跟蹤位置

           Count                 Synapse     Signal
           -----                 -------     ------
+-------------------------+
| Data: Position          |    Both Edges     ___
| Function: Quadrature x4 |  <------------   / A \
|                         |                 _______
|                         |
|                         |    Both Edges     ___
|                         |  <------------   / B \
|                         |                 _______
+-------------------------+

在此示例中,兩個訊號(正交編碼器線 A 和 B)與單個計數關聯:A 或 B 上的上升沿或下降沿觸發“正交 x4”功能,該功能確定移動方向並更新相應的位置資料。“正交 x4”功能可能在正交編碼器計數器裝置的硬體中實現;計數、訊號和突觸僅僅代表這種硬體行為和功能。

與同一計數關聯的訊號可以具有不同的突觸動作模式條件。例如,一個在非正交脈衝-方向模式下執行的正交編碼器計數器裝置,可以有一條輸入線專用於運動,第二條輸入線專用於方向

           Count                   Synapse      Signal
           -----                   -------      ------
+---------------------------+
| Data: Position            |    Rising Edge     ___
| Function: Pulse-Direction |  <-------------   / A \ (Movement)
|                           |                  _______
|                           |
|                           |       None         ___
|                           |  <-------------   / B \ (Direction)
|                           |                  _______
+---------------------------+

只有訊號 A 觸發“脈衝-方向”更新功能,但仍然需要訊號 B 的瞬時狀態來確定方向,以便正確更新位置資料。最終,兩個訊號都透過兩個相應的突觸與同一計數關聯,但只有一個突觸具有觸發相應計數功能的活動動作模式條件,而另一個則保留為“無”條件動作模式,以表明其相應訊號可用於狀態評估,儘管它處於非觸發模式。

請記住,訊號、突觸和計數是抽象表示,無需與各自的物理源緊密結合。這使得計數器的使用者可以擺脫物理元件的細微差別(例如輸入線是差分還是單端),而是專注於資料和過程所代表的核心思想(例如從正交編碼資料解釋的位置)。

驅動程式 API

驅動程式開發者可以透過包含 include/linux/counter.h 標頭檔案,在其程式碼中使用通用計數器介面。此標頭檔案提供了用於定義計數器裝置的幾個核心資料結構、函式原型和宏。

結構體 counter_comp

計數器元件節點

定義:

struct counter_comp {
    enum counter_comp_type type;
    const char *name;
    void *priv;
    union {
        int (*action_read)(struct counter_device *counter,struct counter_count *count,struct counter_synapse *synapse, enum counter_synapse_action *action);
        int (*device_u8_read)(struct counter_device *counter, u8 *val);
        int (*count_u8_read)(struct counter_device *counter, struct counter_count *count, u8 *val);
        int (*signal_u8_read)(struct counter_device *counter, struct counter_signal *signal, u8 *val);
        int (*device_u32_read)(struct counter_device *counter, u32 *val);
        int (*count_u32_read)(struct counter_device *counter, struct counter_count *count, u32 *val);
        int (*signal_u32_read)(struct counter_device *counter, struct counter_signal *signal, u32 *val);
        int (*device_u64_read)(struct counter_device *counter, u64 *val);
        int (*count_u64_read)(struct counter_device *counter, struct counter_count *count, u64 *val);
        int (*signal_u64_read)(struct counter_device *counter, struct counter_signal *signal, u64 *val);
        int (*signal_array_u32_read)(struct counter_device *counter,struct counter_signal *signal, size_t idx, u32 *val);
        int (*device_array_u64_read)(struct counter_device *counter, size_t idx, u64 *val);
        int (*count_array_u64_read)(struct counter_device *counter,struct counter_count *count, size_t idx, u64 *val);
        int (*signal_array_u64_read)(struct counter_device *counter,struct counter_signal *signal, size_t idx, u64 *val);
    };
    union {
        int (*action_write)(struct counter_device *counter,struct counter_count *count,struct counter_synapse *synapse, enum counter_synapse_action action);
        int (*device_u8_write)(struct counter_device *counter, u8 val);
        int (*count_u8_write)(struct counter_device *counter, struct counter_count *count, u8 val);
        int (*signal_u8_write)(struct counter_device *counter, struct counter_signal *signal, u8 val);
        int (*device_u32_write)(struct counter_device *counter, u32 val);
        int (*count_u32_write)(struct counter_device *counter, struct counter_count *count, u32 val);
        int (*signal_u32_write)(struct counter_device *counter, struct counter_signal *signal, u32 val);
        int (*device_u64_write)(struct counter_device *counter, u64 val);
        int (*count_u64_write)(struct counter_device *counter, struct counter_count *count, u64 val);
        int (*signal_u64_write)(struct counter_device *counter, struct counter_signal *signal, u64 val);
        int (*signal_array_u32_write)(struct counter_device *counter,struct counter_signal *signal, size_t idx, u32 val);
        int (*device_array_u64_write)(struct counter_device *counter, size_t idx, u64 val);
        int (*count_array_u64_write)(struct counter_device *counter,struct counter_count *count, size_t idx, u64 val);
        int (*signal_array_u64_write)(struct counter_device *counter,struct counter_signal *signal, size_t idx, u64 val);
    };
};

成員

型別

計數器元件資料型別

名稱

裝置專用元件名稱

私有資料

元件相關資料

{unnamed_union}

匿名

action_read

突觸動作模式讀取回調。相應的突觸動作模式的讀取值應透過 action 引數傳回。

device_u8_read

裝置 u8 元件讀取回調。相應的裝置 u8 元件的讀取值應透過 val 引數傳回。

count_u8_read

計數 u8 元件讀取回調。相應的計數 u8 元件的讀取值應透過 val 引數傳回。

signal_u8_read

訊號 u8 元件讀取回調。相應的訊號 u8 元件的讀取值應透過 val 引數傳回。

device_u32_read

裝置 u32 元件讀取回調。相應的裝置 u32 元件的讀取值應透過 val 引數傳回。

count_u32_read

計數 u32 元件讀取回調。相應的計數 u32 元件的讀取值應透過 val 引數傳回。

signal_u32_read

訊號 u32 元件讀取回調。相應的訊號 u32 元件的讀取值應透過 val 引數傳回。

device_u64_read

裝置 u64 元件讀取回調。相應的裝置 u64 元件的讀取值應透過 val 引數傳回。

count_u64_read

計數 u64 元件讀取回調。相應的計數 u64 元件的讀取值應透過 val 引數傳回。

signal_u64_read

訊號 u64 元件讀取回調。相應的訊號 u64 元件的讀取值應透過 val 引數傳回。

signal_array_u32_read

訊號 u32 陣列元件讀取回調。相應計數 u32 陣列元件元素的索引透過 idx 引數傳入。相應計數 u32 陣列元件元素的讀取值應透過 val 引數傳回。

device_array_u64_read

裝置 u64 陣列元件讀取回調。相應裝置 u64 陣列元件元素的索引透過 idx 引數傳入。相應裝置 u64 陣列元件元素的讀取值應透過 val 引數傳回。

count_array_u64_read

計數 u64 陣列元件讀取回調。相應計數 u64 陣列元件元素的索引透過 idx 引數傳入。相應計數 u64 陣列元件元素的讀取值應透過 val 引數傳回。

signal_array_u64_read

訊號 u64 陣列元件讀取回調。相應計數 u64 陣列元件元素的索引透過 idx 引數傳入。相應計數 u64 陣列元件元素的讀取值應透過 val 引數傳回。

{unnamed_union}

匿名

action_write

突觸動作模式寫入回撥。相應的突觸動作模式的寫入值透過 action 引數傳入。

device_u8_write

裝置 u8 元件寫入回撥。相應的裝置 u8 元件的寫入值透過 val 引數傳入。

count_u8_write

計數 u8 元件寫入回撥。相應的計數 u8 元件的寫入值透過 val 引數傳入。

signal_u8_write

訊號 u8 元件寫入回撥。相應的訊號 u8 元件的寫入值透過 val 引數傳入。

device_u32_write

裝置 u32 元件寫入回撥。相應的裝置 u32 元件的寫入值透過 val 引數傳入。

count_u32_write

計數 u32 元件寫入回撥。相應的計數 u32 元件的寫入值透過 val 引數傳入。

signal_u32_write

訊號 u32 元件寫入回撥。相應的訊號 u32 元件的寫入值透過 val 引數傳入。

device_u64_write

裝置 u64 元件寫入回撥。相應的裝置 u64 元件的寫入值透過 val 引數傳入。

count_u64_write

計數 u64 元件寫入回撥。相應的計數 u64 元件的寫入值透過 val 引數傳入。

signal_u64_write

訊號 u64 元件寫入回撥。相應的訊號 u64 元件的寫入值透過 val 引數傳入。

signal_array_u32_write

訊號 u32 陣列元件寫入回撥。相應訊號 u32 陣列元件元素的索引透過 idx 引數傳入。相應訊號 u32 陣列元件元素的寫入值透過 val 引數傳入。

device_array_u64_write

裝置 u64 陣列元件寫入回撥。相應裝置 u64 陣列元件元素的索引透過 idx 引數傳入。相應裝置 u64 陣列元件元素的寫入值透過 val 引數傳入。

count_array_u64_write

計數 u64 陣列元件寫入回撥。相應計數 u64 陣列元件元素的索引透過 idx 引數傳入。相應計數 u64 陣列元件元素的寫入值透過 val 引數傳入。

signal_array_u64_write

訊號 u64 陣列元件寫入回撥。相應訊號 u64 陣列元件元素的索引透過 idx 引數傳入。相應訊號 u64 陣列元件元素的寫入值透過 val 引數傳入。

結構體 counter_signal

計數器訊號節點

定義:

struct counter_signal {
    int id;
    const char *name;
    struct counter_comp *ext;
    size_t num_ext;
};

成員

ID

用於識別訊號的唯一 ID

名稱

裝置專用訊號名稱

擴充套件

可選的訊號擴充套件陣列

擴充套件數量

ext 中指定的訊號擴充套件數量

結構體 counter_synapse

計數器突觸節點

定義:

struct counter_synapse {
    const enum counter_synapse_action *actions_list;
    size_t num_actions;
    struct counter_signal *signal;
};

成員

動作列表

可用動作模式陣列

動作數量

actions_list 中指定的動作模式數量

訊號

指向關聯訊號的指標

結構體 counter_count

計數器計數節點

定義:

struct counter_count {
    int id;
    const char *name;
    const enum counter_function *functions_list;
    size_t num_functions;
    struct counter_synapse *synapses;
    size_t num_synapses;
    struct counter_comp *ext;
    size_t num_ext;
};

成員

ID

用於識別計數的唯一 ID

名稱

裝置專用計數名稱

功能列表

可用功能模式陣列

功能數量

functions_list 中指定的功能模式數量

突觸

用於初始化的突觸陣列

突觸數量

synapses 中指定的突觸數量

擴充套件

可選的計數擴充套件陣列

擴充套件數量

ext 中指定的計數擴充套件數量

結構體 counter_event_node

計數器事件節點

定義:

struct counter_event_node {
    struct list_head l;
    u8 event;
    u8 channel;
    struct list_head comp_list;
};

成員

l

當前正在觀察的計數器事件列表

事件

觸發的事件

通道

事件通道

元件列表

事件觸發時要觀察的元件列表

結構體 counter_ops

來自驅動程式的回撥

定義:

struct counter_ops {
    int (*signal_read)(struct counter_device *counter,struct counter_signal *signal, enum counter_signal_level *level);
    int (*count_read)(struct counter_device *counter, struct counter_count *count, u64 *value);
    int (*count_write)(struct counter_device *counter, struct counter_count *count, u64 value);
    int (*function_read)(struct counter_device *counter,struct counter_count *count, enum counter_function *function);
    int (*function_write)(struct counter_device *counter,struct counter_count *count, enum counter_function function);
    int (*action_read)(struct counter_device *counter,struct counter_count *count,struct counter_synapse *synapse, enum counter_synapse_action *action);
    int (*action_write)(struct counter_device *counter,struct counter_count *count,struct counter_synapse *synapse, enum counter_synapse_action action);
    int (*events_configure)(struct counter_device *counter);
    int (*watch_validate)(struct counter_device *counter, const struct counter_watch *watch);
};

成員

訊號讀取

訊號的可選讀取回調。相應訊號的讀取電平應透過 level 引數傳回。

計數讀取

計數的讀取回調。相應計數的讀取值應透過 value 引數傳回。

計數寫入

計數的 optional 寫入回撥。相應計數的寫入值透過 value 引數傳入。

功能讀取

計數功能模式的讀取回調。相應計數的讀取功能模式應透過 function 引數傳回。

功能寫入

計數功能模式的可選寫入回撥。相應計數的要寫入的功能模式透過 function 引數傳入。

action_read

突觸動作模式的可選讀取回調。相應突觸的讀取動作模式應透過 action 引數傳回。

action_write

突觸動作模式的可選寫入回撥。相應突觸的要寫入的動作模式透過 action 引數傳入。

事件配置

配置事件的可選寫入回撥。結構體 counter_event_node 列表可以透過 counter 引數的 events_list 成員訪問。

觀察驗證

驗證觀察的可選回撥。計數器元件觀察配置透過 watch 引數傳入。返回值為 0 表示有效的計數器元件觀察配置。

結構體 counter_device

計數器資料結構

定義:

struct counter_device {
    const char *name;
    struct device *parent;
    const struct counter_ops *ops;
    struct counter_signal *signals;
    size_t num_signals;
    struct counter_count *counts;
    size_t num_counts;
    struct counter_comp *ext;
    size_t num_ext;
    struct device dev;
    struct cdev chrdev;
    struct list_head events_list;
    spinlock_t events_list_lock;
    struct list_head next_events_list;
    struct mutex n_events_list_lock;
    struct counter_event *events;
    wait_queue_head_t events_wait;
    spinlock_t events_in_lock;
    struct mutex events_out_lock;
    struct mutex ops_exist_lock;
};

成員

名稱

裝置名稱

父級

提供計數器的可選父裝置

操作

來自驅動程式的回撥

訊號

訊號陣列

訊號數量

signals 中指定的訊號數量

計數

計數陣列

計數數量

counts 中指定的計數數量

擴充套件

可選的計數器裝置擴充套件陣列

擴充套件數量

ext 中指定的計數器裝置擴充套件數量

裝置

內部裝置結構

字元裝置

內部字元裝置結構

事件列表

當前正在觀察的計數器事件列表

事件列表鎖

保護計數器事件列表操作的鎖

下一個事件列表

下一個正在觀察的計數器事件列表

下一個事件列表鎖

保護計數器下一個事件列表操作的鎖

事件

檢測到的計數器事件佇列

事件等待

允許阻塞讀取計數器事件的等待佇列

事件入隊鎖

保護計數器事件入隊操作的鎖

事件出隊鎖

保護計數器事件出隊操作的鎖

操作存在鎖

防止移除期間使用的鎖

void *counter_priv(const 結構體 counter_device *const counter)

訪問計數器裝置私有資料

引數

const 結構體 counter_device *const counter

計數器裝置

描述

獲取計數器裝置私有資料

結構體 counter_device *counter_alloc(size_t sizeof_priv)

分配一個 counter_device

引數

size_t sizeof_priv

驅動程式私有資料的大小

描述

這是計數器註冊的第一部分。該結構體是動態分配的,以確保嵌入式 結構體 device 具有正確的生命週期。

如果成功,再次呼叫 counter_put() 來釋放 counter_device

int counter_add(結構體 counter_device *counter)

完成計數器註冊

引數

結構體 counter_device *counter

要新增的計數器

描述

這是計數器註冊的第二部分。

如果成功,再次呼叫 counter_unregister() 來釋放 counter_device

void counter_unregister(結構體 counter_device *const counter)

從系統中登出計數器

引數

結構體 counter_device *const counter

指向要登出的計數器的指標

描述

該計數器已從系統中登出。

結構體 counter_device *devm_counter_alloc(結構體 device *dev, size_t sizeof_priv)

分配一個 counter_device

引數

結構體 device *dev

要註冊釋放回調的裝置

size_t sizeof_priv

驅動程式私有資料的大小

描述

這是 counter_add() 的裝置管理版本。它註冊一個清理回撥,以負責呼叫 counter_put()

int devm_counter_add(結構體 device *dev, 結構體 counter_device *const counter)

完成計數器註冊

引數

結構體 device *dev

要註冊釋放回調的裝置

結構體 counter_device *const counter

要新增的計數器

描述

這是 counter_add() 的裝置管理版本。它註冊一個清理回撥,以負責呼叫 counter_unregister()

void counter_push_event(結構體 counter_device *const counter, const u8 event, const u8 channel)

為使用者空間讀取排隊事件

引數

結構體 counter_device *const counter

指向計數器結構體的指標

const u8 event

觸發的事件

const u8 channel

事件通道

注意

如果沒有人在觀察相應的事件,它將被靜默丟棄。

驅動程式實現

為了支援計數器裝置,驅動程式必須首先透過 counter_signal 結構體分配可用計數器訊號。這些訊號應儲存為一個數組,並在計數器註冊到系統之前,將其設定為已分配 counter_device 結構體的 signals 陣列成員。

計數器計數可以透過 counter_count 結構體分配,相應的計數器訊號關聯(突觸)透過 counter_synapse 結構體建立。關聯的 counter_synapse 結構體儲存為一個數組,並設定為相應 counter_count 結構體的 synapses 陣列成員。這些 counter_count 結構體在計數器註冊到系統之前,被設定為已分配 counter_device 結構體的 counts 陣列成員。

必須向 counter_device 結構體提供驅動程式回撥,以便與裝置通訊:讀取和寫入各種訊號和計數,並分別設定和獲取各種突觸和計數的“動作模式”和“功能模式”。

一個 counter_device 結構體使用 counter_alloc() 分配,然後透過將其傳遞給 counter_add() 函式註冊到系統,並透過將其傳遞給 counter_unregister 函式登出。這些函式還有裝置管理版本:devm_counter_alloc()devm_counter_add()

結構體 counter_comp 結構體用於定義訊號、突觸和計數的計數器擴充套件。

“type”成員指定此擴充套件處理的高階資料型別(例如 BOOL、COUNT_DIRECTION 等)。然後,計數器裝置驅動程式可以透過回撥設定“*_read”和“*_write”聯合成員,以使用原生 C 資料型別(即 u8、u64 等)處理該資料。

為驅動程式開發者提供了諸如 COUNTER_COMP_COUNT_U64 等便捷宏。特別是,驅動程式開發者應使用所提供的宏來定義標準計數器子系統屬性,以維護使用者空間的一致介面。例如,計數器裝置驅動程式可以定義以下幾個標準屬性

struct counter_comp count_ext[] = {
        COUNTER_COMP_DIRECTION(count_direction_read),
        COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
        COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
};

這使得檢視、新增和修改此驅動程式支援的屬性(“direction”、“enable”和“ceiling”)變得簡單,並且可以在不陷入複雜的結構體大括號中也能維護此程式碼。

回撥必須與相應元件或擴充套件預期的函式型別匹配。這些函式型別在 結構體 counter_comp 結構體中定義為“*_read”和“*_write”聯合成員。

上述示例中提到的擴充套件的相應回撥原型將是

int count_direction_read(struct counter_device *counter,
                         struct counter_count *count,
                         enum counter_count_direction *direction);
int count_enable_read(struct counter_device *counter,
                      struct counter_count *count, u8 *enable);
int count_enable_write(struct counter_device *counter,
                       struct counter_count *count, u8 enable);
int count_ceiling_read(struct counter_device *counter,
                       struct counter_count *count, u64 *ceiling);
int count_ceiling_write(struct counter_device *counter,
                        struct counter_count *count, u64 ceiling);

確定要建立的擴充套件型別是一個作用域問題。

  • 訊號擴充套件是暴露訊號特定資訊/控制的屬性。這些型別的屬性將存在於 sysfs 中訊號的目錄下。

    例如,如果訊號具有反轉功能,可以有一個名為“invert”的訊號擴充套件來切換該功能:/sys/bus/counter/devices/counterX/signalY/invert

  • 計數擴充套件是暴露計數特定資訊/控制的屬性。這些型別的屬性將存在於 sysfs 中計數的目錄下。

    例如,如果要暫停/取消暫停計數的更新,可以有一個名為“enable”的計數擴充套件來切換此功能:/sys/bus/counter/devices/counterX/countY/enable

  • 裝置擴充套件是暴露不特定於某個計數或訊號的資訊/控制的屬性。您可以在此處放置全域性功能或其他雜項功能。

    例如,如果您的裝置具有過溫感測器,可以透過一個名為“error_overtemp”的裝置擴充套件報告晶片過熱:/sys/bus/counter/devices/counterX/error_overtemp

子系統架構

計數器驅動程式以原生方式(即 u8u64 等)傳遞和接收資料,共享計數器模組處理 sysfs 介面之間的轉換。這保證了所有計數器驅動程式的標準使用者空間介面,並透過通用裝置驅動程式 ABI 啟用了通用計數器字元裝置介面。

計數器驅動程式如何向下傳遞計數值的高階檢視示例如下。驅動程式回撥首先註冊到計數器核心元件,供計數器使用者空間介面元件使用

Driver callbacks registration:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                +----------------------------+
                | Counter device driver      |
                +----------------------------+
                | Processes data from device |
                +----------------------------+
                        |
                 -------------------
                / driver callbacks /
                -------------------
                        |
                        V
                +----------------------+
                | Counter core         |
                +----------------------+
                | Routes device driver |
                | callbacks to the     |
                | userspace interfaces |
                +----------------------+
                        |
                 -------------------
                / driver callbacks /
                -------------------
                        |
        +---------------+---------------+
        |                               |
        V                               V
+--------------------+          +---------------------+
| Counter sysfs      |          | Counter chrdev      |
+--------------------+          +---------------------+
| Translates to the  |          | Translates to the   |
| standard Counter   |          | standard Counter    |
| sysfs output       |          | character device    |
+--------------------+          +---------------------+

此後,資料可以在計數器裝置驅動程式和計數器使用者空間介面之間直接傳輸

Count data request:
~~~~~~~~~~~~~~~~~~~
                 ----------------------
                / Counter device       \
                +----------------------+
                | Count register: 0x28 |
                +----------------------+
                        |
                 -----------------
                / raw count data /
                -----------------
                        |
                        V
                +----------------------------+
                | Counter device driver      |
                +----------------------------+
                | Processes data from device |
                |----------------------------|
                | Type: u64                  |
                | Value: 42                  |
                +----------------------------+
                        |
                 ----------
                / u64     /
                ----------
                        |
        +---------------+---------------+
        |                               |
        V                               V
+--------------------+          +---------------------+
| Counter sysfs      |          | Counter chrdev      |
+--------------------+          +---------------------+
| Translates to the  |          | Translates to the   |
| standard Counter   |          | standard Counter    |
| sysfs output       |          | character device    |
|--------------------|          |---------------------|
| Type: const char * |          | Type: u64           |
| Value: "42"        |          | Value: 42           |
+--------------------+          +---------------------+
        |                               |
 ---------------                 -----------------------
/ const char * /                / struct counter_event /
---------------                 -----------------------
        |                               |
        |                               V
        |                       +-----------+
        |                       | read      |
        |                       +-----------+
        |                       \ Count: 42 /
        |                        -----------
        |
        V
+--------------------------------------------------+
| `/sys/bus/counter/devices/counterX/countY/count` |
+--------------------------------------------------+
\ Count: "42"                                      /
 --------------------------------------------------

涉及四個主要元件

計數器裝置驅動程式

與硬體裝置通訊以讀取/寫入資料;例如,正交編碼器、計時器等的計數器驅動程式。

計數器核心

將計數器裝置驅動程式註冊到系統,以便在使用者空間互動期間呼叫相應的回撥。

計數器 sysfs

將計數器資料轉換為標準計數器 sysfs 介面格式,反之亦然。

有關可用通用計數器介面 sysfs 屬性的詳細分類,請參閱 ABI 檔案 testing/sysfs-bus-counter

計數器字元裝置

將計數器事件轉換為標準計數器字元裝置;資料透過標準字元裝置讀取呼叫傳輸,而計數器事件透過 ioctl 呼叫配置。

Sysfs 介面

通用計數器介面生成了幾個 sysfs 屬性,它們位於 /sys/bus/counter/devices/counterX 目錄下,其中 X 是相應的計數器裝置 ID。有關每個通用計數器介面 sysfs 屬性的詳細資訊,請參閱 ABI 檔案 testing/sysfs-bus-counter

透過這些 sysfs 屬性,程式和指令碼可以與相應計數器裝置的通用計數器範例中的計數、訊號和突觸進行互動。

計數器字元裝置

計數器字元裝置節點在 /dev 目錄下建立為 counterX,其中 X 是相應的計數器裝置 ID。標準計數器資料型別的定義透過使用者空間 include/uapi/linux/counter.h 檔案公開。

計數器事件

計數器裝置驅動程式可以透過利用 counter_push_event 函式來支援計數器事件

void counter_push_event(struct counter_device *const counter, const u8 event,
                        const u8 channel);

事件 ID 由 event 引數指定;事件通道 ID 由 channel 引數指定。當呼叫此函式時,將收集與相應事件關聯的計數器資料,併為每個資料生成一個 結構體 counter_event 並推送到使用者空間。

計數器事件可以由使用者配置,以報告各種感興趣的計數器資料。這可以被概念化為要執行的計數器元件讀取呼叫列表。例如

COUNTER_EVENT_OVERFLOW

COUNTER_EVENT_INDEX

通道 0

通道 0

  • 計數 0

  • 計數 1

  • 訊號 3

  • 計數 4 擴充套件 2

  • 訊號 5 擴充套件 0

  • 訊號 0

  • 訊號 0 擴充套件 0

  • 擴充套件 4

通道 1

  • 訊號 4

  • 訊號 4 擴充套件 0

  • 計數 7

例如,當呼叫 counter_push_event(counter, COUNTER_EVENT_INDEX, 1) 時,它將遍歷 COUNTER_EVENT_INDEX 事件通道 1 的列表,並執行訊號 4、訊號 4 擴充套件 0 和計數 7 的讀取回調——為每個資料返回的資料作為 結構體 counter_event 推送到一個 kfifo 中,使用者空間可以透過對相應字元裝置節點執行標準讀取操作來檢索它。

使用者空間

使用者空間應用程式可以透過對計數器字元裝置節點執行 ioctl 操作來配置計數器事件。以下 ioctl 程式碼由使用者空間 linux/counter.h 標頭檔案支援和提供

  • COUNTER_ADD_WATCH_IOCTL

  • COUNTER_ENABLE_EVENTS_IOCTL

  • COUNTER_DISABLE_EVENTS_IOCTL

為了配置事件以收集計數器資料,使用者首先使用相關事件 ID、事件通道 ID 以及要讀取的所需計數器元件的資訊填充 結構體 counter_watch,然後透過 COUNTER_ADD_WATCH_IOCTL ioctl 命令傳遞它。

請注意,透過將 component.type 成員設定為 COUNTER_COMPONENT_NONE,可以觀察事件而不收集計數器資料。在此配置下,計數器字元裝置將簡單地填充這些相應 結構體 counter_event 元素的事件時間戳,並忽略元件值。

COUNTER_ADD_WATCH_IOCTL 命令將緩衝這些計數器觀察。準備就緒後,可以使用 COUNTER_ENABLE_EVENTS_IOCTL ioctl 命令來啟用這些計數器觀察。

使用者空間應用程式隨後可以對計數器字元裝置節點執行 read 操作(可選地首先呼叫 poll),以檢索帶有所需資料的 結構體 counter_event 元素。