> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/man4/ng_l2cap.4.md).

# ng\_l2cap(4)

`ng_l2cap` — 实现蓝牙逻辑链路控制和适配协议的 Netgraph 节点类型

## 名称

`ng_l2cap` Adaptation Protocol (L2CAP)

## 概要

`#include <sys/types.h>`

`#include <netgraph/bluetooth/include/ng_hci.h>`

`#include <netgraph/bluetooth/include/ng_l2cap.h>`

## 描述

`l2cap` 节点类型是一种 Netgraph 节点类型，按照蓝牙规范 v1.1 第 D 章实现蓝牙逻辑链路控制和适配协议。

L2CAP 为上层协议提供面向连接和无连接的数据服务，具备协议多路复用、分段和重组操作以及组抽象能力。L2CAP 允许更高层协议和应用程序发送和接收最大 64 千字节长度的 L2CAP 数据包。

### L2CAP 假设

* 两个单元之间的 ACL 链路已建立。基带提供数据包的有序交付，尽管可能存在个别数据包损坏和重复。任何两个设备之间最多存在一条 ACL 链路。
* 基带始终提供全双工通信通道的印象。这并不意味着所有 L2CAP 通信都是双向的。多播和单向流量（如视频）不需要双工通道。
* L2CAP 使用基带层可用的机制提供可靠通道。基带始终在请求时执行数据完整性检查，并重发数据直到成功确认或超时。由于确认可能丢失，即使数据已成功发送，也可能发生超时。

## L2CAP 一般操作

逻辑链路控制和适配协议（L2CAP）基于“通道”概念。每个通道以多对一方式绑定到单个协议。多个通道可以绑定到同一协议，但一个通道不能绑定到多个协议。在通道上接收的每个 L2CAP 数据包都会被定向到相应的更高层协议。

L2CAP 通道的每个端点都由一个通道标识符引用。通道标识符（CID）是代表设备上逻辑通道端点的本地名称。从 0x0001 到 0x003F 的标识符保留用于特定的 L2CAP 功能。空标识符（0x0000）定义为非法标识符，绝不能用作目的端点。所有 L2CAP 信令命令都发送到 CID 0x0001。CID 0x0002 保留用于面向组的通道。同一 CID 不得在本地设备与某个远程设备之间的多个同时 L2CAP 通道中重复用作本地 L2CAP 通道端点。

CID 分配相对于特定设备，设备可以独立于其他设备分配 CID。因此，即使多个连接到单个本地设备的远程设备已将相同的 CID 值分配给（远程）通道端点，本地设备仍能将每个远程 CID 唯一地关联到不同的设备。

### 通道操作状态

**`NG_L2CAP_CLOSED`** 在此状态下，没有与该 CID 关联的通道。这是唯一允许链路级连接（基带）不存在的状态。链路断开将所有其他状态强制转换为 `NG_L2CAP_CLOSED` 状态。

**`NG_L2CAP_W4_L2CAP_CON_RSP`** 在此状态下，CID 代表一个本地端点，已发送引用此端点的 L2CAP 连接请求消息，正在等待相应的 L2CAP 连接响应消息。

**`NG_L2CAP_W4_L2CA_CON_RSP`** 在此状态下，远程端点存在，本地 L2CAP 实体已收到 L2CAP 连接请求。已向上层发送 L2CA 连接指示，本地 L2CAP 实体处理收到的 L2CAP 连接请求的部分正在等待相应的响应。响应可能需要执行安全检查。

**`NG_L2CAP_CONFIG`** 在此状态下，连接已建立，但双方仍在协商通道参数。重新协商通道参数时也可能进入此状态。进入 `NG_L2CAP_CONFIG` 状态之前，所有发出数据流量都会暂停，因为数据流量的流量参数将被重新协商。在远程通道端点进入 `NG_L2CAP_CONFIG` 状态之前，传入数据流量仍被接受。在 `NG_L2CAP_CONFIG` 状态中，如果只使用默认值，双方将发出 L2CAP 配置请求消息；将发送空消息。如果需要协商大量参数，将发送多个消息以避免任何 MTU 限制并增量协商。从 `NG_L2CAP_CONFIG` 状态转换到 `NG_L2CAP_OPEN` 状态需要双方都准备好。当 L2CAP 实体收到对其最终请求的肯定响应并肯定响应了远程设备的最终请求时，即认为已就绪。

**`NG_L2CAP_OPEN`** 在此状态下，连接已建立并配置，数据流可以继续。

**`NG_L2CAP_W4_L2CAP_DISCON_RSP`** 在此状态下，连接正在关闭，已发送 L2CAP 断开请求消息。正在等待相应的响应。

**`NG_L2CAP_W4_L2CA_DISCON_RSP`** 在此状态下，远程端点的连接正在关闭，已收到 L2CAP 断开请求消息。已向上层发送 L2CA 断开指示，通知 CID 的所有者远程端点正在关闭。在响应远程端点之前，正在等待上层相应的响应。

### 协议多路复用

L2CAP 支持协议多路复用，因为基带协议不支持任何标识在其上方多路复用的更高层协议的“类型”字段。L2CAP 能够区分上层协议，如服务发现协议、RFCOMM 和电话控制。

### 分段和重组

基带协议定义的数据包大小有限。大型 L2CAP 数据包在通过空中传输之前必须分段为多个较小的基带数据包。同样，多个接收到的基带数据包可以重组为单个较大的 L2CAP 数据包。

### 服务质量

L2CAP 连接建立过程允许交换有关两个蓝牙单元之间预期服务质量（QoS）的信息。

### 组

基带协议支持 piconet 概念，即一组使用同一时钟同步跳频的设备。L2CAP 组抽象允许实现将协议组高效映射到 piconet。

以下功能不在 L2CAP 职责范围内：

* L2CAP 不传输指定用于 SCO 链路的音频。
* L2CAP 不强制可靠通道或确保数据完整性，即 L2CAP 不执行重传或校验和计算。
* L2CAP 不支持可靠多播通道。
* L2CAP 不支持全局组名概念。

## 钩子

本节点类型支持以下钩子：

**`hci`** 蓝牙主机控制器接口下游钩子。

**`l2c`** 上层协议上游钩子。通常蓝牙 L2CAP 套接字层连接到此钩子。

**`ctl`** 控制钩子。通常蓝牙原始 L2CAP 套接字层连接到此钩子。

## 上层协议接口（L2CA 控制消息）

蓝牙规范规定 L2CA 请求必须阻塞直到响应就绪。L2CAP 节点使用 Netgraph 消息头中的 `token` 字段匹配 L2CA 请求和响应。上层协议必须填充 `token`。L2CAP 节点将排队请求并开始处理。随后，当响应就绪或超时发生时，L2CAP 节点将创建新的 Netgraph 消息，设置 `token` 和 `NFG_RESP` 标志，并将消息发送到上层。注意，L2CA 指示消息不会填充 `token`，也不会设置 `NGF_RESP` 标志。这没有理由，因为它们只是通知，不需要确认。

```sh
/* L2CA 数据包头 */
typedef struct {
        uint32_t token;  /* 在 L2CAP_L2CA_WRITE 中使用的令牌 */
        uint16_t length; /* 数据长度 */
        uint16_t lcid;   /* 本地通道 ID */
} __attribute__ ((packed)) ng_l2cap_l2ca_hdr_t;
```

**`NGM_L2CAP_L2CA_CON`** 请求创建代表到物理地址的逻辑连接的通道。输入参数为目标协议（PSM）和远程设备的 48 位地址（BD\_ADDR）。输出参数为本地 L2CAP 实体分配的本地 CID（LCID）和请求结果。如果结果指示待处理通知，状态值可能包含有关延迟连接建立的处理更多信息。

**`NGM_L2CAP_L2CA_CON_IND`** 此消息包含发出连接请求的远程设备地址、代表被请求通道的本地 CID、请求中包含的标识符以及请求所针对的 PSM 值等参数。

**`NGM_L2CAP_L2CA_CON_RSP`** 对连接请求事件指示发出响应。输入参数为远程设备的 48 位地址、请求中发送的标识符、本地 CID、响应代码和附加到响应代码的状态。输出参数为服务请求的结果。此原语在收到指示后最多只能调用一次。

**`NGM_L2CAP_L2CA_CFG`** 请求通道的初始配置（或重新配置）到新的通道参数集。输入参数为本地 CID 端点、新的传入可接收 MTU（InMTU）、新的传出流量规范以及刷新和链路超时。输出参数为结果、接受的传入 MTU（InMTU）、远程方的流量请求以及刷新和链路超时。

**`NGM_L2CAP_L2CA_CFG_IND`** 此消息包含指示请求已发送到的通道本地 CID、传出 MTU 大小（可跨通道发送的最大数据包）和描述传入数据特征的 flowspec 等参数。所有其他通道参数如果远程设备未提供，则设置为默认值。

**`NGM_L2CAP_L2CA_CFG_RSP`** 对配置请求事件指示发出响应。输入参数包括正在配置的端点的本地 CID、传出传输 MTU（可能等于或小于配置指示事件中的 OutMTU 参数）以及接受的传入流量 flowspec。输出参数为结果值。

**`NGM_L2CAP_L2CA_QOS_IND`** 此消息包含指示 QoS 协议被违反的远程蓝牙设备地址的参数。

**`NGM_L2CAP_L2CA_DISCON`** 请求断开通道。输入参数为代表本地通道端点的 CID。输出参数为结果。如果收到 L2CAP 断开响应，则结果为零，否则返回非零值。一旦请求断开，任何进程都无法从 CID 成功读取或写入。

**`NGM_L2CAP_L2CA_DISCON_IND`** 此消息包含指示请求已发送到的本地 CID 的参数。

**`NGM_L2CAP_L2CA_WRITE`** 对数据传输请求的响应。实际数据必须从相应的上游钩子接收，并且必须以如下定义的头部为前缀。输出参数为结果和写入的数据长度。

**`NGM_L2CAP_L2CA_GRP_CREATE`** 请求创建 CID 以代表到多个设备的逻辑连接。输入参数为传出无连接流量标记的 PSM 值和用于传入流量的过滤器。输出参数为代表本地端点的 CID。创建时，组为空，但目的地为 PSM 值的传入流量可读。*注意：此请求尚未实现。*

**`NGM_L2CAP_L2CA_GRP_CLOSE`** 使用此消息关闭一个组。*注意：此请求尚未实现。*

**`NGM_L2CAP_L2CA_GRP_ADD_MEMBER`** 请求向组中添加成员。输入参数包括代表组的 CID 和要添加的组成员的 BD\_ADDR。输出参数结果确认请求的成功或失败。*注意：此请求尚未实现。*

**`NGM_L2CAP_L2CA_GRP_REM_MEMBER`** 请求从组中移除成员。输入参数包括代表组的 CID 和要移除的组成员的 BD\_ADDR。输出参数结果确认请求的成功或失败。*注意：此请求尚未实现。*

**`NGM_L2CAP_L2CA_GRP_MEMBERSHIP`** 请求报告组成员。输入参数 CID 代表被查询的组。输出参数结果确认操作的成功或失败。如果结果成功，BD\_ADDR\_Lst 为组中 N 个成员的蓝牙地址列表。*注意：此请求尚未实现。*

**`NGM_L2CAP_L2CA_PING`** 发起 L2CA 回显请求消息并接收相应的 L2CAP 回显响应消息。输入参数为远程蓝牙设备 BD\_ADDR、回显数据和回显数据长度。输出参数为结果、回显数据和回显数据长度。

**`NGM_L2CAP_L2CA_GET_INFO`** 发起 L2CA 信息请求消息并接收相应的 L2CAP 信息响应消息。输入参数为远程蓝牙设备 BD\_ADDR 和信息类型。输出参数为结果、信息数据和信息数据大小。

**`NGM_L2CAP_L2CA_ENABLE_CLT`** 请求禁用（启用）无连接数据包的接收。输入参数为指示应阻止（解除阻止）的服务的 PSM 值和 Enable 标志。

## NETGRAPH 控制消息

本节点类型支持通用控制消息，以及以下消息：

**`NGM_L2CAP_NODE_GET_FLAGS`** 返回节点的当前状态。

**`NGM_L2CAP_NODE_GET_DEBUG`** 返回包含节点当前调试级别的整数。

**`NGM_L2CAP_NODE_SET_DEBUG`** 此命令接受一个整数参数，设置节点的当前调试级别。

**`NGM_L2CAP_NODE_GET_CON_LIST`** 返回活动基带连接列表（即 ACL 链路）。

**`NGM_L2CAP_NODE_GET_CHAN_LIST`** 返回活动 L2CAP 通道列表。

**`NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO`** 返回包含当前自动断开超时值（秒）的整数。

**`NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO`** 此命令接受一个整数并设置自动断开超时值（秒）。特殊值 0（零）禁用自动断开超时。

## 关闭

本节点在收到 `NGM_SHUTDOWN` 控制消息时关闭，或在所有钩子都已断开时关闭。

## 参见

[netgraph(4)](/man4/netgraph.4.md), l2control(8), l2ping(8), ngctl(8)

## 历史

`l2cap` 节点类型实现于 FreeBSD 5.0。

## 作者

Maksim Yevmenkin <m_evmenkin@yahoo.com>

## 缺陷

很可能存在缺陷。如发现请报告。
