2015-05-06 10:24:52 +03:00
|
|
|
/* l2cap.c - L2CAP handling */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2015 Intel Corporation
|
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2015-05-06 10:24:52 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-05-06 10:24:52 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2015-05-06 10:24:52 +03:00
|
|
|
*/
|
|
|
|
|
2015-04-28 10:43:14 +03:00
|
|
|
#include <nanokernel.h>
|
2015-06-03 10:35:58 -04:00
|
|
|
#include <arch/cpu.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
#include <toolchain.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2015-09-15 10:08:04 +02:00
|
|
|
#include <atomic.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
#include <misc/byteorder.h>
|
2015-09-30 14:49:25 +03:00
|
|
|
#include <misc/util.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-06-16 17:25:37 +03:00
|
|
|
#include <bluetooth/log.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
2015-09-15 10:08:04 +02:00
|
|
|
#include <bluetooth/driver.h>
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
|
|
#include "hci_core.h"
|
2015-06-15 11:05:35 +03:00
|
|
|
#include "conn_internal.h"
|
2015-04-28 10:43:14 +03:00
|
|
|
#include "l2cap.h"
|
2015-04-28 10:44:37 +03:00
|
|
|
#include "att.h"
|
2015-05-06 12:16:45 +03:00
|
|
|
#include "smp.h"
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-13 14:06:34 +03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_L2CAP)
|
|
|
|
#undef BT_DBG
|
|
|
|
#define BT_DBG(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2015-09-30 14:49:25 +03:00
|
|
|
#define L2CAP_LE_MIN_MTU 23
|
|
|
|
#define L2CAP_LE_MAX_CREDITS 1
|
|
|
|
|
|
|
|
#define L2CAP_CID_START 0x0040
|
|
|
|
#define L2CAP_LE_CID_END 0x007f
|
|
|
|
|
2015-05-21 18:53:13 +03:00
|
|
|
static struct bt_l2cap_chan *channels;
|
2015-09-30 12:36:24 +03:00
|
|
|
static struct bt_l2cap_server *servers;
|
2015-05-21 18:53:13 +03:00
|
|
|
|
2015-04-28 10:43:14 +03:00
|
|
|
static uint8_t get_ident(struct bt_conn *conn)
|
|
|
|
{
|
2015-05-18 12:37:48 +03:00
|
|
|
conn->l2cap.ident++;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
|
|
|
/* handle integer overflow (0 is not valid) */
|
2015-05-18 12:37:48 +03:00
|
|
|
if (!conn->l2cap.ident) {
|
|
|
|
conn->l2cap.ident++;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-18 12:37:48 +03:00
|
|
|
return conn->l2cap.ident;
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
|
|
|
|
2015-05-21 18:53:13 +03:00
|
|
|
void bt_l2cap_chan_register(struct bt_l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
BT_DBG("CID 0x%04x\n", chan->cid);
|
|
|
|
|
|
|
|
chan->_next = channels;
|
|
|
|
channels = chan;
|
|
|
|
}
|
|
|
|
|
2015-09-30 12:36:24 +03:00
|
|
|
static struct bt_l2cap_server *l2cap_server_lookup_psm(uint16_t psm)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_server *server;
|
|
|
|
|
|
|
|
for (server = servers; server; server = server->_next) {
|
|
|
|
if (server->psm == psm) {
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_l2cap_server_register(struct bt_l2cap_server *server)
|
|
|
|
{
|
|
|
|
if (server->psm < 0x0080 || server->psm > 0x00ff || !server->accept) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if given PSM is already in use */
|
|
|
|
if (l2cap_server_lookup_psm(server->psm)) {
|
|
|
|
BT_DBG("PSM already registered\n");
|
|
|
|
return -EADDRINUSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("PSM 0x%04x\n", server->psm);
|
|
|
|
|
|
|
|
server->_next = servers;
|
|
|
|
servers = server;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-21 19:03:17 +03:00
|
|
|
void bt_l2cap_connected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
for (chan = channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->connected) {
|
|
|
|
chan->connected(conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_l2cap_disconnected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
for (chan = channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->disconnected) {
|
|
|
|
chan->disconnected(conn);
|
|
|
|
}
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
|
|
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->disconnected) {
|
|
|
|
chan->disconnected(conn);
|
|
|
|
}
|
|
|
|
}
|
2015-05-21 19:03:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void bt_l2cap_encrypt_change(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
for (chan = channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->encrypt_change) {
|
|
|
|
chan->encrypt_change(conn);
|
|
|
|
}
|
|
|
|
}
|
2015-09-30 14:49:25 +03:00
|
|
|
|
|
|
|
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->encrypt_change) {
|
|
|
|
chan->encrypt_change(conn);
|
|
|
|
}
|
|
|
|
}
|
2015-05-21 19:03:17 +03:00
|
|
|
}
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
struct bt_buf *bt_l2cap_create_pdu(struct bt_conn *conn)
|
2015-04-28 10:43:14 +03:00
|
|
|
{
|
2015-05-20 14:58:18 +03:00
|
|
|
size_t head_reserve = sizeof(struct bt_l2cap_hdr) +
|
|
|
|
sizeof(struct bt_hci_acl_hdr) +
|
2015-07-15 13:53:17 +03:00
|
|
|
bt_dev.drv->head_reserve;
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
return bt_buf_get(BT_ACL_OUT, head_reserve);
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
void bt_l2cap_send(struct bt_conn *conn, uint16_t cid, struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_hdr *hdr;
|
2015-05-06 10:59:43 +03:00
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
hdr = bt_buf_push(buf, sizeof(*hdr));
|
|
|
|
hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
hdr->cid = sys_cpu_to_le16(cid);
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
bt_conn_send(conn, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rej_not_understood(struct bt_conn *conn, uint8_t ident)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_cmd_reject *rej;
|
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
buf = bt_l2cap_create_pdu(conn);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-04-28 10:43:14 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
hdr->code = BT_L2CAP_CMD_REJECT;
|
|
|
|
hdr->ident = ident;
|
|
|
|
hdr->len = sys_cpu_to_le16(sizeof(*rej));
|
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
rej = bt_buf_add(buf, sizeof(*rej));
|
2015-04-28 10:43:14 +03:00
|
|
|
rej->reason = sys_cpu_to_le16(BT_L2CAP_REJ_NOT_UNDERSTOOD);
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void le_conn_param_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_conn_param_rsp *rsp = (void *)buf->data;
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*rsp)) {
|
|
|
|
BT_ERR("Too small LE conn param rsp\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("LE conn param rsp result %u\n", sys_le16_to_cpu(rsp->result));
|
|
|
|
}
|
|
|
|
|
2015-09-07 18:25:59 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
2015-06-12 14:47:54 +02:00
|
|
|
static void le_conn_param_update_req(struct bt_conn *conn, uint8_t ident,
|
|
|
|
struct bt_buf *buf)
|
|
|
|
{
|
2015-07-29 13:45:33 +03:00
|
|
|
uint16_t min, max, latency, timeout;
|
|
|
|
bool params_valid;
|
2015-06-12 14:47:54 +02:00
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
|
|
|
struct bt_l2cap_conn_param_rsp *rsp;
|
|
|
|
struct bt_l2cap_conn_param_req *req = (void *)buf->data;
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
|
|
|
BT_ERR("Too small LE conn update param req\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->role != BT_HCI_ROLE_MASTER) {
|
2015-07-23 15:54:37 +02:00
|
|
|
rej_not_understood(conn, ident);
|
2015-06-12 14:47:54 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
min = sys_le16_to_cpu(req->min_interval);
|
|
|
|
max = sys_le16_to_cpu(req->max_interval);
|
|
|
|
latency = sys_le16_to_cpu(req->latency);
|
|
|
|
timeout = sys_le16_to_cpu(req->timeout);
|
|
|
|
|
|
|
|
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x timeout: 0x%4.4x",
|
|
|
|
min, max, latency, timeout);
|
|
|
|
|
|
|
|
buf = bt_l2cap_create_pdu(conn);
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-29 13:45:33 +03:00
|
|
|
params_valid = bt_le_conn_params_valid(min, max, latency, timeout);
|
2015-06-12 14:47:54 +02:00
|
|
|
|
|
|
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
|
|
|
hdr->code = BT_L2CAP_CONN_PARAM_RSP;
|
|
|
|
hdr->ident = ident;
|
|
|
|
hdr->len = sys_cpu_to_le16(sizeof(*rsp));
|
|
|
|
|
|
|
|
rsp = bt_buf_add(buf, sizeof(*rsp));
|
2015-07-29 13:45:33 +03:00
|
|
|
if (params_valid) {
|
|
|
|
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED);
|
|
|
|
} else {
|
|
|
|
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED);
|
|
|
|
}
|
|
|
|
|
2015-06-12 14:47:54 +02:00
|
|
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
|
|
|
|
2015-07-29 13:45:33 +03:00
|
|
|
if (params_valid) {
|
2015-07-15 14:08:53 +03:00
|
|
|
bt_conn_le_conn_update(conn, min, max, latency, timeout);
|
2015-06-12 14:47:54 +02:00
|
|
|
}
|
|
|
|
}
|
2015-09-07 18:25:59 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
2015-06-12 14:47:54 +02:00
|
|
|
|
2015-09-30 14:49:25 +03:00
|
|
|
static struct bt_l2cap_chan *l2cap_chan_lookup_dcid(struct bt_conn *conn,
|
|
|
|
uint16_t dcid)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->dcid == dcid)
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bt_l2cap_chan *l2cap_chan_lookup_scid(struct bt_conn *conn,
|
|
|
|
uint16_t scid)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
|
|
|
|
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->cid == scid) {
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_chan_alloc_cid(struct bt_conn *conn,
|
|
|
|
struct bt_l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
uint16_t cid;
|
|
|
|
|
|
|
|
if (chan->cid > 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try matching dcid first */
|
|
|
|
if (!l2cap_chan_lookup_scid(conn, chan->dcid)) {
|
|
|
|
chan->cid = chan->dcid;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Check conn type before assigning cid */
|
|
|
|
for (cid = L2CAP_CID_START; cid < L2CAP_LE_CID_END; cid++) {
|
|
|
|
if (!l2cap_chan_lookup_scid(conn, cid)) {
|
|
|
|
chan->cid = cid;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
l2cap_chan_alloc_cid(conn, chan);
|
|
|
|
|
|
|
|
/* Attach channel to the connection */
|
|
|
|
chan->_next = conn->l2cap.channels;
|
|
|
|
conn->l2cap.channels = chan;
|
|
|
|
|
|
|
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->cid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void le_conn_req(struct bt_conn *conn, uint8_t ident, struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
struct bt_l2cap_server *server;
|
|
|
|
struct bt_l2cap_le_conn_req *req = (void *)buf->data;
|
|
|
|
struct bt_l2cap_le_conn_rsp *rsp;
|
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
|
|
|
uint16_t psm, scid, mtu, mps;
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*req)) {
|
|
|
|
BT_ERR("Too small LE conn req packet size\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
psm = sys_le16_to_cpu(req->psm);
|
|
|
|
scid = sys_le16_to_cpu(req->scid);
|
|
|
|
mtu = sys_le16_to_cpu(req->mtu);
|
|
|
|
mps = sys_le16_to_cpu(req->mps);
|
|
|
|
|
|
|
|
BT_DBG("psm 0x%02x scid 0x%04x mtu 0x%04x mps 0x%04x\n", psm, scid,
|
|
|
|
mtu, mps);
|
|
|
|
|
|
|
|
if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) {
|
|
|
|
BT_ERR("Invalid LE-Conn Req params\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = bt_l2cap_create_pdu(conn);
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mtu = min(mtu, bt_buf_tailroom(buf));
|
|
|
|
|
|
|
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
|
|
|
hdr->code = BT_L2CAP_LE_CONN_RSP;
|
|
|
|
hdr->ident = ident;
|
|
|
|
hdr->len = sys_cpu_to_le16(sizeof(*rsp));
|
|
|
|
|
|
|
|
rsp = bt_buf_add(buf, sizeof(*rsp));
|
|
|
|
memset(rsp, 0, sizeof(*rsp));
|
|
|
|
|
|
|
|
/* Check if there is a server registered */
|
|
|
|
server = l2cap_server_lookup_psm(psm);
|
|
|
|
if (!psm) {
|
|
|
|
rsp->dcid = req->scid;
|
|
|
|
rsp->result = BT_L2CAP_ERR_PSM_NOT_SUPP;
|
|
|
|
goto rsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Add security check */
|
|
|
|
|
|
|
|
chan = l2cap_chan_lookup_dcid(conn, scid);
|
|
|
|
if (chan) {
|
|
|
|
rsp->dcid = req->scid;
|
|
|
|
rsp->result = BT_L2CAP_ERR_NO_RESOURCES;
|
|
|
|
goto rsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Request server to accept the new connection and allocate the
|
|
|
|
* channel.
|
|
|
|
*
|
|
|
|
* TODO: Handle different errors, it may be required to respond async.
|
|
|
|
*/
|
|
|
|
if (server->accept(conn, &chan) < 0) {
|
|
|
|
rsp->dcid = req->scid;
|
|
|
|
rsp->result = BT_L2CAP_ERR_NO_RESOURCES;
|
|
|
|
goto rsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->dcid = scid;
|
|
|
|
chan->mps = mps;
|
|
|
|
chan->mtu = mtu;
|
|
|
|
chan->credits_rx = L2CAP_LE_MAX_CREDITS;
|
|
|
|
chan->credits_tx = sys_le16_to_cpu(req->credits);
|
|
|
|
|
|
|
|
l2cap_chan_add(conn, chan);
|
|
|
|
|
|
|
|
if (chan->connected) {
|
|
|
|
chan->connected(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp->dcid = sys_cpu_to_le16(chan->cid);
|
|
|
|
rsp->mps = sys_cpu_to_le16(chan->mps);
|
|
|
|
rsp->mtu = sys_cpu_to_le16(chan->mtu);
|
|
|
|
rsp->credits = sys_cpu_to_le16(chan->credits_rx);
|
|
|
|
rsp->result = BT_L2CAP_SUCCESS;
|
|
|
|
|
|
|
|
rsp:
|
|
|
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
|
|
|
}
|
|
|
|
|
2015-04-28 10:43:14 +03:00
|
|
|
static void le_sig(struct bt_conn *conn, struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_sig_hdr *hdr = (void *)buf->data;
|
|
|
|
uint16_t len;
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
|
|
|
BT_ERR("Too small L2CAP LE signaling PDU\n");
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = sys_le16_to_cpu(hdr->len);
|
|
|
|
bt_buf_pull(buf, sizeof(*hdr));
|
|
|
|
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_DBG("LE signaling code 0x%02x ident %u len %u\n", hdr->code,
|
2015-04-28 10:43:14 +03:00
|
|
|
hdr->ident, len);
|
|
|
|
|
|
|
|
if (buf->len != len) {
|
|
|
|
BT_ERR("L2CAP length mismatch (%u != %u)\n", buf->len, len);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hdr->ident) {
|
|
|
|
BT_ERR("Invalid ident value in L2CAP PDU\n");
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (hdr->code) {
|
|
|
|
case BT_L2CAP_CONN_PARAM_RSP:
|
|
|
|
le_conn_param_rsp(conn, buf);
|
|
|
|
break;
|
2015-09-07 18:25:59 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
2015-06-12 14:47:54 +02:00
|
|
|
case BT_L2CAP_CONN_PARAM_REQ:
|
|
|
|
le_conn_param_update_req(conn, hdr->ident, buf);
|
|
|
|
break;
|
2015-09-07 18:25:59 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
2015-09-30 14:49:25 +03:00
|
|
|
case BT_L2CAP_LE_CONN_REQ:
|
|
|
|
le_conn_req(conn, hdr->ident, buf);
|
|
|
|
break;
|
2015-04-28 10:43:14 +03:00
|
|
|
default:
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_WARN("Unknown L2CAP PDU code 0x%02x\n", hdr->code);
|
2015-04-28 10:43:14 +03:00
|
|
|
rej_not_understood(conn, hdr->ident);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
drop:
|
|
|
|
bt_buf_put(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_l2cap_recv(struct bt_conn *conn, struct bt_buf *buf)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_hdr *hdr = (void *)buf->data;
|
2015-05-21 18:53:13 +03:00
|
|
|
struct bt_l2cap_chan *chan;
|
2015-04-28 10:43:14 +03:00
|
|
|
uint16_t cid;
|
|
|
|
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
|
|
|
BT_ERR("Too small L2CAP PDU received\n");
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cid = sys_le16_to_cpu(hdr->cid);
|
|
|
|
bt_buf_pull(buf, sizeof(*hdr));
|
|
|
|
|
|
|
|
BT_DBG("Packet for CID %u len %u\n", cid, buf->len);
|
|
|
|
|
2015-05-21 18:53:13 +03:00
|
|
|
for (chan = channels; chan; chan = chan->_next) {
|
|
|
|
if (chan->cid == cid) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!chan) {
|
2015-09-30 14:49:25 +03:00
|
|
|
chan = l2cap_chan_lookup_scid(conn, cid);
|
|
|
|
if (!chan) {
|
|
|
|
BT_WARN("Ignoring data for unknown CID 0x%04x\n", cid);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
|
|
|
chan->recv(conn, buf);
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
|
|
|
|
2015-08-23 20:17:11 +02:00
|
|
|
int bt_l2cap_update_conn_param(struct bt_conn *conn)
|
2015-04-28 10:43:14 +03:00
|
|
|
{
|
|
|
|
struct bt_l2cap_sig_hdr *hdr;
|
|
|
|
struct bt_l2cap_conn_param_req *req;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
buf = bt_l2cap_create_pdu(conn);
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!buf) {
|
2015-08-23 20:17:11 +02:00
|
|
|
return -ENOBUFS;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
2015-04-28 10:43:14 +03:00
|
|
|
hdr->code = BT_L2CAP_CONN_PARAM_REQ;
|
|
|
|
hdr->ident = get_ident(conn);
|
|
|
|
hdr->len = sys_cpu_to_le16(sizeof(*req));
|
|
|
|
|
2015-05-18 10:18:21 +03:00
|
|
|
req = bt_buf_add(buf, sizeof(*req));
|
2015-04-28 10:43:14 +03:00
|
|
|
req->min_interval = sys_cpu_to_le16(LE_CONN_MIN_INTERVAL);
|
|
|
|
req->max_interval = sys_cpu_to_le16(LE_CONN_MAX_INTERVAL);
|
|
|
|
req->latency = sys_cpu_to_le16(LE_CONN_LATENCY);
|
|
|
|
req->timeout = sys_cpu_to_le16(LE_CONN_TIMEOUT);
|
|
|
|
|
2015-05-20 14:58:18 +03:00
|
|
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
2015-08-23 20:17:11 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-04-28 10:43:14 +03:00
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
2015-07-13 11:10:17 +03:00
|
|
|
int bt_l2cap_init(void)
|
2015-05-21 18:53:13 +03:00
|
|
|
{
|
2015-07-13 11:10:17 +03:00
|
|
|
int err;
|
|
|
|
|
2015-05-21 18:53:13 +03:00
|
|
|
static struct bt_l2cap_chan chan = {
|
|
|
|
.cid = BT_L2CAP_CID_LE_SIG,
|
|
|
|
.recv = le_sig,
|
|
|
|
};
|
|
|
|
|
|
|
|
bt_att_init();
|
2015-07-13 11:10:17 +03:00
|
|
|
|
|
|
|
err = bt_smp_init();
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2015-05-21 18:53:13 +03:00
|
|
|
|
|
|
|
bt_l2cap_chan_register(&chan);
|
2015-07-13 11:10:17 +03:00
|
|
|
|
|
|
|
return 0;
|
2015-05-21 18:53:13 +03:00
|
|
|
}
|