Bluetooth: Add ATT Read by Type Request skeleton
This adds the defines for Read by Type Value PDUs along with initial code to parse them: > ACL Data RX: Handle 3585 flags 0x02 dlen 11 ATT: Read By Type Request (0x08) len 6 Handle range: 0x0001-0xffff Attribute type: Characteristic (0x2803) < ACL Data TX: Handle 3585 flags 0x00 dlen 9 ATT: Error Response (0x01) len 4 Read By Type Request (0x08) Handle: 0x0001 Error: Attribute Not Found (0x0a) Change-Id: I0b4371b8decebf33710a949552a11c80c544b5f8 Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
f73b3b954e
commit
0d853d64c2
2 changed files with 129 additions and 27 deletions
|
@ -34,6 +34,7 @@
|
||||||
#include <toolchain.h>
|
#include <toolchain.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <misc/byteorder.h>
|
#include <misc/byteorder.h>
|
||||||
#include <misc/util.h>
|
#include <misc/util.h>
|
||||||
|
|
||||||
|
@ -109,10 +110,29 @@ static void att_mtu_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
bt_conn_send(conn, buf);
|
bt_conn_send(conn, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool range_is_valid(uint16_t start, uint16_t end, uint16_t *err)
|
||||||
|
{
|
||||||
|
/* Handle 0 is invalid */
|
||||||
|
if (!start || !end) {
|
||||||
|
if (err)
|
||||||
|
*err = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if range is valid */
|
||||||
|
if (start > end) {
|
||||||
|
if (err)
|
||||||
|
*err = start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void att_find_info_req(struct bt_conn *conn, struct bt_buf *data)
|
static void att_find_info_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
{
|
{
|
||||||
struct bt_att_find_info_req *req;
|
struct bt_att_find_info_req *req;
|
||||||
uint16_t start_handle, end_handle;
|
uint16_t start_handle, end_handle, err_handle;
|
||||||
|
|
||||||
if (data->len != sizeof(*req)) {
|
if (data->len != sizeof(*req)) {
|
||||||
send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, 0,
|
send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, 0,
|
||||||
|
@ -127,15 +147,10 @@ static void att_find_info_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
|
|
||||||
BT_DBG("start_handle %u end_handle %u\n", start_handle, end_handle);
|
BT_DBG("start_handle %u end_handle %u\n", start_handle, end_handle);
|
||||||
|
|
||||||
/* Handle 0 is invalid */
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
||||||
if (!start_handle || !end_handle) {
|
send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, err_handle,
|
||||||
start_handle = 0;
|
BT_ATT_ERR_INVALID_HANDLE);
|
||||||
goto invalid_handle;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if range is valid */
|
|
||||||
if (start_handle > end_handle) {
|
|
||||||
goto invalid_handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Generate proper response once a database is defined */
|
/* TODO: Generate proper response once a database is defined */
|
||||||
|
@ -143,16 +158,12 @@ static void att_find_info_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle,
|
send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle,
|
||||||
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
invalid_handle:
|
|
||||||
send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle,
|
|
||||||
BT_ATT_ERR_INVALID_HANDLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void att_find_type_req(struct bt_conn *conn, struct bt_buf *data)
|
static void att_find_type_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
{
|
{
|
||||||
struct bt_att_find_type_req *req;
|
struct bt_att_find_type_req *req;
|
||||||
uint16_t start_handle, end_handle, type;
|
uint16_t start_handle, end_handle, err_handle, type;
|
||||||
uint8_t *value;
|
uint8_t *value;
|
||||||
|
|
||||||
if (data->len < sizeof(*req)) {
|
if (data->len < sizeof(*req)) {
|
||||||
|
@ -171,15 +182,10 @@ static void att_find_type_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
BT_DBG("start_handle %u end_handle %u type %u\n", start_handle,
|
BT_DBG("start_handle %u end_handle %u type %u\n", start_handle,
|
||||||
end_handle, type);
|
end_handle, type);
|
||||||
|
|
||||||
/* Handle 0 is invalid */
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
||||||
if (!start_handle || !end_handle) {
|
send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, err_handle,
|
||||||
start_handle = 0;
|
BT_ATT_ERR_INVALID_HANDLE);
|
||||||
goto invalid_handle;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if range is valid */
|
|
||||||
if (start_handle > end_handle) {
|
|
||||||
goto invalid_handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Generate proper response once a database is defined */
|
/* TODO: Generate proper response once a database is defined */
|
||||||
|
@ -188,10 +194,71 @@ static void att_find_type_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
invalid_handle:
|
static bool uuid_create(struct bt_uuid *uuid, uint8_t *data, uint8_t len)
|
||||||
send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle,
|
{
|
||||||
BT_ATT_ERR_INVALID_HANDLE);
|
uint16_t u16;
|
||||||
|
|
||||||
|
if (len > sizeof(uuid->u128))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 2:
|
||||||
|
uuid->type = BT_UUID_16;
|
||||||
|
/* TODO: Add unaligned helpers for these operations */
|
||||||
|
memcpy(&u16, data, len);
|
||||||
|
uuid->u16 = sys_le16_to_cpu(u16);
|
||||||
|
return true;
|
||||||
|
case 16:
|
||||||
|
uuid->type = BT_UUID_128;
|
||||||
|
memcpy(uuid->u128, data, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void att_read_type_req(struct bt_conn *conn, struct bt_buf *data)
|
||||||
|
{
|
||||||
|
struct bt_att_read_type_req *req;
|
||||||
|
uint16_t start_handle, end_handle, err_handle;
|
||||||
|
struct bt_uuid uuid;
|
||||||
|
|
||||||
|
/* Type can only be UUID16 or UUID128 */
|
||||||
|
if (data->len != sizeof(*req) + sizeof(uuid.u16) &&
|
||||||
|
data->len != sizeof(*req) + sizeof(uuid.u128)) {
|
||||||
|
send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, 0,
|
||||||
|
BT_ATT_ERR_INVALID_PDU);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
req = (void *)data->data;
|
||||||
|
|
||||||
|
start_handle = sys_le16_to_cpu(req->start_handle);
|
||||||
|
end_handle = sys_le16_to_cpu(req->end_handle);
|
||||||
|
bt_buf_pull(data, sizeof(*req));
|
||||||
|
|
||||||
|
if (!uuid_create(&uuid, data->data, data->len)) {
|
||||||
|
send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, 0,
|
||||||
|
BT_ATT_ERR_UNLIKELY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_DBG("start_handle %u end_handle %u type %u\n",
|
||||||
|
start_handle, end_handle, uuid.u16);
|
||||||
|
|
||||||
|
if (!range_is_valid(start_handle, end_handle, &err_handle)) {
|
||||||
|
send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, err_handle,
|
||||||
|
BT_ATT_ERR_INVALID_HANDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Generate proper response once a database is defined */
|
||||||
|
|
||||||
|
send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, start_handle,
|
||||||
|
BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
|
@ -217,6 +284,9 @@ void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf)
|
||||||
case BT_ATT_OP_FIND_TYPE_REQ:
|
case BT_ATT_OP_FIND_TYPE_REQ:
|
||||||
att_find_type_req(conn, buf);
|
att_find_type_req(conn, buf);
|
||||||
break;
|
break;
|
||||||
|
case BT_ATT_OP_READ_TYPE_REQ:
|
||||||
|
att_read_type_req(conn, buf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BT_DBG("Unhandled ATT code %u\n", hdr->code);
|
BT_DBG("Unhandled ATT code %u\n", hdr->code);
|
||||||
send_err_rsp(conn, hdr->code, 0, BT_ATT_ERR_NOT_SUPPORTED);
|
send_err_rsp(conn, hdr->code, 0, BT_ATT_ERR_NOT_SUPPORTED);
|
||||||
|
|
|
@ -121,5 +121,37 @@ struct bt_att_find_type_rsp {
|
||||||
struct bt_att_handle_group list[0];
|
struct bt_att_handle_group list[0];
|
||||||
} PACK_STRUCT;
|
} PACK_STRUCT;
|
||||||
|
|
||||||
|
#define BT_UUID_16 0x00
|
||||||
|
#define BT_UUID_128 0x01
|
||||||
|
|
||||||
|
/* TODO: Move UUID to its own file */
|
||||||
|
struct bt_uuid {
|
||||||
|
uint8_t type;
|
||||||
|
union {
|
||||||
|
uint16_t u16;
|
||||||
|
uint8_t u128[16];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read By Type Request */
|
||||||
|
#define BT_ATT_OP_READ_TYPE_REQ 0x08
|
||||||
|
struct bt_att_read_type_req {
|
||||||
|
uint16_t start_handle;
|
||||||
|
uint16_t end_handle;
|
||||||
|
uint8_t uuid[0];
|
||||||
|
} PACK_STRUCT;
|
||||||
|
|
||||||
|
struct bt_att_data {
|
||||||
|
uint16_t handle;
|
||||||
|
uint8_t value[0];
|
||||||
|
} PACK_STRUCT;
|
||||||
|
|
||||||
|
/* Read By Type Response */
|
||||||
|
#define BT_ATT_OP_READ_TYPE_RSP 0x09
|
||||||
|
struct bt_att_read_type_rsp {
|
||||||
|
uint8_t len;
|
||||||
|
struct bt_att_data data[0];
|
||||||
|
} PACK_STRUCT;
|
||||||
|
|
||||||
void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf);
|
void bt_att_recv(struct bt_conn *conn, struct bt_buf *buf);
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue