硬件驱动的内核 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 用于与驱动程序通信。可以通过调用函数 detach_capi_ctr() 并传入指向同一 struct capi_ctr 的指针来撤销注册。

在设备实际可以使用之前,驱动程序必须填写设备信息字段 ‘manu’、‘version’、‘profile’ 和 ‘serial’ 在设备的 capi_ctr 结构中,并通过调用 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

指向特定于驱动程序的数据的不透明指针,内核 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 SKBs

CAPI 消息在内核 CAPI 和驱动程序之间通过 send_message() 和 capi_ctr_handle_message() 传递,并存储在套接字缓冲区 (skb) 的数据部分中。每个 skb 都包含一个根据 CAPI 2.0 标准编码的 CAPI 消息。

对于数据传输消息 DATA_B3_REQ 和 DATA_B3_IND,实际负载数据紧跟在同一 skb 内的 CAPI 消息本身之后。“数据”和“数据 64”参数不用于处理。可以通过将 CAPI 消息的长度字段设置为 22 而不是 30 来省略 Data64 参数。

4.4 _cmsg 结构

(在 <linux/isdn/capiutil.h> 中声明)

_cmsg 结构以易于访问的形式存储 CAPI 2.0 消息的内容。它包含所有可能的 CAPI 2.0 参数的成员,包括附加信息和 B 协议结构化参数的子参数,但以下例外情况除外

  • 第二个主叫方号码 (CONNECT_IND)

  • Data64 (DATA_B3_REQ 和 DATA_B3_IND)

  • 发送完成(附加信息的子参数,CONNECT_REQ 和 INFO_REQ)

  • 全局配置(B 协议的子参数,CONNECT_REQ、CONNECT_RESP 和 SELECT_B_PROTOCOL_REQ)

仅实际使用当前正在处理的消息类型中出现的那些参数。未使用的成员应设置为零。

成员以它们所代表的参数的 CAPI 2.0 标准名称命名。有关确切的拼写,请参见 <linux/isdn/capiutil.h>。成员数据类型为

u8

用于类型为“字节”的 CAPI 参数

u16

用于类型为“字”的 CAPI 参数

u32

用于类型为“双字”的 CAPI 参数

_cstruct

用于类型为“struct”的 CAPI 参数。该成员是指向缓冲区的指针,该缓冲区包含 CAPI 编码(长度 + 内容)中的参数。它也可能为 NULL,这将表示为空(零长度)参数。子参数以编码形式存储在内容部分中。

_cmstruct

类型为“struct”的 CAPI 参数的替代表示形式(仅用于“附加信息”和“B 协议”参数)。该表示形式是包含以下值之一的单个字节: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>)

Get 宏

Set 宏

元素(类型)

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 命令, u8 子命令)

返回与给定命令和子命令值对应的 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 将记录整个消息的可读表示形式。