diff --git a/drivers/nble/gatt.c b/drivers/nble/gatt.c index 2cd7d3abb37..71d23d2a377 100644 --- a/drivers/nble/gatt.c +++ b/drivers/nble/gatt.c @@ -407,12 +407,6 @@ void bt_gatt_cancel(struct bt_conn *conn) { } -int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles, - size_t count, bt_gatt_read_func_t func) -{ - return -ENOSYS; -} - void on_ble_gattc_write_rsp(const struct ble_gattc_write_rsp *ev, void *priv) { diff --git a/include/bluetooth/gatt.h b/include/bluetooth/gatt.h index 9934dd29bba..b297b4363ae 100644 --- a/include/bluetooth/gatt.h +++ b/include/bluetooth/gatt.h @@ -771,26 +771,40 @@ struct bt_gatt_discover_params { int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *params); +struct bt_gatt_read_params; + /** @brief Read callback function * * @param conn Connection object. * @param err Error code. - * @param data Attribute value data. + * @param params Read parameters used. + * @param data Attribute value data. NULL means read has completed. * @param length Attribute value length. */ typedef uint8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, int err, + struct bt_gatt_read_params *params, const void *data, uint16_t length); /** @brief GATT Read parameters */ struct bt_gatt_read_params { - /** Attribute handle */ - uint16_t handle; - /** Attribute data offset */ - uint16_t offset; /** Read attribute callback */ bt_gatt_read_func_t func; - /** Read destroy callback */ - void (*destroy)(void *user_data); + /** Handles count. + * If equals to 1 single.handle and single.offset are used. + * If >1 Read Multiple Characteristic Values is performed and handles + * are used. + */ + size_t handle_count; + union { + struct { + /** Attribute handle */ + uint16_t handle; + /** Attribute data offset */ + uint16_t offset; + } single; + /** Handles to read in Read Multiple Characteristic Values */ + uint16_t *handles; + }; }; /** @brief Read Attribute Value by handle @@ -908,21 +922,6 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, */ void bt_gatt_cancel(struct bt_conn *conn); -/** @brief Read Multiple Attribute Values by set of handles - * - * Routine to be used to retrieve set of attributes values determined by set of - * handles in one call. - * - * @param conn Connection object. - * @param handles Set of valid handles to attributes. - * @param count Number of handles to be read. - * @param func User callback routine to get retrieved values. - * - * @return 0 in case of success or negative value in case of error. - */ -int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles, - size_t count, bt_gatt_read_func_t func); - #endif /* CONFIG_BLUETOOTH_GATT_CLIENT */ #endif /* CONFIG_BLUETOOTH_CENTRAL || CONFIG_BLUETOOTH_PERIPHERAL */ diff --git a/net/bluetooth/gatt.c b/net/bluetooth/gatt.c index fb823dad647..913d715d565 100644 --- a/net/bluetooth/gatt.c +++ b/net/bluetooth/gatt.c @@ -855,18 +855,18 @@ static uint16_t parse_include(struct bt_conn *conn, const void *pdu, attr->handle = handle; if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { - handle = 0; - goto done; + return 0; } } - /* Stop if could not parse the whole PDU */ - if (length > 0) { - return 0; + /* Whole PDU read without error */ + if (length == 0 && handle) { + return handle; } done: - return handle; + params->func(conn, NULL, params); + return 0; } static uint16_t parse_characteristic(struct bt_conn *conn, const void *pdu, @@ -929,18 +929,18 @@ static uint16_t parse_characteristic(struct bt_conn *conn, const void *pdu, attr->handle = handle; if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { - handle = 0; - goto done; + return 0; } } - /* Stop if could not parse the whole PDU */ - if (length > 0) { - return 0; + /* Whole PDU read without error */ + if (length == 0 && handle) { + return handle; } done: - return handle; + params->func(conn, NULL, params); + return 0; } static void att_read_type_rsp(struct bt_conn *conn, uint8_t err, @@ -963,7 +963,7 @@ static void att_read_type_rsp(struct bt_conn *conn, uint8_t err, } if (!handle) { - goto done; + return; } /* Continue from the last handle */ @@ -1166,12 +1166,12 @@ static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu, BT_DBG("err 0x%02x", err); if (err) { - params->func(conn, err, NULL, 0); - goto done; + params->func(conn, err, params, NULL, 0); + return; } - if (params->func(conn, 0, pdu, length) == BT_GATT_ITER_STOP) { - goto done; + if (params->func(conn, 0, params, pdu, length) == BT_GATT_ITER_STOP) { + return; } /* @@ -1181,21 +1181,15 @@ static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu, * if the rest of the Characteristic Value is required. */ if (length < (bt_att_get_mtu(conn) - 1)) { - goto done; + params->func(conn, 0, params, NULL, 0); + return; } - params->offset += length; + params->single.offset += length; /* Continue reading the attribute */ if (bt_gatt_read(conn, params) < 0) { - params->func(conn, BT_ATT_ERR_UNLIKELY, NULL, 0); - goto done; - } - - return; -done: - if (params->destroy) { - params->destroy(params); + params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0); } } @@ -1211,25 +1205,67 @@ static int gatt_read_blob(struct bt_conn *conn, } req = net_buf_add(buf, sizeof(*req)); - req->handle = sys_cpu_to_le16(params->handle); - req->offset = sys_cpu_to_le16(params->offset); + req->handle = sys_cpu_to_le16(params->single.handle); + req->offset = sys_cpu_to_le16(params->single.offset); - BT_DBG("handle 0x%04x offset 0x%04x", params->handle, params->offset); + BT_DBG("handle 0x%04x offset 0x%04x", params->single.handle, + params->single.offset); return gatt_send(conn, buf, att_read_rsp, params, NULL); } +static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err, + const void *pdu, uint16_t length, + void *user_data) +{ + struct bt_gatt_read_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + if (err) { + params->func(conn, err, params, NULL, 0); + return; + } + + params->func(conn, 0, params, pdu, length); + + /* mark read as complete since read multiple is single response */ + params->func(conn, 0, params, NULL, 0); +} + +static int gatt_read_multiple(struct bt_conn *conn, + struct bt_gatt_read_params *params) +{ + struct net_buf *buf; + uint8_t i; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ, + params->handle_count * sizeof(uint16_t)); + if (!buf) { + return -ENOMEM; + } + + for (i = 0; i < params->handle_count; i++) { + net_buf_add_le16(buf, params->handles[i]); + } + + return gatt_send(conn, buf, att_read_multiple_rsp, params, NULL); +} + int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params) { struct net_buf *buf; struct bt_att_read_req *req; - if (!conn || !params || !params->handle || !params->func || - !params->destroy) { + if (!conn || !params || !params->handle_count || !params->func) { return -EINVAL; } - if (params->offset) { + if (params->handle_count > 1) { + return gatt_read_multiple(conn, params); + } + + if (params->single.offset) { return gatt_read_blob(conn, params); } @@ -1239,9 +1275,9 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params) } req = net_buf_add(buf, sizeof(*req)); - req->handle = sys_cpu_to_le16(params->handle); + req->handle = sys_cpu_to_le16(params->single.handle); - BT_DBG("handle 0x%04x", params->handle); + BT_DBG("handle 0x%04x", params->single.handle); return gatt_send(conn, buf, att_read_rsp, params, NULL); } @@ -1576,48 +1612,6 @@ void bt_gatt_cancel(struct bt_conn *conn) bt_att_cancel(conn); } -static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err, - const void *pdu, uint16_t length, - void *user_data) -{ - bt_gatt_read_func_t func = user_data; - - BT_DBG("err 0x%02x", err); - - if (err) { - func(conn, err, NULL, 0); - return; - } - - func(conn, 0, pdu, length); -} - -int bt_gatt_read_multiple(struct bt_conn *conn, const uint16_t *handles, - size_t count, bt_gatt_read_func_t func) -{ - struct net_buf *buf; - uint8_t i; - - if (!conn || conn->state != BT_CONN_CONNECTED) { - return -ENOTCONN; - } - - if (!handles || count < 2 || !func) { - return -EINVAL; - } - - buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ, - count * sizeof(*handles)); - if (!buf) { - return -ENOMEM; - } - - for (i = 0; i < count; i++) { - net_buf_add_le16(buf, handles[i]); - } - - return gatt_send(conn, buf, att_read_multiple_rsp, func, NULL); -} #endif /* CONFIG_BLUETOOTH_GATT_CLIENT */ void bt_gatt_disconnected(struct bt_conn *conn) diff --git a/samples/bluetooth/shell/src/main.c b/samples/bluetooth/shell/src/main.c index 2e671643f32..92145ac3537 100644 --- a/samples/bluetooth/shell/src/main.c +++ b/samples/bluetooth/shell/src/main.c @@ -770,23 +770,20 @@ done: static struct bt_gatt_read_params read_params; -static uint8_t read_func(struct bt_conn *conn, int err, const void *data, - uint16_t length) +static uint8_t read_func(struct bt_conn *conn, int err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length) { printk("Read complete: err %u length %u\n", err, length); + if (!data) { + memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + return BT_GATT_ITER_CONTINUE; } -static void read_destroy(void *user_data) -{ - struct bt_gatt_read_params *params = user_data; - - printk("Read destroy\n"); - - memset(params, 0, sizeof(*params)); -} - static void cmd_gatt_read(int argc, char *argv[]) { int err; @@ -797,17 +794,17 @@ static void cmd_gatt_read(int argc, char *argv[]) } read_params.func = read_func; - read_params.destroy = read_destroy; if (argc < 2) { printk("handle required\n"); return; } - read_params.handle = strtoul(argv[1], NULL, 16); + read_params.handle_count = 1; + read_params.single.handle = strtoul(argv[1], NULL, 16); if (argc > 2) { - read_params.offset = strtoul(argv[2], NULL, 16); + read_params.single.offset = strtoul(argv[2], NULL, 16); } err = bt_gatt_read(default_conn, &read_params); @@ -828,7 +825,7 @@ void cmd_gatt_mread(int argc, char *argv[]) return; } - if (argc < 2) { + if (argc < 3) { printk("Attribute handles in hex format to read required\n"); return; } @@ -842,7 +839,11 @@ void cmd_gatt_mread(int argc, char *argv[]) h[i] = strtoul(argv[i + 1], NULL, 16); } - err = bt_gatt_read_multiple(default_conn, h, i, read_func); + read_params.func = read_func; + read_params.handle_count = i; + read_params.handles = h; /* not used in read func */ + + err = bt_gatt_read(default_conn, &read_params); if (err) { printk("GATT multiple read request failed (err %d)\n", err); } diff --git a/samples/bluetooth/tester/src/gatt.c b/samples/bluetooth/tester/src/gatt.c index 41292cd30d1..35f74340353 100644 --- a/samples/bluetooth/tester/src/gatt.c +++ b/samples/bluetooth/tester/src/gatt.c @@ -88,15 +88,6 @@ static void gatt_buf_clear(void) memset(&gatt_buf, 0, sizeof(gatt_buf)); } -static bool gatt_buf_isempty(void) -{ - if (gatt_buf.len) { - return false; - } - - return true; -} - static struct bt_gatt_attr *gatt_db_add(const struct bt_gatt_attr *pattern) { static struct bt_gatt_attr *attr = gatt_db; @@ -1199,32 +1190,14 @@ fail_conn: static struct bt_gatt_read_params read_params; -static void read_destroy(void *user_data) +static void read_destroy(struct bt_gatt_read_params *params) { - struct bt_gatt_read_params *params = user_data; - memset(params, 0, sizeof(*params)); - - if (!gatt_buf_isempty()) { - gatt_buf_clear(); - } + gatt_buf_clear(); } -static void read_result(void *user_data) -{ - /* Respond with an error if the buffer was cleared. */ - if (gatt_buf_isempty()) { - tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, - CONTROLLER_INDEX, BTP_STATUS_FAILED); - } else { - tester_send(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX, - gatt_buf.buf, gatt_buf.len); - } - - read_destroy(user_data); -} - -static uint8_t read_cb(struct bt_conn *conn, int err, const void *data, +static uint8_t read_cb(struct bt_conn *conn, int err, + struct bt_gatt_read_params *params, const void *data, uint16_t length) { struct gatt_read_rp *rp = (void *) gatt_buf.buf; @@ -1232,17 +1205,20 @@ static uint8_t read_cb(struct bt_conn *conn, int err, const void *data, /* Respond to the Lower Tester with ATT Error received */ if (err) { rp->att_response = err; + } + + /* read complete */ + if (!data) { + tester_send(BTP_SERVICE_ID_GATT, btp_opcode, CONTROLLER_INDEX, + gatt_buf.buf, gatt_buf.len); + read_destroy(params); return BT_GATT_ITER_STOP; } - /* - * Clear gatt_buf if there is no more space available to cache - * read result. This will cause read_result function to send - * BTP error status to the Lower Tester. - */ if (!gatt_buf_add(data, length)) { - gatt_buf_clear(); - + tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode, + CONTROLLER_INDEX, BTP_STATUS_FAILED); + read_destroy(params); return BT_GATT_ITER_STOP; } @@ -1265,10 +1241,13 @@ static void read(uint8_t *data, uint16_t len) goto fail; } - read_params.handle = sys_le16_to_cpu(cmd->handle); - read_params.offset = 0x0000; + read_params.handle_count = 1; + read_params.single.handle = sys_le16_to_cpu(cmd->handle); + read_params.single.offset = 0x0000; read_params.func = read_cb; - read_params.destroy = read_result; + + /* TODO should be handled as user_data via CONTAINER_OF macro */ + btp_opcode = GATT_READ; if (bt_gatt_read(conn, &read_params) < 0) { read_destroy(&read_params); @@ -1287,20 +1266,6 @@ fail_conn: BTP_STATUS_FAILED); } -static void read_long_result(void *user_data) -{ - /* Respond with an error if the buffer was cleared. */ - if (gatt_buf_isempty()) { - tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG, - CONTROLLER_INDEX, BTP_STATUS_FAILED); - } else { - tester_send(BTP_SERVICE_ID_GATT, GATT_READ_LONG, - CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); - } - - read_destroy(user_data); -} - static void read_long(uint8_t *data, uint16_t len) { const struct gatt_read_long_cmd *cmd = (void *) data; @@ -1315,10 +1280,13 @@ static void read_long(uint8_t *data, uint16_t len) goto fail; } - read_params.handle = sys_le16_to_cpu(cmd->handle); - read_params.offset = sys_le16_to_cpu(cmd->offset); + read_params.handle_count = 1; + read_params.single.handle = sys_le16_to_cpu(cmd->handle); + read_params.single.offset = sys_le16_to_cpu(cmd->offset); read_params.func = read_cb; - read_params.destroy = read_long_result; + + /* TODO should be handled as user_data via CONTAINER_OF macro */ + btp_opcode = GATT_READ_LONG; if (bt_gatt_read(conn, &read_params) < 0) { read_destroy(&read_params); @@ -1337,24 +1305,6 @@ fail_conn: BTP_STATUS_FAILED); } -static uint8_t read_multiple_result(struct bt_conn *conn, int err, - const void *data, uint16_t length) -{ - read_cb(conn, err, data, length); - - if (gatt_buf_isempty()) { - tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, - CONTROLLER_INDEX, BTP_STATUS_FAILED); - } else { - tester_send(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, - CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len); - - gatt_buf_clear(); - } - - return BT_GATT_ITER_STOP; -} - static void read_multiple(uint8_t *data, uint16_t len) { const struct gatt_read_multiple_cmd *cmd = (void *) data; @@ -1375,10 +1325,15 @@ static void read_multiple(uint8_t *data, uint16_t len) goto fail; } - if (bt_gatt_read_multiple(conn, handles, cmd->handles_count, - read_multiple_result) < 0) { - gatt_buf_clear(); + read_params.func = read_cb; + read_params.handle_count = i; + read_params.handles = handles; /* not used in read func */ + /* TODO should be handled as user_data via CONTAINER_OF macro */ + btp_opcode = GATT_READ_MULTIPLE; + + if (bt_gatt_read(conn, &read_params) < 0) { + gatt_buf_clear(); goto fail; }