核心 CAPI 介面到硬體驅動程式

1. 概述

根據 CAPI 2.0 規範:COMMON-ISDN-API (CAPI) 是一種應用程式程式設計介面標準,用於訪問連線到基本速率介面 (BRI) 和一次群速率介面 (PRI) 的 ISDN 裝置。

核心 CAPI 作為 CAPI 應用程式和 CAPI 硬體驅動程式之間的排程層執行。硬體驅動程式透過核心 CAPI 註冊 ISDN 裝置(在 CAPI 行話中稱為控制器),以表明它們已準備好向 CAPI 應用程式提供服務。CAPI 應用程式也向核心 CAPI 註冊,請求與 CAPI 裝置關聯。然後,核心 CAPI 將應用程式註冊排程到可用裝置,並將其轉發到相應的硬體驅動程式。接著,核心 CAPI 在應用程式和硬體驅動程式之間雙向轉發 CAPI 訊息。

CAPI 訊息的格式和語義在 CAPI 2.0 標準中指定。該標準可從 https://www.capi.org 免費獲取。

2. 驅動程式和設備註冊

CAPI 驅動程式在使用前,必須透過呼叫核心 CAPI 函式 attach_capi_ctr() 並傳入指向 struct capi_ctr 的指標,將它們控制的每個 ISDN 設備註冊到核心 CAPI。該結構必須填充驅動程式和控制器的名稱,以及一些回撥函式指標,這些指標隨後由核心 CAPI 用於與驅動程式通訊。透過呼叫函式 detach_capi_ctr() 並傳入指向同一個 struct capi_ctr 的指標,可以撤銷註冊。

在裝置實際使用之前,驅動程式必須在裝置的 capi_ctr 結構中填寫裝置資訊欄位 'manu'、'version'、'profile' 和 'serial',並透過呼叫 capi_ctr_ready() 來表示其準備就緒。從那時起,核心 CAPI 可能會為該裝置呼叫已註冊的回撥函式。

如果裝置因任何原因(關機、斷開連線等)變得不可用,驅動程式必須呼叫 capi_ctr_down()。這將阻止核心 CAPI 進一步呼叫回撥函式。

3. 應用程式註冊與通訊

核心 CAPI 透過呼叫硬體驅動程式的 register_appl() 回撥函式,將來自應用程式的註冊請求(呼叫 CAPI 操作 CAPI_REGISTER)轉發給適當的硬體驅動程式。核心 CAPI 分配一個唯一的應用程式 ID (ApplID, u16),並將其連同應用程式提供的引數結構一起傳遞給 register_appl()。這類似於對常規檔案或字元裝置的 open() 操作。

成功從 register_appl() 返回後,來自應用程式的 CAPI 訊息可以透過呼叫 send_message() 回撥函式傳遞給裝置驅動程式。反之,驅動程式可以呼叫核心 CAPI 的 capi_ctr_handle_message() 函式,將接收到的 CAPI 訊息傳遞給核心 CAPI,以便轉發給應用程式,並指定其 ApplID。

來自應用程式的登出請求(CAPI 操作 CAPI_RELEASE)作為對 release_appl() 回撥函式的呼叫進行轉發,傳遞與 register_appl() 相同的 ApplID。從 release_appl() 返回後,該應用程式的 CAPI 訊息將不再能夠傳遞到或從裝置傳遞。

4. 資料結構

4.1 struct capi_driver

此結構描述了核心 CAPI 驅動程式本身。它用於 register_capi_driver() 和 unregister_capi_driver() 函式中,幷包含以下非私有欄位,所有這些欄位都應在呼叫 register_capi_driver() 之前由驅動程式設定

char name[32]

驅動程式的名稱,以零終止的 ASCII 字串形式

char revision[32]

驅動程式的修訂號,以零終止的 ASCII 字串形式

4.2 struct capi_ctr

此結構描述了由核心 CAPI 驅動程式處理的 ISDN 裝置(控制器)。透過 attach_capi_ctr() 函式註冊後,它將傳遞給所有特定於控制器的下層介面和回撥函式,以識別要操作的控制器。

它包含以下非私有欄位

在呼叫 attach_capi_ctr() 之前由驅動程式設定:

struct module *owner

指向擁有該裝置的驅動程式模組的指標

void *driverdata

一個指向驅動程式特定資料的 opaque 指標,核心 CAPI 不會觸及它

char name[32]

控制器的名稱,以零終止的 ASCII 字串形式

char *driver_name

驅動程式的名稱,以零終止的 ASCII 字串形式

int (*load_firmware)(struct capi_ctr *ctrlr, capiloaddata *ldata)

(可選)指向用於向裝置傳送韌體和配置資料的回撥函式的指標

該函式可能在操作完成之前返回。

必須透過呼叫 capi_ctr_ready() 來表示完成。

返回值:成功時為 0,錯誤時為錯誤程式碼。在程序上下文中呼叫。

void (*reset_ctr)(struct capi_ctr *ctrlr)

(可選)指向用於停止裝置、釋放所有已註冊應用程式的回撥函式的指標

該函式可能在操作完成之前返回。

必須透過呼叫 capi_ctr_down() 來表示完成。

在程序上下文中呼叫。

void (*register_appl)(struct capi_ctr *ctrlr, u16 applid, capi_register_params *rparam)

指向用於向設備註冊應用程式的回撥函式的指標

對這些函式的呼叫由核心 CAPI 序列化,因此在任何給定時間,其中只有一個呼叫處於活動狀態。

void (*release_appl)(struct capi_ctr *ctrlr, u16 applid)

指向用於登出裝置應用程式的回撥函式的指標

對這些函式的呼叫由核心 CAPI 序列化,因此在任何給定時間,其中只有一個呼叫處於活動狀態。

u16  (*send_message)(struct capi_ctr *ctrlr, struct sk_buff *skb)

指向用於向裝置傳送 CAPI 訊息的回撥函式的指標

返回值:CAPI 錯誤程式碼

如果該方法返回 0 (CAPI_NOERROR),則驅動程式已取得 skb 的所有權,呼叫者不再能訪問它。如果返回非零(錯誤)值,則 skb 的所有權返回給呼叫者,呼叫者可以重用或釋放它。

返回值僅應用於指示接受或排隊訊息時出現的問題。訊息實際處理過程中出現的錯誤應透過適當的回覆訊息進行指示。

可在程序或中斷上下文中呼叫。

對該函式的呼叫未由核心 CAPI 序列化,即它必須準備好被重入。

char *(*procinfo)(struct capi_ctr *ctrlr)

指向回撥函式的指標,該函式返回裝置在 CAPI 控制器資訊表 /proc/capi/controller 中的條目

注意

除了 send_message() 之外的回撥函式絕不會在中斷上下文中呼叫。

在呼叫 capi_ctr_ready() 之前填寫:

u8 manu[CAPI_MANUFACTURER_LEN]

CAPI_GET_MANUFACTURER 返回的值

capi_version version

CAPI_GET_VERSION 返回的值

capi_profile profile

CAPI_GET_PROFILE 返回的值

u8 serial[CAPI_SERIAL_LEN]

CAPI_GET_SERIAL 返回的值

4.3 SKB

CAPI 訊息透過 send_message() 和 capi_ctr_handle_message() 在核心 CAPI 和驅動程式之間傳遞,並存儲在套接字緩衝區 (skb) 的資料部分中。每個 skb 包含一個根據 CAPI 2.0 標準編碼的 CAPI 訊息。

對於資料傳輸訊息 DATA_B3_REQ 和 DATA_B3_IND,實際的負載資料緊隨 CAPI 訊息本身,在同一個 skb 中。Data 和 Data64 引數不用於處理。可以透過將 CAPI 訊息的長度欄位設定為 22 而不是 30 來省略 Data64 引數。

4.4 _cmsg 結構

(在 <linux/isdn/capiutil.h> 中宣告)

_cmsg 結構以易於訪問的形式儲存 CAPI 2.0 訊息的內容。它包含所有可能的 CAPI 2.0 引數的成員,包括 Additional Info 和 B Protocol 結構化引數的子引數,但以下情況除外

  • 第二個呼叫方號碼 (CONNECT_IND)

  • Data64 (DATA_B3_REQ 和 DATA_B3_IND)

  • 傳送完成(Additional Info 的子引數,CONNECT_REQ 和 INFO_REQ)

  • 全域性配置(B Protocol 的子引數,CONNECT_REQ、CONNECT_RESP 和 SELECT_B_PROTOCOL_REQ)

只有當前正在處理的訊息型別中出現的引數才實際使用。未使用的成員應設定為零。

成員以它們所代表的 CAPI 2.0 標準引數名稱命名。有關確切拼寫,請參見 <linux/isdn/capiutil.h>。成員資料型別為

u8

用於型別為 ‘byte’ 的 CAPI 引數

u16

用於型別為 ‘word’ 的 CAPI 引數

u32

用於型別為 ‘dword’ 的 CAPI 引數

_cstruct

用於型別為 ‘struct’ 的 CAPI 引數。該成員是指向包含 CAPI 編碼引數(長度 + 內容)的緩衝區的指標。它也可以為 NULL,這將被視為表示一個空(零長度)引數。子引數以編碼形式儲存在內容部分中。

_cmstruct

型別為 ‘struct’ 的 CAPI 引數的替代表示(僅用於 ‘Additional Info’ 和 ‘B Protocol’ 引數)。該表示是一個包含以下值之一的位元組:CAPI_DEFAULT:引數為空/缺失。CAPI_COMPOSE:引數存在。子引數值單獨儲存在相應的 _cmsg 結構成員中。

5. 下層介面函式

int attach_capi_ctr(struct capi_ctr *ctrlr)
int detach_capi_ctr(struct capi_ctr *ctrlr)

向核心 CAPI 註冊/登出裝置(控制器)

void capi_ctr_ready(struct capi_ctr *ctrlr)
void capi_ctr_down(struct capi_ctr *ctrlr)

指示控制器就緒/未就緒

void capi_ctr_handle_message(struct capi_ctr * ctrlr, u16 applid,
                             struct sk_buff *skb)

將接收到的 CAPI 訊息傳遞給核心 CAPI,以便轉發給指定的應用程式

6. 輔助函式和宏

用於從/向 CAPI 訊息頭提取/設定元素值的宏(來自 <linux/isdn/capiutil.h>)

獲取宏

設定宏

元素(型別)

CAPIMSG_LEN(m)

CAPIMSG_SETLEN(m, len)

總長度 (u16)

CAPIMSG_APPID(m)

CAPIMSG_SETAPPID(m, applid)

ApplID (u16)

CAPIMSG_COMMAND(m)

CAPIMSG_SETCOMMAND(m,cmd)

命令 (u8)

CAPIMSG_SUBCOMMAND(m)

CAPIMSG_SETSUBCOMMAND(m, cmd)

子命令 (u8)

CAPIMSG_CMD(m)

命令*256 + 子命令 (u16)

CAPIMSG_MSGID(m)

CAPIMSG_SETMSGID(m, msgid)

訊息編號 (u16)

CAPIMSG_CONTROL(m)

CAPIMSG_SETCONTROL(m, contr)

控制器/PLCI/NCCI (u32)

CAPIMSG_DATALEN(m)

CAPIMSG_SETDATALEN(m, len)

資料長度 (u16)

用於處理 _cmsg 結構的庫函式(來自 <linux/isdn/capiutil.h>)

char *capi_cmd2str(u8 Command, u8 Subcommand)

返回與給定命令和子命令值對應的 CAPI 2.0 訊息名稱,作為一個靜態 ASCII 字串。如果命令/子命令不是 CAPI 2.0 標準中定義的命令之一,則返回值可能為 NULL。

7. 除錯

模組 kernelcapi 有一個模組引數 showcapimsgs,用於控制模組產生的一些除錯輸出。它只能在模組載入時設定,透過 modprobe 命令的引數“showcapimsgs=<n>”,可以在命令列或配置檔案中設定。

如果 showcapimsgs 的最低位被設定,kernelcapi 將記錄控制器和應用程式的啟動和關閉事件。

此外,每個已註冊的 CAPI 控制器都有一個相關的 traceflag 引數,用於控制從控制器傳送到控制器以及從控制器接收到的 CAPI 訊息如何被記錄。traceflag 引數在控制器註冊時使用 showcapimsgs 引數的值進行初始化,但之後可以透過 MANUFACTURER_REQ 命令 KCAPI_CMD_TRACE 進行更改。

如果 traceflag 的值非零,則記錄 CAPI 訊息。僅當 traceflag 的值 > 2 時,才記錄 DATA_B3 訊息。

如果 traceflag 的最低位被設定,則僅記錄命令/子命令和訊息長度。否則,kernelcapi 會記錄整個訊息的可讀表示。