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:
Luiz Augusto von Dentz 2015-05-06 15:58:46 +03:00 committed by Anas Nashif
commit 0d853d64c2
2 changed files with 129 additions and 27 deletions

View file

@ -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);

View file

@ -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);