内核 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 会记录整个消息的可读表示。