逻辑链路控制与适配协议(L2CAP)
L2CAP 层支持面向连接的通道,可通过配置选项 CONFIG_BT_L2CAP_DYNAMIC_CHANNEL 启用。这些通道透明地支持分段与重组,还支持基于信用的流控(credit based flow control),因此适用于数据流。
通道实例由 bt_l2cap_chan 结构体表示,该结构体包含 bt_l2cap_chan_ops 结构体中的回调,用于在通道已连接、已断开或加密状态发生变化时发出通知。除此之外,它还包含 recv 回调,每当收到传入数据时都会调用。通过返回 0,或在处理是异步的时使用 bt_l2cap_chan_recv_complete() API,可以将这样接收到的数据标记为已处理。
备注
recv 回调直接从 RX 线程调用,因此不建议在其中长时间阻塞。
发送数据可使用 bt_l2cap_chan_send() API;注意,当没有可用信用额度时该 API 可能会阻塞,并在有更多信用额度可用时立即恢复。
可使用 bt_l2cap_server_register() API 注册服务器,并传入 bt_l2cap_server 结构体,其中指定了它应监听的 psm、所需的安全级别 sec_level,以及用于授权传入连接请求并分配通道实例的回调 accept。分配的对象必须是 bt_l2cap_le_chan 类型,通过 accept 回调返回的通道引用应指向该对象的 chan 成员,如下例所示。
#define PSM 0x29 /* example PSM */
static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf);
static void l2cap_connected(struct bt_l2cap_chan *chan);
static void l2cap_disconnected(struct bt_l2cap_chan *chan);
/* ops used for every channel instance */
static struct bt_l2cap_chan_ops l2cap_ops = {
.recv = l2cap_recv,
.connected = l2cap_connected,
.disconnected = l2cap_disconnected,
};
/* Fixed array of channel instances, one per possible connection */
static struct bt_l2cap_le_chan l2cap_chans[CONFIG_BT_MAX_CONN];
static int accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
struct bt_l2cap_chan **chan)
{
uint8_t conn_index = bt_conn_index(conn);
/* initialize the chosen entry */
(void)memset(&l2cap_chans[conn_index], 0, sizeof(l2cap_chans[conn_index]));
l2cap_chans[conn_index].chan.ops = &l2cap_ops;
*chan = &l2cap_chans[conn_index].chan;
printk("L2CAP channel accepted, assigned chan[%d]\n", conn_index);
return 0; /* accept */
}
static struct bt_l2cap_server server = {
.psm = PSM,
.sec_level = BT_SECURITY_L1,
.accept = accept_cb,
};
acceptor(服务器)示例中提供了演示 L2CAP 动态通道的完整可运行示例:L2CAP Connection Oriented Channels (Acceptor)
固定通道
用户还可以使用 BT_L2CAP_FIXED_CHANNEL_DEFINE 宏定义固定通道。固定通道在连接时初始化,不支持分段。注意,即使 accept 回调以 bt_l2cap_chan 形式传递通道引用,分配的对象也必须是 bt_l2cap_le_chan 类型,且该引用应指向其 chan 成员。下面给出了定义固定通道的示例。
static struct bt_l2cap_le_chan fixed_chan[CONFIG_BT_MAX_CONN];
/* Callbacks are assumed to be defined prior. */
static struct bt_l2cap_chan_ops ops = {
.recv = recv_cb,
.sent = sent_cb,
.connected = connected_cb,
.disconnected = disconnected_cb,
};
static int l2cap_fixed_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
uint8_t conn_index = bt_conn_index(conn);
fixed_chan[conn_index] = (struct bt_l2cap_le_chan){
.chan.ops = &ops,
};
*chan = &fixed_chan[conn_index].chan;
return 0;
}
BT_L2CAP_FIXED_CHANNEL_DEFINE(fixed_channel) = {
.cid = 0x0010,
.accept = l2cap_fixed_accept,
};
客户端通道
可使用 bt_l2cap_chan_connect() API 发起客户端通道,并使用 bt_l2cap_chan_disconnect() API 断开。注意,后者也可以断开由服务器创建的通道实例。
完整示例请参见 initiator(客户端)示例:L2CAP Connection Oriented Channels (Initiator)