Bluetooth: Refactor fixed channel data
This refactorer fixed channel data so that the channel itself carries any extra context necessary. Change-Id: Iea0f29fb7913a29dccdcbef72d92ec4cf4711bf3 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
6c82a63e8b
commit
4197aa814c
7 changed files with 632 additions and 418 deletions
|
@ -72,9 +72,8 @@ struct bt_att_req {
|
||||||
|
|
||||||
/* ATT channel specific context */
|
/* ATT channel specific context */
|
||||||
struct bt_att {
|
struct bt_att {
|
||||||
/* The connection this context is associated with */
|
/* The channel this context is associated with */
|
||||||
struct bt_conn *conn;
|
struct bt_l2cap_chan chan;
|
||||||
uint16_t mtu;
|
|
||||||
struct bt_att_req req;
|
struct bt_att_req req;
|
||||||
/* TODO: Allow more than one pending request */
|
/* TODO: Allow more than one pending request */
|
||||||
};
|
};
|
||||||
|
@ -128,9 +127,9 @@ static void send_err_rsp(struct bt_conn *conn, uint8_t req, uint16_t handle,
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf);
|
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_mtu_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_mtu_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_exchange_mtu_req *req;
|
struct bt_att_exchange_mtu_req *req;
|
||||||
struct bt_att_exchange_mtu_rsp *rsp;
|
struct bt_att_exchange_mtu_rsp *rsp;
|
||||||
struct bt_buf *pdu;
|
struct bt_buf *pdu;
|
||||||
|
@ -164,16 +163,21 @@ static uint8_t att_mtu_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, pdu);
|
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, pdu);
|
||||||
|
|
||||||
att->mtu = min(mtu_client, mtu_server);
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484:
|
||||||
|
*
|
||||||
|
* A device's Exchange MTU Request shall contain the same MTU as the
|
||||||
|
* device's Exchange MTU Response (i.e. the MTU shall be symmetric).
|
||||||
|
*/
|
||||||
|
att->chan.rx.mtu = min(mtu_client, mtu_server);
|
||||||
|
att->chan.tx.mtu = att->chan.rx.mtu;
|
||||||
|
|
||||||
BT_DBG("Negotiated MTU %u\n", att->mtu);
|
BT_DBG("Negotiated MTU %u\n", att->chan.rx.mtu);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_rsp(struct bt_conn *conn, void *pdu, uint16_t len,
|
static uint8_t att_handle_rsp(struct bt_att *att, void *pdu, uint16_t len,
|
||||||
uint8_t err)
|
uint8_t err)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
|
||||||
struct bt_att_req req;
|
struct bt_att_req req;
|
||||||
|
|
||||||
if (!att->req.func) {
|
if (!att->req.func) {
|
||||||
|
@ -184,16 +188,15 @@ static uint8_t att_handle_rsp(struct bt_conn *conn, void *pdu, uint16_t len,
|
||||||
memcpy(&req, &att->req, sizeof(req));
|
memcpy(&req, &att->req, sizeof(req));
|
||||||
att->req.func = NULL;
|
att->req.func = NULL;
|
||||||
|
|
||||||
req.func(conn, err, pdu, len, req.user_data);
|
req.func(att->chan.conn, err, pdu, len, req.user_data);
|
||||||
|
|
||||||
att_req_destroy(&req);
|
att_req_destroy(&req);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_mtu_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_mtu_rsp(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
|
||||||
struct bt_att_exchange_mtu_rsp *rsp;
|
struct bt_att_exchange_mtu_rsp *rsp;
|
||||||
uint16_t mtu;
|
uint16_t mtu;
|
||||||
|
|
||||||
|
@ -209,19 +212,28 @@ static uint8_t att_mtu_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
/* Check if MTU is valid */
|
/* Check if MTU is valid */
|
||||||
if (mtu < BT_ATT_DEFAULT_LE_MTU) {
|
if (mtu < BT_ATT_DEFAULT_LE_MTU) {
|
||||||
return att_handle_rsp(conn, NULL, 0, BT_ATT_ERR_INVALID_PDU);
|
return att_handle_rsp(att, NULL, 0, BT_ATT_ERR_INVALID_PDU);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clip MTU based on the maximum amount of data bt_buf can hold
|
/* Clip MTU based on the maximum amount of data bt_buf can hold
|
||||||
* excluding L2CAP, ACL and driver headers.
|
* excluding L2CAP, ACL and driver headers.
|
||||||
*/
|
*/
|
||||||
att->mtu = min(mtu, BT_BUF_MAX_DATA - (sizeof(struct bt_l2cap_hdr) +
|
att->chan.rx.mtu = min(mtu, BT_BUF_MAX_DATA -
|
||||||
|
(sizeof(struct bt_l2cap_hdr) +
|
||||||
sizeof(struct bt_hci_acl_hdr) +
|
sizeof(struct bt_hci_acl_hdr) +
|
||||||
bt_dev.drv->head_reserve));
|
bt_dev.drv->head_reserve));
|
||||||
|
|
||||||
BT_DBG("Negotiated MTU %u\n", att->mtu);
|
|
||||||
|
|
||||||
return att_handle_rsp(conn, rsp, buf->len, 0);
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484:
|
||||||
|
*
|
||||||
|
* A device's Exchange MTU Request shall contain the same MTU as the
|
||||||
|
* device's Exchange MTU Response (i.e. the MTU shall be symmetric).
|
||||||
|
*/
|
||||||
|
att->chan.tx.mtu = att->chan.rx.mtu;
|
||||||
|
|
||||||
|
BT_DBG("Negotiated MTU %u\n", att->chan.rx.mtu);
|
||||||
|
|
||||||
|
return att_handle_rsp(att, rsp, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool range_is_valid(uint16_t start, uint16_t end, uint16_t *err)
|
static bool range_is_valid(uint16_t start, uint16_t end, uint16_t *err)
|
||||||
|
@ -246,7 +258,7 @@ static bool range_is_valid(uint16_t start, uint16_t end, uint16_t *err)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct find_info_data {
|
struct find_info_data {
|
||||||
struct bt_conn *conn;
|
struct bt_att *att;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att_find_info_rsp *rsp;
|
struct bt_att_find_info_rsp *rsp;
|
||||||
union {
|
union {
|
||||||
|
@ -258,7 +270,7 @@ struct find_info_data {
|
||||||
static uint8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data)
|
static uint8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
{
|
{
|
||||||
struct find_info_data *data = user_data;
|
struct find_info_data *data = user_data;
|
||||||
struct bt_att *att = data->conn->att;
|
struct bt_att *att = data->att;
|
||||||
|
|
||||||
BT_DBG("handle 0x%04x\n", attr->handle);
|
BT_DBG("handle 0x%04x\n", attr->handle);
|
||||||
|
|
||||||
|
@ -280,8 +292,9 @@ static uint8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
data->info16->handle = sys_cpu_to_le16(attr->handle);
|
data->info16->handle = sys_cpu_to_le16(attr->handle);
|
||||||
data->info16->uuid = sys_cpu_to_le16(attr->uuid->u16);
|
data->info16->uuid = sys_cpu_to_le16(attr->uuid->u16);
|
||||||
|
|
||||||
return att->mtu - data->buf->len > sizeof(*data->info16) ?
|
if (att->chan.tx.mtu - data->buf->len > sizeof(*data->info16)) {
|
||||||
BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
case BT_ATT_INFO_128:
|
case BT_ATT_INFO_128:
|
||||||
if (attr->uuid->type != BT_UUID_128) {
|
if (attr->uuid->type != BT_UUID_128) {
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
@ -293,16 +306,19 @@ static uint8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
memcpy(data->info128->uuid, attr->uuid->u128,
|
memcpy(data->info128->uuid, attr->uuid->u128,
|
||||||
sizeof(data->info128->uuid));
|
sizeof(data->info128->uuid));
|
||||||
|
|
||||||
return att->mtu - data->buf->len > sizeof(*data->info128) ?
|
if (att->chan.tx.mtu - data->buf->len >
|
||||||
BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
sizeof(*data->info128)) {
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_find_info_rsp(struct bt_conn *conn, uint16_t start_handle,
|
static uint8_t att_find_info_rsp(struct bt_att *att, uint16_t start_handle,
|
||||||
uint16_t end_handle)
|
uint16_t end_handle)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct find_info_data data;
|
struct find_info_data data;
|
||||||
|
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
|
@ -312,7 +328,7 @@ static uint8_t att_find_info_rsp(struct bt_conn *conn, uint16_t start_handle,
|
||||||
return BT_ATT_ERR_UNLIKELY;
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.conn = conn;
|
data.att = att;
|
||||||
bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data);
|
bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data);
|
||||||
|
|
||||||
if (!data.rsp) {
|
if (!data.rsp) {
|
||||||
|
@ -328,8 +344,9 @@ static uint8_t att_find_info_rsp(struct bt_conn *conn, uint16_t start_handle,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_find_info_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_find_info_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_find_info_req *req;
|
struct bt_att_find_info_req *req;
|
||||||
uint16_t start_handle, end_handle, err_handle;
|
uint16_t start_handle, end_handle, err_handle;
|
||||||
|
|
||||||
|
@ -347,11 +364,11 @@ static uint8_t att_find_info_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return att_find_info_rsp(conn, start_handle, end_handle);
|
return att_find_info_rsp(att, start_handle, end_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct find_type_data {
|
struct find_type_data {
|
||||||
struct bt_conn *conn;
|
struct bt_att *att;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att_handle_group *group;
|
struct bt_att_handle_group *group;
|
||||||
const void *value;
|
const void *value;
|
||||||
|
@ -361,7 +378,8 @@ struct find_type_data {
|
||||||
static uint8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
static uint8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
{
|
{
|
||||||
struct find_type_data *data = user_data;
|
struct find_type_data *data = user_data;
|
||||||
struct bt_att *att = data->conn->att;
|
struct bt_att *att = data->att;
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
int read;
|
int read;
|
||||||
uint8_t uuid[16];
|
uint8_t uuid[16];
|
||||||
|
|
||||||
|
@ -376,11 +394,11 @@ static uint8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
BT_DBG("handle 0x%04x\n", attr->handle);
|
BT_DBG("handle 0x%04x\n", attr->handle);
|
||||||
|
|
||||||
/* stop if there is no space left */
|
/* stop if there is no space left */
|
||||||
if (att->mtu - data->buf->len < sizeof(*data->group))
|
if (att->chan.tx.mtu - data->buf->len < sizeof(*data->group))
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
|
||||||
/* Read attribute value and store in the buffer */
|
/* Read attribute value and store in the buffer */
|
||||||
read = attr->read(data->conn, attr, uuid, sizeof(uuid), 0);
|
read = attr->read(conn, attr, uuid, sizeof(uuid), 0);
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
/* TODO: Return an error if this fails */
|
/* TODO: Return an error if this fails */
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
@ -432,8 +450,9 @@ static uint8_t att_find_type_rsp(struct bt_conn *conn, uint16_t start_handle,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_find_type_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_find_type_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_find_type_req *req;
|
struct bt_att_find_type_req *req;
|
||||||
uint16_t start_handle, end_handle, err_handle, type;
|
uint16_t start_handle, end_handle, err_handle, type;
|
||||||
uint8_t *value;
|
uint8_t *value;
|
||||||
|
@ -490,7 +509,7 @@ static bool uuid_create(struct bt_uuid *uuid, struct bt_buf *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct read_type_data {
|
struct read_type_data {
|
||||||
struct bt_conn *conn;
|
struct bt_att *att;
|
||||||
struct bt_uuid *uuid;
|
struct bt_uuid *uuid;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att_read_type_rsp *rsp;
|
struct bt_att_read_type_rsp *rsp;
|
||||||
|
@ -500,7 +519,8 @@ struct read_type_data {
|
||||||
static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
{
|
{
|
||||||
struct read_type_data *data = user_data;
|
struct read_type_data *data = user_data;
|
||||||
struct bt_att *att = data->conn->att;
|
struct bt_att *att = data->att;
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
/* Skip if doesn't match */
|
/* Skip if doesn't match */
|
||||||
|
@ -515,8 +535,8 @@ static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
data->item->handle = sys_cpu_to_le16(attr->handle);
|
data->item->handle = sys_cpu_to_le16(attr->handle);
|
||||||
|
|
||||||
/* Read attribute value and store in the buffer */
|
/* Read attribute value and store in the buffer */
|
||||||
read = attr->read(data->conn, attr, data->buf->data + data->buf->len,
|
read = attr->read(conn, attr, data->buf->data + data->buf->len,
|
||||||
att->mtu - data->buf->len, 0);
|
att->chan.tx.mtu - data->buf->len, 0);
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
/* TODO: Handle read errors */
|
/* TODO: Handle read errors */
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
@ -534,13 +554,14 @@ static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
bt_buf_add(data->buf, read);
|
bt_buf_add(data->buf, read);
|
||||||
|
|
||||||
/* return true only if there are still space for more items */
|
/* return true only if there are still space for more items */
|
||||||
return att->mtu - data->buf->len > data->rsp->len ?
|
return att->chan.tx.mtu - data->buf->len > data->rsp->len ?
|
||||||
BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_type_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
static uint8_t att_read_type_rsp(struct bt_att *att, struct bt_uuid *uuid,
|
||||||
uint16_t start_handle, uint16_t end_handle)
|
uint16_t start_handle, uint16_t end_handle)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct read_type_data data;
|
struct read_type_data data;
|
||||||
|
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
|
@ -551,7 +572,7 @@ static uint8_t att_read_type_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
||||||
return BT_ATT_ERR_UNLIKELY;
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.conn = conn;
|
data.att = att;
|
||||||
data.uuid = uuid;
|
data.uuid = uuid;
|
||||||
data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp));
|
data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp));
|
||||||
data.rsp->len = 0;
|
data.rsp->len = 0;
|
||||||
|
@ -571,8 +592,9 @@ static uint8_t att_read_type_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_type_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_read_type_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_read_type_req *req;
|
struct bt_att_read_type_req *req;
|
||||||
uint16_t start_handle, end_handle, err_handle;
|
uint16_t start_handle, end_handle, err_handle;
|
||||||
struct bt_uuid uuid;
|
struct bt_uuid uuid;
|
||||||
|
@ -602,7 +624,7 @@ static uint8_t att_read_type_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return att_read_type_rsp(conn, &uuid, start_handle, end_handle);
|
return att_read_type_rsp(att, &uuid, start_handle, end_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t err_to_att(int err)
|
static uint8_t err_to_att(int err)
|
||||||
|
@ -654,7 +676,7 @@ static uint8_t check_perm(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct read_data {
|
struct read_data {
|
||||||
struct bt_conn *conn;
|
struct bt_att *att;
|
||||||
uint16_t offset;
|
uint16_t offset;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att_read_rsp *rsp;
|
struct bt_att_read_rsp *rsp;
|
||||||
|
@ -664,7 +686,8 @@ struct read_data {
|
||||||
static uint8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)
|
static uint8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
{
|
{
|
||||||
struct read_data *data = user_data;
|
struct read_data *data = user_data;
|
||||||
struct bt_att *att = data->conn->att;
|
struct bt_att *att = data->att;
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
BT_DBG("handle 0x%04x\n", attr->handle);
|
BT_DBG("handle 0x%04x\n", attr->handle);
|
||||||
|
@ -683,14 +706,14 @@ static uint8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check attribute permissions */
|
/* Check attribute permissions */
|
||||||
data->err = check_perm(data->conn, attr, BT_GATT_PERM_READ_MASK);
|
data->err = check_perm(conn, attr, BT_GATT_PERM_READ_MASK);
|
||||||
if (data->err) {
|
if (data->err) {
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read attribute value and store in the buffer */
|
/* Read attribute value and store in the buffer */
|
||||||
read = attr->read(data->conn, attr, data->buf->data + data->buf->len,
|
read = attr->read(conn, attr, data->buf->data + data->buf->len,
|
||||||
att->mtu - data->buf->len, data->offset);
|
att->chan.tx.mtu - data->buf->len, data->offset);
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
data->err = err_to_att(read);
|
data->err = err_to_att(read);
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
@ -701,9 +724,10 @@ static uint8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
return BT_GATT_ITER_CONTINUE;
|
return BT_GATT_ITER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp,
|
static uint8_t att_read_rsp(struct bt_att *att, uint8_t op, uint8_t rsp,
|
||||||
uint16_t handle, uint16_t offset)
|
uint16_t handle, uint16_t offset)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct read_data data;
|
struct read_data data;
|
||||||
|
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
|
@ -717,7 +741,7 @@ static uint8_t att_read_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp,
|
||||||
return BT_ATT_ERR_UNLIKELY;
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.conn = conn;
|
data.att = att;
|
||||||
data.offset = offset;
|
data.offset = offset;
|
||||||
|
|
||||||
/* Pre-set error if no attr will be found in handle */
|
/* Pre-set error if no attr will be found in handle */
|
||||||
|
@ -738,7 +762,7 @@ static uint8_t att_read_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_read_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_att_read_req *req;
|
struct bt_att_read_req *req;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
@ -749,11 +773,11 @@ static uint8_t att_read_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
BT_DBG("handle 0x%04x\n", handle);
|
BT_DBG("handle 0x%04x\n", handle);
|
||||||
|
|
||||||
return att_read_rsp(conn, BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP,
|
return att_read_rsp(att, BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP,
|
||||||
handle, 0);
|
handle, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_blob_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_read_blob_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_att_read_blob_req *req;
|
struct bt_att_read_blob_req *req;
|
||||||
uint16_t handle, offset;
|
uint16_t handle, offset;
|
||||||
|
@ -765,12 +789,13 @@ static uint8_t att_read_blob_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
BT_DBG("handle 0x%04x offset %u\n", handle, offset);
|
BT_DBG("handle 0x%04x offset %u\n", handle, offset);
|
||||||
|
|
||||||
return att_read_rsp(conn, BT_ATT_OP_READ_BLOB_REQ,
|
return att_read_rsp(att, BT_ATT_OP_READ_BLOB_REQ,
|
||||||
BT_ATT_OP_READ_BLOB_RSP, handle, offset);
|
BT_ATT_OP_READ_BLOB_RSP, handle, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_mult_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_read_mult_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct read_data data;
|
struct read_data data;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
|
@ -781,7 +806,7 @@ static uint8_t att_read_mult_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return BT_ATT_ERR_UNLIKELY;
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.conn = conn;
|
data.att = att;
|
||||||
|
|
||||||
while (buf->len >= sizeof(uint16_t)) {
|
while (buf->len >= sizeof(uint16_t)) {
|
||||||
handle = bt_buf_pull_le16(buf);
|
handle = bt_buf_pull_le16(buf);
|
||||||
|
@ -815,7 +840,7 @@ static uint8_t att_read_mult_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct read_group_data {
|
struct read_group_data {
|
||||||
struct bt_conn *conn;
|
struct bt_att *att;
|
||||||
struct bt_uuid *uuid;
|
struct bt_uuid *uuid;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att_read_group_rsp *rsp;
|
struct bt_att_read_group_rsp *rsp;
|
||||||
|
@ -825,7 +850,8 @@ struct read_group_data {
|
||||||
static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
{
|
{
|
||||||
struct read_group_data *data = user_data;
|
struct read_group_data *data = user_data;
|
||||||
struct bt_att *att = data->conn->att;
|
struct bt_att *att = data->att;
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
/* If UUID don't match update group end_handle */
|
/* If UUID don't match update group end_handle */
|
||||||
|
@ -839,7 +865,8 @@ static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
BT_DBG("handle 0x%04x\n", attr->handle);
|
BT_DBG("handle 0x%04x\n", attr->handle);
|
||||||
|
|
||||||
/* Stop if there is no space left */
|
/* Stop if there is no space left */
|
||||||
if (data->rsp->len && att->mtu - data->buf->len < data->rsp->len)
|
if (data->rsp->len &&
|
||||||
|
att->chan.tx.mtu - data->buf->len < data->rsp->len)
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
|
||||||
/* Fast foward to next group position */
|
/* Fast foward to next group position */
|
||||||
|
@ -850,8 +877,8 @@ static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
data->group->end_handle = sys_cpu_to_le16(attr->handle);
|
||||||
|
|
||||||
/* Read attribute value and store in the buffer */
|
/* Read attribute value and store in the buffer */
|
||||||
read = attr->read(data->conn, attr, data->buf->data + data->buf->len,
|
read = attr->read(conn, attr, data->buf->data + data->buf->len,
|
||||||
att->mtu - data->buf->len, 0);
|
att->chan.tx.mtu - data->buf->len, 0);
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
/* TODO: Handle read errors */
|
/* TODO: Handle read errors */
|
||||||
return BT_GATT_ITER_STOP;
|
return BT_GATT_ITER_STOP;
|
||||||
|
@ -872,9 +899,10 @@ static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data)
|
||||||
return BT_GATT_ITER_CONTINUE;
|
return BT_GATT_ITER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_group_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
static uint8_t att_read_group_rsp(struct bt_att *att, struct bt_uuid *uuid,
|
||||||
uint16_t start_handle, uint16_t end_handle)
|
uint16_t start_handle, uint16_t end_handle)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct read_group_data data;
|
struct read_group_data data;
|
||||||
|
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
|
@ -885,7 +913,7 @@ static uint8_t att_read_group_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
||||||
return BT_ATT_ERR_UNLIKELY;
|
return BT_ATT_ERR_UNLIKELY;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.conn = conn;
|
data.att = att;
|
||||||
data.uuid = uuid;
|
data.uuid = uuid;
|
||||||
data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp));
|
data.rsp = bt_buf_add(data.buf, sizeof(*data.rsp));
|
||||||
data.rsp->len = 0;
|
data.rsp->len = 0;
|
||||||
|
@ -905,8 +933,9 @@ static uint8_t att_read_group_rsp(struct bt_conn *conn, struct bt_uuid *uuid,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_read_group_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_read_group_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_read_group_req *req;
|
struct bt_att_read_group_req *req;
|
||||||
uint16_t start_handle, end_handle, err_handle;
|
uint16_t start_handle, end_handle, err_handle;
|
||||||
struct bt_uuid uuid;
|
struct bt_uuid uuid;
|
||||||
|
@ -950,7 +979,7 @@ static uint8_t att_read_group_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return att_read_group_rsp(conn, &uuid, start_handle, end_handle);
|
return att_read_group_rsp(att, &uuid, start_handle, end_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct write_data {
|
struct write_data {
|
||||||
|
@ -1061,8 +1090,9 @@ static uint8_t att_write_rsp(struct bt_conn *conn, uint8_t op, uint8_t rsp,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_write_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_write_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_write_req *req;
|
struct bt_att_write_req *req;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
|
@ -1077,8 +1107,9 @@ static uint8_t att_write_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
handle, 0, buf->data, buf->len);
|
handle, 0, buf->data, buf->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_prepare_write_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_prepare_write_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_prepare_write_req *req;
|
struct bt_att_prepare_write_req *req;
|
||||||
uint16_t handle, offset;
|
uint16_t handle, offset;
|
||||||
|
|
||||||
|
@ -1154,8 +1185,9 @@ static uint8_t att_exec_write_rsp(struct bt_conn *conn, uint8_t flags)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_exec_write_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_exec_write_req(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_exec_write_req *req;
|
struct bt_att_exec_write_req *req;
|
||||||
|
|
||||||
req = (void *)buf->data;
|
req = (void *)buf->data;
|
||||||
|
@ -1165,8 +1197,9 @@ static uint8_t att_exec_write_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return att_exec_write_rsp(conn, req->flags);
|
return att_exec_write_rsp(conn, req->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_write_cmd(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_write_cmd(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_write_cmd *req;
|
struct bt_att_write_cmd *req;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
|
@ -1184,8 +1217,9 @@ static uint8_t att_write_cmd(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return att_write_rsp(conn, 0, 0, handle, 0, buf->data, buf->len);
|
return att_write_rsp(conn, 0, 0, handle, 0, buf->data, buf->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_signed_write_cmd(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_signed_write_cmd(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
struct bt_att_signed_write_cmd *req;
|
struct bt_att_signed_write_cmd *req;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1236,9 +1270,8 @@ static int att_change_security(struct bt_conn *conn, uint8_t err)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_SMP */
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
||||||
|
|
||||||
static uint8_t att_error_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_error_rsp(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
|
||||||
struct bt_att_req *req = &att->req;
|
struct bt_att_req *req = &att->req;
|
||||||
struct bt_att_error_rsp *rsp;
|
struct bt_att_error_rsp *rsp;
|
||||||
struct bt_att_hdr *hdr;
|
struct bt_att_hdr *hdr;
|
||||||
|
@ -1262,7 +1295,7 @@ static uint8_t att_error_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Check if security needs to be changed */
|
/* Check if security needs to be changed */
|
||||||
if (!att_change_security(conn, err)) {
|
if (!att_change_security(att->chan.conn, err)) {
|
||||||
req->retrying = true;
|
req->retrying = true;
|
||||||
/* Wait security_changed: TODO: Handle fail case */
|
/* Wait security_changed: TODO: Handle fail case */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1270,83 +1303,84 @@ static uint8_t att_error_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
#endif /* CONFIG_BLUETOOTH_SMP */
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return att_handle_rsp(conn, NULL, 0, err);
|
return att_handle_rsp(att, NULL, 0, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_find_info_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_find_info_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_find_type_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_find_type_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_read_type_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_read_type_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_read_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_read_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_read_blob_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_read_blob_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_read_mult_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_read_mult_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_write_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_write_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_prepare_write_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_prepare_write_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_handle_exec_write_rsp(struct bt_conn *conn,
|
static uint8_t att_handle_exec_write_rsp(struct bt_att *att,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
return att_handle_rsp(conn, buf->data, buf->len, 0);
|
return att_handle_rsp(att, buf->data, buf->len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_notify(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_notify(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
handle = bt_buf_pull_le16(buf);
|
handle = bt_buf_pull_le16(buf);
|
||||||
|
@ -1358,8 +1392,9 @@ static uint8_t att_notify(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t att_indicate(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t att_indicate(struct bt_att *att, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = att->chan.conn;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
|
||||||
handle = bt_buf_pull_le16(buf);
|
handle = bt_buf_pull_le16(buf);
|
||||||
|
@ -1380,7 +1415,7 @@ static uint8_t att_indicate(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
uint8_t op;
|
uint8_t op;
|
||||||
uint8_t (*func)(struct bt_conn *conn, struct bt_buf *buf);
|
uint8_t (*func)(struct bt_att *att, struct bt_buf *buf);
|
||||||
uint8_t expect_len;
|
uint8_t expect_len;
|
||||||
} handlers[] = {
|
} handlers[] = {
|
||||||
{ BT_ATT_OP_ERROR_RSP, att_error_rsp,
|
{ BT_ATT_OP_ERROR_RSP, att_error_rsp,
|
||||||
|
@ -1435,13 +1470,14 @@ static const struct {
|
||||||
sizeof(struct bt_att_write_cmd) + sizeof(struct bt_att_signature) },
|
sizeof(struct bt_att_write_cmd) + sizeof(struct bt_att_signature) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
static void bt_att_recv(struct bt_l2cap_chan *chan, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_att *att = CONTAINER_OF(chan, struct bt_att, chan);
|
||||||
struct bt_att_hdr *hdr = (void *)buf->data;
|
struct bt_att_hdr *hdr = (void *)buf->data;
|
||||||
uint8_t err = BT_ATT_ERR_NOT_SUPPORTED;
|
uint8_t err = BT_ATT_ERR_NOT_SUPPORTED;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
BT_ASSERT(conn->att);
|
BT_ASSERT(att);
|
||||||
|
|
||||||
if (buf->len < sizeof(*hdr)) {
|
if (buf->len < sizeof(*hdr)) {
|
||||||
BT_ERR("Too small ATT PDU received\n");
|
BT_ERR("Too small ATT PDU received\n");
|
||||||
|
@ -1464,7 +1500,7 @@ static void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = handlers[i].func(conn, buf);
|
err = handlers[i].func(att, buf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1475,21 +1511,40 @@ static void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_DBG("ATT error 0x%02x", err);
|
BT_DBG("ATT error 0x%02x", err);
|
||||||
send_err_rsp(conn, hdr->code, 0, err);
|
send_err_rsp(chan->conn, hdr->code, 0, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
bt_buf_put(buf);
|
bt_buf_put(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct bt_att *att_chan_get(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
chan = bt_l2cap_lookup_rx_cid(conn, BT_L2CAP_CID_ATT);
|
||||||
|
if (!chan) {
|
||||||
|
BT_ERR("Unable to find ATT channel");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONTAINER_OF(chan, struct bt_att, chan);
|
||||||
|
}
|
||||||
|
|
||||||
struct bt_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
|
struct bt_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
|
||||||
{
|
{
|
||||||
struct bt_att_hdr *hdr;
|
struct bt_att_hdr *hdr;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
struct bt_att *att = conn->att;
|
struct bt_att *att;
|
||||||
|
|
||||||
if (len + sizeof(op) > att->mtu) {
|
att = att_chan_get(conn);
|
||||||
BT_WARN("ATT MTU exceeded, max %u, wanted %u\n", att->mtu, len);
|
if (!att) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len + sizeof(op) > att->chan.tx.mtu) {
|
||||||
|
BT_WARN("ATT MTU exceeded, max %u, wanted %u\n",
|
||||||
|
att->chan.tx.mtu, len);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1504,48 +1559,33 @@ struct bt_buf *bt_att_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_att_connected(struct bt_conn *conn)
|
static void bt_att_connected(struct bt_l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
int i;
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->tx.cid);
|
||||||
|
|
||||||
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
chan->tx.mtu = BT_ATT_DEFAULT_LE_MTU;
|
||||||
|
chan->rx.mtu = BT_ATT_DEFAULT_LE_MTU;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(bt_att_pool); i++) {
|
bt_gatt_connected(chan->conn);
|
||||||
struct bt_att *att = &bt_att_pool[i];
|
|
||||||
|
|
||||||
if (!att->conn) {
|
|
||||||
att->conn = conn;
|
|
||||||
conn->att = att;
|
|
||||||
att->mtu = BT_ATT_DEFAULT_LE_MTU;
|
|
||||||
bt_gatt_connected(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_ERR("No available ATT context for conn %p\n", conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_att_disconnected(struct bt_conn *conn)
|
static void bt_att_disconnected(struct bt_l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
struct bt_att *att = CONTAINER_OF(chan, struct bt_att, chan);
|
||||||
|
|
||||||
if (!att) {
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->tx.cid);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
|
||||||
|
|
||||||
conn->att = NULL;
|
|
||||||
memset(att, 0, sizeof(*att));
|
memset(att, 0, sizeof(*att));
|
||||||
bt_gatt_disconnected(conn);
|
bt_gatt_disconnected(chan->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_SMP)
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
||||||
static void security_changed(struct bt_conn *conn, bt_security_t level)
|
static void security_changed(struct bt_conn *conn, bt_security_t level)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
struct bt_att *att;
|
||||||
struct bt_att_req *req;
|
struct bt_att_req *req;
|
||||||
|
|
||||||
|
att = att_chan_get(conn);
|
||||||
if (!att) {
|
if (!att) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1568,16 +1608,44 @@ static struct bt_conn_cb conn_callbacks = {
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_BLUETOOTH_SMP */
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
||||||
|
|
||||||
void bt_att_init(void)
|
static int bt_att_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||||
{
|
{
|
||||||
static struct bt_l2cap_chan chan = {
|
int i;
|
||||||
.cid = BT_L2CAP_CID_ATT,
|
static struct bt_l2cap_chan_ops ops = {
|
||||||
.recv = bt_att_recv,
|
.connected = bt_att_connected,
|
||||||
.connected = bt_att_connected,
|
.disconnected = bt_att_disconnected,
|
||||||
.disconnected = bt_att_disconnected,
|
.recv = bt_att_recv,
|
||||||
};
|
};
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bt_att_pool); i++) {
|
||||||
|
struct bt_att *att = &bt_att_pool[i];
|
||||||
|
|
||||||
|
if (att->chan.conn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
att->chan.ops = &ops;
|
||||||
|
|
||||||
|
*chan = &att->chan;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_ERR("No available ATT context for conn %p\n", conn);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_att_init(void)
|
||||||
|
{
|
||||||
|
static struct bt_l2cap_fixed_chan chan = {
|
||||||
|
.cid = BT_L2CAP_CID_ATT,
|
||||||
|
.accept = bt_att_accept,
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_l2cap_fixed_chan_register(&chan);
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_SMP)
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
||||||
bt_conn_cb_register(&conn_callbacks);
|
bt_conn_cb_register(&conn_callbacks);
|
||||||
|
@ -1587,9 +1655,15 @@ void bt_att_init(void)
|
||||||
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
||||||
uint16_t bt_att_get_mtu(struct bt_conn *conn)
|
uint16_t bt_att_get_mtu(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_att *att = conn->att;
|
struct bt_att *att;
|
||||||
|
|
||||||
return att->mtu;
|
att = att_chan_get(conn);
|
||||||
|
if (!att) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tx and rx MTU shall be symmetric */
|
||||||
|
return att->chan.tx.mtu;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_att_send(struct bt_conn *conn, struct bt_buf *buf, bt_att_func_t func,
|
int bt_att_send(struct bt_conn *conn, struct bt_buf *buf, bt_att_func_t func,
|
||||||
|
@ -1602,7 +1676,7 @@ int bt_att_send(struct bt_conn *conn, struct bt_buf *buf, bt_att_func_t func,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
att = conn->att;
|
att = att_chan_get(conn);
|
||||||
if (!att) {
|
if (!att) {
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
}
|
}
|
||||||
|
@ -1646,7 +1720,7 @@ void bt_att_cancel(struct bt_conn *conn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
att = conn->att;
|
att = att_chan_get(conn);
|
||||||
if (!att) {
|
if (!att) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,6 @@ typedef enum {
|
||||||
BT_CONN_DISCONNECT,
|
BT_CONN_DISCONNECT,
|
||||||
} bt_conn_state_t;
|
} bt_conn_state_t;
|
||||||
|
|
||||||
/* L2CAP signaling channel specific context */
|
|
||||||
struct bt_conn_l2cap {
|
|
||||||
uint8_t ident;
|
|
||||||
void *channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* bt_conn flags: the flags defined here represent connection parameters */
|
/* bt_conn flags: the flags defined here represent connection parameters */
|
||||||
enum {
|
enum {
|
||||||
BT_CONN_AUTO_CONNECT,
|
BT_CONN_AUTO_CONNECT,
|
||||||
|
@ -63,10 +57,8 @@ struct bt_conn {
|
||||||
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
/* Fixed channel contexts */
|
/* L2CAP channels */
|
||||||
struct bt_conn_l2cap l2cap;
|
void *channels;
|
||||||
void *att;
|
|
||||||
void *smp;
|
|
||||||
|
|
||||||
uint8_t le_conn_interval;
|
uint8_t le_conn_interval;
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,10 @@
|
||||||
|
|
||||||
#include "hci_core.h"
|
#include "hci_core.h"
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
|
#if defined(CONFIG_BLUETOOTH_CONN)
|
||||||
#include "conn_internal.h"
|
#include "conn_internal.h"
|
||||||
#include "l2cap_internal.h"
|
#include "l2cap_internal.h"
|
||||||
|
#endif /* CONFIG_BLUETOOTH_CONN */
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
|
|
||||||
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
|
||||||
|
|
|
@ -47,22 +47,53 @@
|
||||||
#define L2CAP_CID_START 0x0040
|
#define L2CAP_CID_START 0x0040
|
||||||
#define L2CAP_LE_CID_END 0x007f
|
#define L2CAP_LE_CID_END 0x007f
|
||||||
|
|
||||||
static struct bt_l2cap_chan *channels;
|
static struct bt_l2cap_fixed_chan *channels;
|
||||||
static struct bt_l2cap_server *servers;
|
static struct bt_l2cap_server *servers;
|
||||||
|
|
||||||
|
/* L2CAP signalling channel specific context */
|
||||||
|
struct bt_l2cap {
|
||||||
|
/* The channel this context is associated with */
|
||||||
|
struct bt_l2cap_chan chan;
|
||||||
|
|
||||||
|
uint8_t ident;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct bt_l2cap bt_l2cap_pool[CONFIG_BLUETOOTH_MAX_CONN];
|
||||||
|
|
||||||
|
static struct bt_l2cap *l2cap_chan_get(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
chan = bt_l2cap_lookup_rx_cid(conn, BT_L2CAP_CID_LE_SIG);
|
||||||
|
if (!chan) {
|
||||||
|
BT_ERR("Unable to find L2CAP Signalling channel");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONTAINER_OF(chan, struct bt_l2cap, chan);
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t get_ident(struct bt_conn *conn)
|
static uint8_t get_ident(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
conn->l2cap.ident++;
|
|
||||||
|
|
||||||
/* handle integer overflow (0 is not valid) */
|
struct bt_l2cap *l2cap;
|
||||||
if (!conn->l2cap.ident) {
|
|
||||||
conn->l2cap.ident++;
|
l2cap = l2cap_chan_get(conn);
|
||||||
|
if (!l2cap) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn->l2cap.ident;
|
l2cap->ident++;
|
||||||
|
|
||||||
|
/* handle integer overflow (0 is not valid) */
|
||||||
|
if (!l2cap->ident) {
|
||||||
|
l2cap->ident++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l2cap->ident;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_l2cap_chan_register(struct bt_l2cap_chan *chan)
|
void bt_l2cap_fixed_chan_register(struct bt_l2cap_fixed_chan *chan)
|
||||||
{
|
{
|
||||||
BT_DBG("CID 0x%04x\n", chan->cid);
|
BT_DBG("CID 0x%04x\n", chan->cid);
|
||||||
|
|
||||||
|
@ -103,13 +134,58 @@ int bt_l2cap_server_register(struct bt_l2cap_server *server)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void l2cap_chan_alloc_cid(struct bt_conn *conn,
|
||||||
|
struct bt_l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
uint16_t cid;
|
||||||
|
|
||||||
|
if (chan->rx.cid > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try matching dcid first */
|
||||||
|
if (!bt_l2cap_lookup_rx_cid(conn, chan->rx.cid)) {
|
||||||
|
chan->rx.cid = chan->tx.cid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Check conn type before assigning cid */
|
||||||
|
for (cid = L2CAP_CID_START; cid < L2CAP_LE_CID_END; cid++) {
|
||||||
|
if (!bt_l2cap_lookup_rx_cid(conn, cid)) {
|
||||||
|
chan->rx.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->channels;
|
||||||
|
conn->channels = chan;
|
||||||
|
chan->conn = conn;
|
||||||
|
|
||||||
|
BT_DBG("conn %p chan %p cid 0x%04x\n", conn, chan, chan->rx.cid);
|
||||||
|
}
|
||||||
|
|
||||||
void bt_l2cap_connected(struct bt_conn *conn)
|
void bt_l2cap_connected(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
|
struct bt_l2cap_fixed_chan *fchan;
|
||||||
struct bt_l2cap_chan *chan;
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
for (chan = channels; chan; chan = chan->_next) {
|
for (fchan = channels; fchan; fchan = fchan->_next) {
|
||||||
if (chan->connected) {
|
if (fchan->accept(conn, &chan) < 0) {
|
||||||
chan->connected(conn);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chan->rx.cid = fchan->cid;
|
||||||
|
|
||||||
|
l2cap_chan_add(conn, chan);
|
||||||
|
|
||||||
|
if (chan->ops->connected) {
|
||||||
|
chan->ops->connected(chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,32 +194,30 @@ void bt_l2cap_disconnected(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_l2cap_chan *chan;
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
for (chan = channels; chan; chan = chan->_next) {
|
for (chan = conn->channels; chan;) {
|
||||||
if (chan->disconnected) {
|
struct bt_l2cap_chan *next;
|
||||||
chan->disconnected(conn);
|
|
||||||
|
/* prefetch since disconnected callback may cleanup */
|
||||||
|
next = chan->_next;
|
||||||
|
|
||||||
|
if (chan->ops->disconnected) {
|
||||||
|
chan->ops->disconnected(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chan->conn = NULL;
|
||||||
|
chan = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
conn->channels = NULL;
|
||||||
if (chan->disconnected) {
|
|
||||||
chan->disconnected(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_l2cap_encrypt_change(struct bt_conn *conn)
|
void bt_l2cap_encrypt_change(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_l2cap_chan *chan;
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
for (chan = channels; chan; chan = chan->_next) {
|
for (chan = conn->channels; chan; chan = chan->_next) {
|
||||||
if (chan->encrypt_change) {
|
if (chan->ops->encrypt_change) {
|
||||||
chan->encrypt_change(conn);
|
chan->ops->encrypt_change(chan);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (chan = conn->l2cap.channels; chan; chan = chan->_next) {
|
|
||||||
if (chan->encrypt_change) {
|
|
||||||
chan->encrypt_change(conn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +264,7 @@ static void rej_not_understood(struct bt_conn *conn, uint8_t ident)
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void le_conn_param_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
static void le_conn_param_rsp(struct bt_l2cap *l2cap, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_l2cap_conn_param_rsp *rsp = (void *)buf->data;
|
struct bt_l2cap_conn_param_rsp *rsp = (void *)buf->data;
|
||||||
|
|
||||||
|
@ -203,9 +277,10 @@ static void le_conn_param_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
static void le_conn_param_update_req(struct bt_conn *conn, uint8_t ident,
|
static void le_conn_param_update_req(struct bt_l2cap *l2cap, uint8_t ident,
|
||||||
struct bt_buf *buf)
|
struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = l2cap->chan.conn;
|
||||||
uint16_t min, max, latency, timeout;
|
uint16_t min, max, latency, timeout;
|
||||||
bool params_valid;
|
bool params_valid;
|
||||||
struct bt_l2cap_sig_hdr *hdr;
|
struct bt_l2cap_sig_hdr *hdr;
|
||||||
|
@ -230,7 +305,7 @@ static void le_conn_param_update_req(struct bt_conn *conn, uint8_t ident,
|
||||||
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x timeout: 0x%4.4x",
|
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x timeout: 0x%4.4x",
|
||||||
min, max, latency, timeout);
|
min, max, latency, timeout);
|
||||||
|
|
||||||
buf = bt_l2cap_create_pdu(conn);
|
buf = bt_l2cap_create_pdu(l2cap->chan.conn);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -249,7 +324,7 @@ static void le_conn_param_update_req(struct bt_conn *conn, uint8_t ident,
|
||||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED);
|
rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
bt_l2cap_send(l2cap->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||||
|
|
||||||
if (params_valid) {
|
if (params_valid) {
|
||||||
bt_conn_le_conn_update(conn, min, max, latency, timeout);
|
bt_conn_le_conn_update(conn, min, max, latency, timeout);
|
||||||
|
@ -257,76 +332,16 @@ static void le_conn_param_update_req(struct bt_conn *conn, uint8_t ident,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
static struct bt_l2cap_chan *l2cap_chan_lookup_dcid(struct bt_conn *conn,
|
static void le_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
|
||||||
uint16_t dcid)
|
struct bt_buf *buf)
|
||||||
{
|
|
||||||
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_conn *conn = l2cap->chan.conn;
|
||||||
struct bt_l2cap_chan *chan;
|
struct bt_l2cap_chan *chan;
|
||||||
struct bt_l2cap_server *server;
|
struct bt_l2cap_server *server;
|
||||||
struct bt_l2cap_le_conn_req *req = (void *)buf->data;
|
struct bt_l2cap_le_conn_req *req = (void *)buf->data;
|
||||||
struct bt_l2cap_le_conn_rsp *rsp;
|
struct bt_l2cap_le_conn_rsp *rsp;
|
||||||
struct bt_l2cap_sig_hdr *hdr;
|
struct bt_l2cap_sig_hdr *hdr;
|
||||||
uint16_t psm, scid, mtu, mps;
|
uint16_t psm, scid, mtu, mps, credits;
|
||||||
|
|
||||||
if (buf->len < sizeof(*req)) {
|
if (buf->len < sizeof(*req)) {
|
||||||
BT_ERR("Too small LE conn req packet size\n");
|
BT_ERR("Too small LE conn req packet size\n");
|
||||||
|
@ -337,9 +352,10 @@ static void le_conn_req(struct bt_conn *conn, uint8_t ident, struct bt_buf *buf)
|
||||||
scid = sys_le16_to_cpu(req->scid);
|
scid = sys_le16_to_cpu(req->scid);
|
||||||
mtu = sys_le16_to_cpu(req->mtu);
|
mtu = sys_le16_to_cpu(req->mtu);
|
||||||
mps = sys_le16_to_cpu(req->mps);
|
mps = sys_le16_to_cpu(req->mps);
|
||||||
|
credits = sys_le16_to_cpu(req->credits);
|
||||||
|
|
||||||
BT_DBG("psm 0x%02x scid 0x%04x mtu 0x%04x mps 0x%04x\n", psm, scid,
|
BT_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u\n", psm, scid,
|
||||||
mtu, mps);
|
mtu, mps, credits);
|
||||||
|
|
||||||
if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) {
|
if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) {
|
||||||
BT_ERR("Invalid LE-Conn Req params\n");
|
BT_ERR("Invalid LE-Conn Req params\n");
|
||||||
|
@ -351,13 +367,13 @@ static void le_conn_req(struct bt_conn *conn, uint8_t ident, struct bt_buf *buf)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtu = min(mtu, bt_buf_tailroom(buf));
|
|
||||||
|
|
||||||
hdr = bt_buf_add(buf, sizeof(*hdr));
|
hdr = bt_buf_add(buf, sizeof(*hdr));
|
||||||
hdr->code = BT_L2CAP_LE_CONN_RSP;
|
hdr->code = BT_L2CAP_LE_CONN_RSP;
|
||||||
hdr->ident = ident;
|
hdr->ident = ident;
|
||||||
hdr->len = sys_cpu_to_le16(sizeof(*rsp));
|
hdr->len = sys_cpu_to_le16(sizeof(*rsp));
|
||||||
|
|
||||||
|
mtu = min(mtu, bt_buf_tailroom(buf));
|
||||||
|
|
||||||
rsp = bt_buf_add(buf, sizeof(*rsp));
|
rsp = bt_buf_add(buf, sizeof(*rsp));
|
||||||
memset(rsp, 0, sizeof(*rsp));
|
memset(rsp, 0, sizeof(*rsp));
|
||||||
|
|
||||||
|
@ -371,7 +387,7 @@ static void le_conn_req(struct bt_conn *conn, uint8_t ident, struct bt_buf *buf)
|
||||||
|
|
||||||
/* TODO: Add security check */
|
/* TODO: Add security check */
|
||||||
|
|
||||||
chan = l2cap_chan_lookup_dcid(conn, scid);
|
chan = bt_l2cap_lookup_tx_cid(conn, scid);
|
||||||
if (chan) {
|
if (chan) {
|
||||||
rsp->dcid = req->scid;
|
rsp->dcid = req->scid;
|
||||||
rsp->result = BT_L2CAP_ERR_NO_RESOURCES;
|
rsp->result = BT_L2CAP_ERR_NO_RESOURCES;
|
||||||
|
@ -389,30 +405,37 @@ static void le_conn_req(struct bt_conn *conn, uint8_t ident, struct bt_buf *buf)
|
||||||
goto rsp;
|
goto rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
chan->dcid = scid;
|
/* Init TX parameters */
|
||||||
chan->mps = mps;
|
chan->tx.cid = scid;
|
||||||
chan->mtu = mtu;
|
chan->tx.mps = mps;
|
||||||
chan->credits_rx = L2CAP_LE_MAX_CREDITS;
|
chan->tx.mtu = mtu;
|
||||||
chan->credits_tx = sys_le16_to_cpu(req->credits);
|
chan->tx.credits = credits;
|
||||||
|
|
||||||
|
/* Init RX parameters */
|
||||||
|
chan->rx.mps = bt_buf_tailroom(buf) + sizeof(*rsp);
|
||||||
|
/* TODO: Once segmentation is supported these can be different */
|
||||||
|
chan->rx.mtu = chan->rx.mps;
|
||||||
|
chan->rx.credits = L2CAP_LE_MAX_CREDITS;
|
||||||
|
|
||||||
l2cap_chan_add(conn, chan);
|
l2cap_chan_add(conn, chan);
|
||||||
|
|
||||||
if (chan->connected) {
|
if (chan->ops->connected) {
|
||||||
chan->connected(conn);
|
chan->ops->connected(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp->dcid = sys_cpu_to_le16(chan->cid);
|
rsp->dcid = sys_cpu_to_le16(chan->rx.cid);
|
||||||
rsp->mps = sys_cpu_to_le16(chan->mps);
|
rsp->mps = sys_cpu_to_le16(chan->rx.mps);
|
||||||
rsp->mtu = sys_cpu_to_le16(chan->mtu);
|
rsp->mtu = sys_cpu_to_le16(chan->rx.mtu);
|
||||||
rsp->credits = sys_cpu_to_le16(chan->credits_rx);
|
rsp->credits = sys_cpu_to_le16(chan->rx.credits);
|
||||||
rsp->result = BT_L2CAP_SUCCESS;
|
rsp->result = BT_L2CAP_SUCCESS;
|
||||||
|
|
||||||
rsp:
|
rsp:
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void le_sig(struct bt_conn *conn, struct bt_buf *buf)
|
static void l2cap_recv(struct bt_l2cap_chan *chan, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_l2cap *l2cap = CONTAINER_OF(chan, struct bt_l2cap, chan);
|
||||||
struct bt_l2cap_sig_hdr *hdr = (void *)buf->data;
|
struct bt_l2cap_sig_hdr *hdr = (void *)buf->data;
|
||||||
uint16_t len;
|
uint16_t len;
|
||||||
|
|
||||||
|
@ -439,19 +462,19 @@ static void le_sig(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
switch (hdr->code) {
|
switch (hdr->code) {
|
||||||
case BT_L2CAP_CONN_PARAM_RSP:
|
case BT_L2CAP_CONN_PARAM_RSP:
|
||||||
le_conn_param_rsp(conn, buf);
|
le_conn_param_rsp(l2cap, buf);
|
||||||
break;
|
break;
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
case BT_L2CAP_CONN_PARAM_REQ:
|
case BT_L2CAP_CONN_PARAM_REQ:
|
||||||
le_conn_param_update_req(conn, hdr->ident, buf);
|
le_conn_param_update_req(l2cap, hdr->ident, buf);
|
||||||
break;
|
break;
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
case BT_L2CAP_LE_CONN_REQ:
|
case BT_L2CAP_LE_CONN_REQ:
|
||||||
le_conn_req(conn, hdr->ident, buf);
|
le_conn_req(l2cap, hdr->ident, buf);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BT_WARN("Unknown L2CAP PDU code 0x%02x\n", hdr->code);
|
BT_WARN("Unknown L2CAP PDU code 0x%02x\n", hdr->code);
|
||||||
rej_not_understood(conn, hdr->ident);
|
rej_not_understood(chan->conn, hdr->ident);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,22 +499,14 @@ void bt_l2cap_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
BT_DBG("Packet for CID %u len %u\n", cid, buf->len);
|
BT_DBG("Packet for CID %u len %u\n", cid, buf->len);
|
||||||
|
|
||||||
for (chan = channels; chan; chan = chan->_next) {
|
chan = bt_l2cap_lookup_rx_cid(conn, cid);
|
||||||
if (chan->cid == cid) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
chan = l2cap_chan_lookup_scid(conn, cid);
|
BT_WARN("Ignoring data for unknown CID 0x%04x\n", cid);
|
||||||
if (!chan) {
|
bt_buf_put(buf);
|
||||||
BT_WARN("Ignoring data for unknown CID 0x%04x\n", cid);
|
return;
|
||||||
bt_buf_put(buf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chan->recv(conn, buf);
|
chan->ops->recv(chan, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_l2cap_update_conn_param(struct bt_conn *conn)
|
int bt_l2cap_update_conn_param(struct bt_conn *conn)
|
||||||
|
@ -521,13 +536,53 @@ int bt_l2cap_update_conn_param(struct bt_conn *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void l2cap_connected(struct bt_l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->rx.cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l2cap_disconnected(struct bt_l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->rx.cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static struct bt_l2cap_chan_ops ops = {
|
||||||
|
.connected = l2cap_connected,
|
||||||
|
.disconnected = l2cap_disconnected,
|
||||||
|
.recv = l2cap_recv,
|
||||||
|
};
|
||||||
|
|
||||||
|
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bt_l2cap_pool); i++) {
|
||||||
|
struct bt_l2cap *l2cap = &bt_l2cap_pool[i];
|
||||||
|
|
||||||
|
if (l2cap->chan.conn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2cap->chan.ops = &ops;
|
||||||
|
|
||||||
|
*chan = &l2cap->chan;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_ERR("No available L2CAP context for conn %p\n", conn);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
int bt_l2cap_init(void)
|
int bt_l2cap_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
static struct bt_l2cap_chan chan = {
|
static struct bt_l2cap_fixed_chan chan = {
|
||||||
.cid = BT_L2CAP_CID_LE_SIG,
|
.cid = BT_L2CAP_CID_LE_SIG,
|
||||||
.recv = le_sig,
|
.accept = l2cap_accept,
|
||||||
};
|
};
|
||||||
|
|
||||||
bt_att_init();
|
bt_att_init();
|
||||||
|
@ -537,7 +592,34 @@ int bt_l2cap_init(void)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
bt_l2cap_fixed_chan_register(&chan);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bt_l2cap_chan *bt_l2cap_lookup_tx_cid(struct bt_conn *conn,
|
||||||
|
uint16_t cid)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
for (chan = conn->channels; chan; chan = chan->_next) {
|
||||||
|
if (chan->tx.cid == cid)
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bt_l2cap_chan *bt_l2cap_lookup_rx_cid(struct bt_conn *conn,
|
||||||
|
uint16_t cid)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
for (chan = conn->channels; chan; chan = chan->_next) {
|
||||||
|
if (chan->rx.cid == cid) {
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <bluetooth/l2cap.h>
|
||||||
|
|
||||||
#define BT_L2CAP_CID_ATT 0x0004
|
#define BT_L2CAP_CID_ATT 0x0004
|
||||||
#define BT_L2CAP_CID_LE_SIG 0x0005
|
#define BT_L2CAP_CID_LE_SIG 0x0005
|
||||||
#define BT_L2CAP_CID_SMP 0x0006
|
#define BT_L2CAP_CID_SMP 0x0006
|
||||||
|
@ -87,41 +89,16 @@ struct bt_l2cap_le_conn_rsp {
|
||||||
uint16_t result;
|
uint16_t result;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_l2cap_chan {
|
struct bt_l2cap_fixed_chan {
|
||||||
uint16_t cid;
|
uint16_t cid;
|
||||||
|
|
||||||
/* CoC fields */
|
|
||||||
uint16_t dcid;
|
|
||||||
|
|
||||||
uint16_t mps;
|
|
||||||
uint16_t mtu;
|
|
||||||
|
|
||||||
uint16_t credits_tx;
|
|
||||||
uint16_t credits_rx;
|
|
||||||
|
|
||||||
void (*connected)(struct bt_conn *conn);
|
|
||||||
void (*disconnected)(struct bt_conn *conn);
|
|
||||||
void (*encrypt_change)(struct bt_conn *conn);
|
|
||||||
|
|
||||||
void (*recv)(struct bt_conn *conn,
|
|
||||||
struct bt_buf *buf);
|
|
||||||
|
|
||||||
struct bt_l2cap_chan *_next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bt_l2cap_server {
|
|
||||||
uint16_t psm;
|
|
||||||
|
|
||||||
int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan);
|
int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan);
|
||||||
|
|
||||||
struct bt_l2cap_server *_next;
|
struct bt_l2cap_fixed_chan *_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Register a fixed L2CAP channel for L2CAP */
|
/* Register a fixed L2CAP channel for L2CAP */
|
||||||
void bt_l2cap_chan_register(struct bt_l2cap_chan *chan);
|
void bt_l2cap_fixed_chan_register(struct bt_l2cap_fixed_chan *chan);
|
||||||
|
|
||||||
/* Register L2CAP Server */
|
|
||||||
int bt_l2cap_server_register(struct bt_l2cap_server *server);
|
|
||||||
|
|
||||||
/* Notify L2CAP channels of a new connection */
|
/* Notify L2CAP channels of a new connection */
|
||||||
void bt_l2cap_connected(struct bt_conn *conn);
|
void bt_l2cap_connected(struct bt_conn *conn);
|
||||||
|
@ -146,3 +123,11 @@ int bt_l2cap_update_conn_param(struct bt_conn *conn);
|
||||||
|
|
||||||
/* Initialize L2CAP and supported channels */
|
/* Initialize L2CAP and supported channels */
|
||||||
int bt_l2cap_init(void);
|
int bt_l2cap_init(void);
|
||||||
|
|
||||||
|
/* Lookup channel by Transmission CID */
|
||||||
|
struct bt_l2cap_chan *bt_l2cap_lookup_tx_cid(struct bt_conn *conn,
|
||||||
|
uint16_t cid);
|
||||||
|
|
||||||
|
/* Lookup channel by Receiver CID */
|
||||||
|
struct bt_l2cap_chan *bt_l2cap_lookup_rx_cid(struct bt_conn *conn,
|
||||||
|
uint16_t cid);
|
||||||
|
|
|
@ -71,8 +71,8 @@ enum {
|
||||||
|
|
||||||
/* SMP channel specific context */
|
/* SMP channel specific context */
|
||||||
struct bt_smp {
|
struct bt_smp {
|
||||||
/* The connection this context is associated with */
|
/* The channel this context is associated with */
|
||||||
struct bt_conn *conn;
|
struct bt_l2cap_chan chan;
|
||||||
|
|
||||||
/* SMP Timeout fiber handle */
|
/* SMP Timeout fiber handle */
|
||||||
void *timeout;
|
void *timeout;
|
||||||
|
@ -151,7 +151,7 @@ static uint8_t get_pair_method(struct bt_smp *smp, uint8_t remote_io)
|
||||||
* and responder inputs
|
* and responder inputs
|
||||||
*/
|
*/
|
||||||
if (method == PASSKEY_ROLE) {
|
if (method == PASSKEY_ROLE) {
|
||||||
if (smp->conn->role == BT_HCI_ROLE_MASTER) {
|
if (smp->chan.conn->role == BT_HCI_ROLE_MASTER) {
|
||||||
method = PASSKEY_DISPLAY;
|
method = PASSKEY_DISPLAY;
|
||||||
} else {
|
} else {
|
||||||
method = PASSKEY_INPUT;
|
method = PASSKEY_INPUT;
|
||||||
|
@ -408,6 +408,8 @@ static int smp_s1(const uint8_t k[16], const uint8_t r1[16],
|
||||||
|
|
||||||
static void smp_reset(struct bt_smp *smp)
|
static void smp_reset(struct bt_smp *smp)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
|
|
||||||
if (smp->timeout) {
|
if (smp->timeout) {
|
||||||
fiber_fiber_delayed_start_cancel(smp->timeout);
|
fiber_fiber_delayed_start_cancel(smp->timeout);
|
||||||
smp->timeout = NULL;
|
smp->timeout = NULL;
|
||||||
|
@ -420,13 +422,13 @@ static void smp_reset(struct bt_smp *smp)
|
||||||
atomic_set(&smp->allowed_cmds, 0);
|
atomic_set(&smp->allowed_cmds, 0);
|
||||||
atomic_set(&smp->flags, 0);
|
atomic_set(&smp->flags, 0);
|
||||||
|
|
||||||
if (smp->conn->required_sec_level != smp->conn->sec_level) {
|
if (conn->required_sec_level != conn->sec_level) {
|
||||||
/* TODO report error */
|
/* TODO report error */
|
||||||
/* reset required security level in case of error */
|
/* reset required security level in case of error */
|
||||||
smp->conn->required_sec_level = smp->conn->sec_level;
|
conn->required_sec_level = conn->sec_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(smp->conn->role) {
|
switch (conn->role) {
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
case BT_HCI_ROLE_MASTER:
|
case BT_HCI_ROLE_MASTER:
|
||||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
||||||
|
@ -502,10 +504,8 @@ static void send_err_rsp(struct bt_conn *conn, uint8_t reason)
|
||||||
|
|
||||||
static int smp_init(struct bt_smp *smp)
|
static int smp_init(struct bt_smp *smp)
|
||||||
{
|
{
|
||||||
struct bt_conn *conn = smp->conn;
|
|
||||||
|
|
||||||
/* Initialize SMP context */
|
/* Initialize SMP context */
|
||||||
memset(smp, 0, sizeof(*smp));
|
memset(smp + sizeof(smp->chan), 0, sizeof(*smp) - sizeof(smp->chan));
|
||||||
|
|
||||||
/* Generate local random number */
|
/* Generate local random number */
|
||||||
if (le_rand(smp->prnd, 16)) {
|
if (le_rand(smp->prnd, 16)) {
|
||||||
|
@ -514,8 +514,6 @@ static int smp_init(struct bt_smp *smp)
|
||||||
|
|
||||||
BT_DBG("prnd %s\n", h(smp->prnd, 16));
|
BT_DBG("prnd %s\n", h(smp->prnd, 16));
|
||||||
|
|
||||||
smp->conn = conn;
|
|
||||||
|
|
||||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
|
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -536,6 +534,7 @@ static uint8_t get_auth(uint8_t auth)
|
||||||
|
|
||||||
static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
|
static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
uint32_t passkey;
|
uint32_t passkey;
|
||||||
|
|
||||||
|
@ -545,7 +544,7 @@ static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
|
||||||
* distributed in new pairing. This is to avoid replacing authenticated
|
* distributed in new pairing. This is to avoid replacing authenticated
|
||||||
* keys with unauthenticated ones.
|
* keys with unauthenticated ones.
|
||||||
*/
|
*/
|
||||||
keys = bt_keys_find_addr(&smp->conn->dst);
|
keys = bt_keys_find_addr(&conn->dst);
|
||||||
if (keys && keys->type == BT_KEYS_AUTHENTICATED &&
|
if (keys && keys->type == BT_KEYS_AUTHENTICATED &&
|
||||||
smp->method == JUST_WORKS) {
|
smp->method == JUST_WORKS) {
|
||||||
BT_ERR("JustWorks failed, authenticated keys present\n");
|
BT_ERR("JustWorks failed, authenticated keys present\n");
|
||||||
|
@ -560,7 +559,7 @@ static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
|
||||||
|
|
||||||
passkey %= 1000000;
|
passkey %= 1000000;
|
||||||
|
|
||||||
auth_cb->passkey_display(smp->conn, passkey);
|
auth_cb->passkey_display(conn, passkey);
|
||||||
|
|
||||||
passkey = sys_cpu_to_le32(passkey);
|
passkey = sys_cpu_to_le32(passkey);
|
||||||
memcpy(smp->tk, &passkey, sizeof(passkey));
|
memcpy(smp->tk, &passkey, sizeof(passkey));
|
||||||
|
@ -568,7 +567,7 @@ static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PASSKEY_INPUT:
|
case PASSKEY_INPUT:
|
||||||
auth_cb->passkey_entry(smp->conn);
|
auth_cb->passkey_entry(conn);
|
||||||
break;
|
break;
|
||||||
case JUST_WORKS:
|
case JUST_WORKS:
|
||||||
atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);
|
atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);
|
||||||
|
@ -595,14 +594,31 @@ static bool sec_level_reachable(struct bt_conn *conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct bt_smp *smp_chan_get(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
chan = bt_l2cap_lookup_rx_cid(conn, BT_L2CAP_CID_SMP);
|
||||||
|
if (!chan) {
|
||||||
|
BT_ERR("Unable to find SMP channel");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONTAINER_OF(chan, struct bt_smp, chan);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
|
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
|
||||||
int bt_smp_send_security_req(struct bt_conn *conn)
|
int bt_smp_send_security_req(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_smp *smp;
|
||||||
struct bt_smp_security_request *req;
|
struct bt_smp_security_request *req;
|
||||||
struct bt_buf *req_buf;
|
struct bt_buf *req_buf;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
smp = smp_chan_get(conn);
|
||||||
|
if (!smp) {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
/* SMP Timeout */
|
/* SMP Timeout */
|
||||||
if (atomic_test_bit(&smp->flags, SMP_FLAG_TIMEOUT)) {
|
if (atomic_test_bit(&smp->flags, SMP_FLAG_TIMEOUT)) {
|
||||||
|
@ -640,12 +656,12 @@ int bt_smp_send_security_req(struct bt_conn *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_pairing_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_req(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_pairing *req = (void *)buf->data;
|
struct bt_smp_pairing *req = (void *)buf->data;
|
||||||
struct bt_smp_pairing *rsp;
|
struct bt_smp_pairing *rsp;
|
||||||
struct bt_buf *rsp_buf;
|
struct bt_buf *rsp_buf;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -693,16 +709,16 @@ static uint8_t smp_pairing_req(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return smp_request_tk(smp, req->io_capability);
|
return smp_request_tk(smp, req->io_capability);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static uint8_t smp_pairing_req(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_req(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
return BT_SMP_ERR_CMD_NOTSUPP;
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
|
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
|
||||||
|
|
||||||
static uint8_t smp_send_pairing_confirm(struct bt_conn *conn)
|
static uint8_t smp_send_pairing_confirm(struct bt_smp *smp)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_pairing_confirm *req;
|
struct bt_smp_pairing_confirm *req;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_buf *rsp_buf;
|
struct bt_buf *rsp_buf;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -733,12 +749,17 @@ static uint8_t smp_send_pairing_confirm(struct bt_conn *conn)
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
int bt_smp_send_pairing_req(struct bt_conn *conn)
|
int bt_smp_send_pairing_req(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_smp *smp;
|
||||||
struct bt_smp_pairing *req;
|
struct bt_smp_pairing *req;
|
||||||
struct bt_buf *req_buf;
|
struct bt_buf *req_buf;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
|
smp = smp_chan_get(conn);
|
||||||
|
if (!smp) {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
/* SMP Timeout */
|
/* SMP Timeout */
|
||||||
if (atomic_test_bit(&smp->flags, SMP_FLAG_TIMEOUT)) {
|
if (atomic_test_bit(&smp->flags, SMP_FLAG_TIMEOUT)) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -789,10 +810,9 @@ int bt_smp_send_pairing_req(struct bt_conn *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_pairing_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_smp_pairing *rsp = (void *)buf->data;
|
struct bt_smp_pairing *rsp = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
uint8_t ret;
|
uint8_t ret;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -816,7 +836,7 @@ static uint8_t smp_pairing_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
if (atomic_test_bit(&smp->flags, SMP_FLAG_TK_VALID)) {
|
if (atomic_test_bit(&smp->flags, SMP_FLAG_TK_VALID)) {
|
||||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
||||||
return smp_send_pairing_confirm(conn);
|
return smp_send_pairing_confirm(smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_set_bit(&smp->flags, SMP_FLAG_CFM_DELAYED);
|
atomic_set_bit(&smp->flags, SMP_FLAG_CFM_DELAYED);
|
||||||
|
@ -824,17 +844,17 @@ static uint8_t smp_pairing_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static uint8_t smp_pairing_rsp(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
return BT_SMP_ERR_CMD_NOTSUPP;
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
static uint8_t smp_send_pairing_random(struct bt_conn *conn)
|
static uint8_t smp_send_pairing_random(struct bt_smp *smp)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_pairing_random *req;
|
struct bt_smp_pairing_random *req;
|
||||||
struct bt_buf *rsp_buf;
|
struct bt_buf *rsp_buf;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
|
|
||||||
rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RANDOM,
|
rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RANDOM,
|
||||||
sizeof(*req));
|
sizeof(*req));
|
||||||
|
@ -852,26 +872,25 @@ static uint8_t smp_send_pairing_random(struct bt_conn *conn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_pairing_confirm(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_confirm(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_smp_pairing_confirm *req = (void *)buf->data;
|
struct bt_smp_pairing_confirm *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
|
||||||
memcpy(smp->pcnf, req->val, sizeof(smp->pcnf));
|
memcpy(smp->pcnf, req->val, sizeof(smp->pcnf));
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
if (conn->role == BT_HCI_ROLE_MASTER) {
|
if (smp->chan.conn->role == BT_HCI_ROLE_MASTER) {
|
||||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
||||||
return smp_send_pairing_random(conn);
|
return smp_send_pairing_random(smp);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
|
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
|
||||||
if (atomic_test_bit(&smp->flags, SMP_FLAG_TK_VALID)) {
|
if (atomic_test_bit(&smp->flags, SMP_FLAG_TK_VALID)) {
|
||||||
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
||||||
return smp_send_pairing_confirm(conn);
|
return smp_send_pairing_confirm(smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_set_bit(&smp->flags, SMP_FLAG_CFM_DELAYED);
|
atomic_set_bit(&smp->flags, SMP_FLAG_CFM_DELAYED);
|
||||||
|
@ -905,10 +924,10 @@ static uint8_t get_encryption_key_size(struct bt_smp *smp)
|
||||||
return min(req->max_key_size, rsp->max_key_size);
|
return min(req->max_key_size, rsp->max_key_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_pairing_random(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_random(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_pairing_random *req = (void *)buf->data;
|
struct bt_smp_pairing_random *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
uint8_t cfm[16];
|
uint8_t cfm[16];
|
||||||
int err;
|
int err;
|
||||||
|
@ -992,16 +1011,16 @@ static uint8_t smp_pairing_random(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
atomic_set_bit(&smp->flags, SMP_FLAG_ENC_PENDING);
|
atomic_set_bit(&smp->flags, SMP_FLAG_ENC_PENDING);
|
||||||
|
|
||||||
smp_send_pairing_random(conn);
|
smp_send_pairing_random(smp);
|
||||||
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
|
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_pairing_failed(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_pairing_failed(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_pairing_fail *req = (void *)buf->data;
|
struct bt_smp_pairing_fail *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
|
|
||||||
BT_ERR("reason 0x%x\n", req->reason);
|
BT_ERR("reason 0x%x\n", req->reason);
|
||||||
|
|
||||||
|
@ -1026,9 +1045,9 @@ static uint8_t smp_pairing_failed(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_smp_distribute_keys(struct bt_conn *conn)
|
static void bt_smp_distribute_keys(struct bt_smp *smp)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
struct bt_buf *buf;
|
struct bt_buf *buf;
|
||||||
|
|
||||||
|
@ -1123,10 +1142,10 @@ static void bt_smp_distribute_keys(struct bt_conn *conn)
|
||||||
#endif /* CONFIG_BLUETOOTH_SIGNING */
|
#endif /* CONFIG_BLUETOOTH_SIGNING */
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_encrypt_info(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_encrypt_info(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_encrypt_info *req = (void *)buf->data;
|
struct bt_smp_encrypt_info *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -1145,10 +1164,10 @@ static uint8_t smp_encrypt_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_master_ident(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_master_ident(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_master_ident *req = (void *)buf->data;
|
struct bt_smp_master_ident *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -1173,7 +1192,7 @@ static uint8_t smp_master_ident(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
||||||
bt_smp_distribute_keys(conn);
|
bt_smp_distribute_keys(smp);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
|
@ -1185,10 +1204,10 @@ static uint8_t smp_master_ident(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_ident_info(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_ident_info(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_ident_info *req = (void *)buf->data;
|
struct bt_smp_ident_info *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -1207,10 +1226,10 @@ static uint8_t smp_ident_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t smp_ident_addr_info(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_ident_addr_info(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_ident_addr_info *req = (void *)buf->data;
|
struct bt_smp_ident_addr_info *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
const bt_addr_le_t *dst;
|
const bt_addr_le_t *dst;
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
|
@ -1264,7 +1283,7 @@ static uint8_t smp_ident_addr_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
||||||
bt_smp_distribute_keys(conn);
|
bt_smp_distribute_keys(smp);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
|
@ -1277,10 +1296,10 @@ static uint8_t smp_ident_addr_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_SIGNING)
|
#if defined(CONFIG_BLUETOOTH_SIGNING)
|
||||||
static uint8_t smp_signing_info(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_signing_info(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_signing_info *req = (void *)buf->data;
|
struct bt_smp_signing_info *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
BT_DBG("\n");
|
BT_DBG("\n");
|
||||||
|
@ -1298,7 +1317,7 @@ static uint8_t smp_signing_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
|
||||||
bt_smp_distribute_keys(conn);
|
bt_smp_distribute_keys(smp);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
|
@ -1310,17 +1329,17 @@ static uint8_t smp_signing_info(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static uint8_t smp_signing_info(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_signing_info(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
return BT_SMP_ERR_CMD_NOTSUPP;
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_SIGNING */
|
#endif /* CONFIG_BLUETOOTH_SIGNING */
|
||||||
|
|
||||||
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
||||||
static uint8_t smp_security_request(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_security_request(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = smp->chan.conn;
|
||||||
struct bt_smp_security_request *req = (void *)buf->data;
|
struct bt_smp_security_request *req = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
uint8_t auth;
|
uint8_t auth;
|
||||||
|
|
||||||
|
@ -1361,14 +1380,14 @@ pair:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static uint8_t smp_security_request(struct bt_conn *conn, struct bt_buf *buf)
|
static uint8_t smp_security_request(struct bt_smp *smp, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
return BT_SMP_ERR_CMD_NOTSUPP;
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
uint8_t (*func)(struct bt_conn *conn, struct bt_buf *buf);
|
uint8_t (*func)(struct bt_smp *smp, struct bt_buf *buf);
|
||||||
uint8_t expect_len;
|
uint8_t expect_len;
|
||||||
} handlers[] = {
|
} handlers[] = {
|
||||||
{ }, /* No op-code defined for 0x00 */
|
{ }, /* No op-code defined for 0x00 */
|
||||||
|
@ -1385,10 +1404,10 @@ static const struct {
|
||||||
{ smp_security_request, sizeof(struct bt_smp_security_request) },
|
{ smp_security_request, sizeof(struct bt_smp_security_request) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
static void bt_smp_recv(struct bt_l2cap_chan *chan, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
|
||||||
struct bt_smp_hdr *hdr = (void *)buf->data;
|
struct bt_smp_hdr *hdr = (void *)buf->data;
|
||||||
struct bt_smp *smp = conn->smp;
|
|
||||||
uint8_t err;
|
uint8_t err;
|
||||||
|
|
||||||
if (buf->len < sizeof(*hdr)) {
|
if (buf->len < sizeof(*hdr)) {
|
||||||
|
@ -1425,12 +1444,12 @@ static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
hdr->code);
|
hdr->code);
|
||||||
err = BT_SMP_ERR_INVALID_PARAMS;
|
err = BT_SMP_ERR_INVALID_PARAMS;
|
||||||
} else {
|
} else {
|
||||||
err = handlers[hdr->code].func(conn, buf);
|
err = handlers[hdr->code].func(smp, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
send_err_rsp(conn, err);
|
send_err_rsp(chan->conn, err);
|
||||||
|
|
||||||
smp_reset(smp);
|
smp_reset(smp);
|
||||||
}
|
}
|
||||||
|
@ -1439,41 +1458,20 @@ done:
|
||||||
bt_buf_put(buf);
|
bt_buf_put(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_smp_connected(struct bt_conn *conn)
|
static void bt_smp_connected(struct bt_l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
int i;
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
|
||||||
|
|
||||||
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->tx.cid);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
smp_reset(smp);
|
||||||
struct bt_smp *smp = &bt_smp_pool[i];
|
|
||||||
|
|
||||||
if (smp->conn) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
smp->conn = conn;
|
|
||||||
conn->smp = smp;
|
|
||||||
|
|
||||||
smp_reset(smp);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_ERR("No available SMP context for conn %p\n", conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_smp_disconnected(struct bt_conn *conn)
|
static void bt_smp_disconnected(struct bt_l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
|
||||||
|
|
||||||
if (!smp) {
|
BT_DBG("chan %p cid 0x%04x\n", chan, chan->tx.cid);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
|
||||||
|
|
||||||
conn->smp = NULL;
|
|
||||||
|
|
||||||
if (smp->timeout) {
|
if (smp->timeout) {
|
||||||
fiber_fiber_delayed_start_cancel(smp->timeout);
|
fiber_fiber_delayed_start_cancel(smp->timeout);
|
||||||
|
@ -1482,13 +1480,14 @@ static void bt_smp_disconnected(struct bt_conn *conn)
|
||||||
memset(smp, 0, sizeof(*smp));
|
memset(smp, 0, sizeof(*smp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_smp_encrypt_change(struct bt_conn *conn)
|
static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
|
||||||
|
struct bt_conn *conn = chan->conn;
|
||||||
struct bt_keys *keys;
|
struct bt_keys *keys;
|
||||||
|
|
||||||
BT_DBG("conn %p handle %u encrypt 0x%02x\n", conn, conn->handle,
|
BT_DBG("chan %p conn %p handle %u encrypt 0x%02x\n", chan, conn,
|
||||||
conn->encrypt);
|
conn->handle, conn->encrypt);
|
||||||
|
|
||||||
if (!smp || !conn->encrypt) {
|
if (!smp || !conn->encrypt) {
|
||||||
return;
|
return;
|
||||||
|
@ -1534,7 +1533,7 @@ static void bt_smp_encrypt_change(struct bt_conn *conn)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
||||||
|
|
||||||
bt_smp_distribute_keys(conn);
|
bt_smp_distribute_keys(smp);
|
||||||
|
|
||||||
/* if all keys were distributed, pairing is done */
|
/* if all keys were distributed, pairing is done */
|
||||||
if (!smp->local_dist && !smp->remote_dist) {
|
if (!smp->local_dist && !smp->remote_dist) {
|
||||||
|
@ -2085,7 +2084,12 @@ int bt_auth_cb_register(const struct bt_auth_cb *cb)
|
||||||
|
|
||||||
void bt_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
void bt_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
||||||
{
|
{
|
||||||
struct bt_smp *smp = conn->smp;
|
struct bt_smp *smp;
|
||||||
|
|
||||||
|
smp = smp_chan_get(conn);
|
||||||
|
if (!smp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
passkey = sys_cpu_to_le32(passkey);
|
passkey = sys_cpu_to_le32(passkey);
|
||||||
memcpy(smp->tk, &passkey, sizeof(passkey));
|
memcpy(smp->tk, &passkey, sizeof(passkey));
|
||||||
|
@ -2096,7 +2100,7 @@ void bt_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if confirm failed ie. due to invalid passkey, cancel pairing */
|
/* if confirm failed ie. due to invalid passkey, cancel pairing */
|
||||||
if (smp_send_pairing_confirm(conn) ) {
|
if (smp_send_pairing_confirm(smp)) {
|
||||||
bt_auth_cancel(conn);
|
bt_auth_cancel(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2120,21 +2124,57 @@ void bt_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
||||||
|
|
||||||
void bt_auth_cancel(struct bt_conn *conn)
|
void bt_auth_cancel(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
|
struct bt_smp *smp;
|
||||||
|
|
||||||
|
smp = smp_chan_get(conn);
|
||||||
|
if (!smp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
send_err_rsp(conn, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
send_err_rsp(conn, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
||||||
smp_reset(conn->smp);
|
|
||||||
|
smp_reset(smp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static struct bt_l2cap_chan_ops ops = {
|
||||||
|
.connected = bt_smp_connected,
|
||||||
|
.disconnected = bt_smp_disconnected,
|
||||||
|
.encrypt_change = bt_smp_encrypt_change,
|
||||||
|
.recv = bt_smp_recv,
|
||||||
|
};
|
||||||
|
|
||||||
|
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
||||||
|
struct bt_smp *smp = &bt_smp_pool[i];
|
||||||
|
|
||||||
|
if (smp->chan.conn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
smp->chan.ops = &ops;
|
||||||
|
|
||||||
|
*chan = &smp->chan;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_ERR("No available SMP context for conn %p\n", conn);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_smp_init(void)
|
int bt_smp_init(void)
|
||||||
{
|
{
|
||||||
static struct bt_l2cap_chan chan = {
|
static struct bt_l2cap_fixed_chan chan = {
|
||||||
.cid = BT_L2CAP_CID_SMP,
|
.cid = BT_L2CAP_CID_SMP,
|
||||||
.recv = bt_smp_recv,
|
.accept = bt_smp_accept,
|
||||||
.connected = bt_smp_connected,
|
|
||||||
.disconnected = bt_smp_disconnected,
|
|
||||||
.encrypt_change = bt_smp_encrypt_change,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
bt_l2cap_fixed_chan_register(&chan);
|
||||||
|
|
||||||
return smp_self_test();
|
return smp_self_test();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,18 +19,29 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <bluetooth/bluetooth.h>
|
#include <nanokernel.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <atomic.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
|
||||||
|
#include <bluetooth/log.h>
|
||||||
|
#include <bluetooth/bluetooth.h>
|
||||||
|
|
||||||
|
#include "hci_core.h"
|
||||||
|
#include "conn_internal.h"
|
||||||
#include "l2cap_internal.h"
|
#include "l2cap_internal.h"
|
||||||
#include "smp.h"
|
#include "smp.h"
|
||||||
|
|
||||||
|
static struct bt_l2cap_chan bt_smp_pool[CONFIG_BLUETOOTH_MAX_CONN];
|
||||||
|
|
||||||
int bt_smp_sign_verify(struct bt_conn *conn, struct bt_buf *buf)
|
int bt_smp_sign_verify(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
static void bt_smp_recv(struct bt_l2cap_chan *chan, struct bt_buf *buf)
|
||||||
{
|
{
|
||||||
|
struct bt_conn *conn = chan->conn;
|
||||||
struct bt_smp_pairing_fail *rsp;
|
struct bt_smp_pairing_fail *rsp;
|
||||||
struct bt_smp_hdr *hdr;
|
struct bt_smp_hdr *hdr;
|
||||||
|
|
||||||
|
@ -56,14 +67,42 @@ static void bt_smp_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
|
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bt_smp_init(void)
|
static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||||
{
|
{
|
||||||
static struct bt_l2cap_chan chan = {
|
int i;
|
||||||
.cid = BT_L2CAP_CID_SMP,
|
static struct bt_l2cap_chan_ops ops = {
|
||||||
.recv = bt_smp_recv,
|
.recv = bt_smp_recv,
|
||||||
};
|
};
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
BT_DBG("conn %p handle %u\n", conn, conn->handle);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
||||||
|
struct bt_l2cap_chan *smp = &bt_smp_pool[i];
|
||||||
|
|
||||||
|
if (smp->conn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
smp->ops = &ops;
|
||||||
|
|
||||||
|
*chan = smp;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_ERR("No available SMP context for conn %p\n", conn);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_smp_init(void)
|
||||||
|
{
|
||||||
|
static struct bt_l2cap_fixed_chan chan = {
|
||||||
|
.cid = BT_L2CAP_CID_SMP,
|
||||||
|
.accept = bt_smp_accept,
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_l2cap_fixed_chan_register(&chan);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue