From: Luiz Augusto von Dentz Date: Fri, 10 May 2019 07:35:00 +0000 (+0300) Subject: shared/gatt-client: Add support for EATT features X-Git-Tag: accepted/tizen/unified/20210607.124421~246 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3a1c3af915b82716fce50bfad0b21a0970b3d727;p=platform%2Fupstream%2Fbluez.git shared/gatt-client: Add support for EATT features This enables EATT in the Client Features if the EATT characteristic is present in the database. Change-Id: I174d644283b8026b2b82e2c1bd2d9aeb5f5bcdbe Signed-off-by: Anuj Jain Signed-off-by: Ayush Garg --- diff --git a/attrib/gattrib.c b/attrib/gattrib.c index 2e1e39a..d5cee12 100755 --- a/attrib/gattrib.c +++ b/attrib/gattrib.c @@ -275,8 +275,9 @@ static void attrib_callback_result(uint8_t opcode, const void *pdu, free(buf); } -static void attrib_callback_notify(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void attrib_callback_notify(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { uint8_t *buf; struct attrib_callbacks *cb = user_data; diff --git a/lib/uuid.h b/lib/uuid.h index e4f0d23..5545b2f 100755 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -176,6 +176,9 @@ extern "C" { #define GATT_CHARAC_CLI_FEAT 0x2B29 #define GATT_CHARAC_DB_HASH 0x2B2A +/* GATT Server Supported features */ +#define GATT_CHARAC_SERVER_FEAT 0x2B3A + typedef struct { enum { BT_UUID_UNSPEC = 0, diff --git a/peripheral/gatt.c b/peripheral/gatt.c index 08541c4..bbbf3f5 100755 --- a/peripheral/gatt.c +++ b/peripheral/gatt.c @@ -136,7 +136,7 @@ static struct gatt_conn *gatt_conn_new(int fd) return NULL; } - conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu); + conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu, 0); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT client\n"); bt_gatt_server_unref(conn->gatt); diff --git a/src/device.c b/src/device.c index 3e76b96..f683a6d 100644 --- a/src/device.c +++ b/src/device.c @@ -7197,7 +7197,7 @@ static void gatt_client_init(struct btd_device *device) } device->client = bt_gatt_client_new(device->db, device->att, - device->att_mtu); + device->att_mtu, 0); if (!device->client) { DBG("Failed to initialize"); return; diff --git a/src/shared/att.c b/src/shared/att.c index 74998b90..9a05c98 100755 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -56,6 +56,8 @@ struct bt_att_chan { uint8_t type; int sec_level; /* Only used for non-L2CAP */ + struct queue *queue; /* Channel dedicated queue */ + struct att_send_op *pending_req; struct att_send_op *pending_ind; bool writer_active; @@ -388,32 +390,47 @@ static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan) struct bt_att *att = chan->att; struct att_send_op *op; - /* See if any operations are already in the write queue */ - op = queue_pop_head(att->write_queue); + /* Check if there is anything queued on the channel */ + op = queue_pop_head(chan->queue); if (op) return op; + /* See if any operations are already in the write queue */ + op = queue_peek_head(att->write_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->write_queue); + /* If there is no pending request, pick an operation from the * request queue. */ if (!chan->pending_req) { - op = queue_pop_head(att->req_queue); - if (op) - return op; + op = queue_peek_head(att->req_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->req_queue); } /* There is either a request pending or no requests queued. If there is * no pending indication, pick an operation from the indication queue. */ if (!chan->pending_ind) { - op = queue_pop_head(att->ind_queue); - if (op) - return op; + op = queue_peek_head(att->ind_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->ind_queue); } return NULL; } +static void disc_att_send_op(void *data) +{ + struct att_send_op *op = data; + + if (op->callback) + op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); + + destroy_att_send_op(op); +} + struct timeout_data { struct bt_att_chan *chan; unsigned int id; @@ -438,13 +455,14 @@ static bool timeout_cb(void *user_data) return false; util_debug(att->debug_callback, att->debug_data, - "Operation timed out: 0x%02x", op->opcode); + "(chan %p) Operation timed out: 0x%02x", + chan, op->opcode); if (att->timeout_callback) att->timeout_callback(op->id, op->opcode, att->timeout_data); op->timeout_id = 0; - destroy_att_send_op(op); + disc_att_send_op(op); /* * Directly terminate the connection as required by the ATT protocol. @@ -463,26 +481,45 @@ static void write_watch_destroy(void *user_data) chan->writer_active = false; } +static ssize_t bt_att_chan_write(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len) +{ + struct bt_att *att = chan->att; + ssize_t ret; + struct iovec iov; + + iov.iov_base = (void *) pdu; + iov.iov_len = len; + + util_debug(att->debug_callback, att->debug_data, + "(chan %p) ATT op 0x%02x", + chan, opcode); + + ret = io_send(chan->io, &iov, 1); + if (ret < 0) { + util_debug(att->debug_callback, att->debug_data, + "(chan %p) write failed: %s", + chan, strerror(-ret)); + + return ret; + } + + util_hexdump('<', pdu, ret, att->debug_callback, att->debug_data); + + return ret; +} + static bool can_write_data(struct io *io, void *user_data) { struct bt_att_chan *chan = user_data; - struct bt_att *att = chan->att; struct att_send_op *op; struct timeout_data *timeout; - ssize_t ret; - struct iovec iov; op = pick_next_send_op(chan); if (!op) return false; - iov.iov_base = op->pdu; - iov.iov_len = op->len; - - ret = io_send(io, &iov, 1); - if (ret < 0) { - util_debug(att->debug_callback, att->debug_data, - "write failed: %s", strerror(-ret)); + if (!bt_att_chan_write(chan, op->opcode, op->pdu, op->len)) { if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); @@ -491,10 +528,6 @@ static bool can_write_data(struct io *io, void *user_data) return true; } - util_debug(att->debug_callback, att->debug_data, - "ATT op 0x%02x", op->opcode); - - util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data); /* Based on the operation type, set either the pending request or the * pending indication. If it came from the write queue, then there is @@ -551,7 +584,7 @@ static void wakeup_chan_writer(void *data, void *user_data) /* Set the write handler only if there is anything that can be sent * at all. */ - if (queue_isempty(att->write_queue)) { + if (queue_isempty(chan->queue) && queue_isempty(att->write_queue)) { if ((chan->pending_req || queue_isempty(att->req_queue)) && (chan->pending_ind || queue_isempty(att->ind_queue))) return; @@ -581,16 +614,6 @@ static void disconn_handler(void *data, void *user_data) disconn->callback(err, disconn->user_data); } -static void disc_att_send_op(void *data) -{ - struct att_send_op *op = data; - - if (op->callback) - op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); - - destroy_att_send_op(op); -} - static void bt_att_chan_free(void *data) { struct bt_att_chan *chan = data; @@ -601,6 +624,8 @@ static void bt_att_chan_free(void *data) if (chan->pending_ind) destroy_att_send_op(chan->pending_ind); + queue_destroy(chan->queue, destroy_att_send_op); + io_destroy(chan->io); free(chan->buf); @@ -618,8 +643,8 @@ static bool disconnect_cb(struct io *io, void *user_data) if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { util_debug(chan->att->debug_callback, chan->att->debug_data, - "Failed to obtain disconnect error: %s", - strerror(errno)); + "(chan %p) Failed to obtain disconnect" + " error: %s", chan, strerror(errno)); err = 0; } @@ -751,7 +776,8 @@ static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu, } util_debug(att->debug_callback, att->debug_data, - "Retrying operation %p", op); + "(chan %p) Retrying operation " + "%p", chan, op); chan->pending_req = NULL; @@ -775,7 +801,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, */ if (!op) { util_debug(att->debug_callback, att->debug_data, - "Received unexpected ATT response"); + "(chan %p) Received unexpected ATT " + "response", chan); io_shutdown(chan->io); return; } @@ -807,7 +834,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, fail: util_debug(att->debug_callback, att->debug_data, - "Failed to handle response PDU; opcode: 0x%02x", opcode); + "(chan %p) Failed to handle response PDU; opcode: " + "0x%02x", chan, opcode); rsp_opcode = BT_ATT_OP_ERROR_RSP; @@ -832,7 +860,8 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) */ if (!op || pdu_len) { util_debug(att->debug_callback, att->debug_data, - "Received unexpected/invalid ATT confirmation"); + "(chan %p) Received unexpected/invalid ATT " + "confirmation", chan); io_shutdown(chan->io); return; } @@ -958,7 +987,7 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t opcode, found = true; if (notify->callback) - notify->callback(opcode, pdu, pdu_len, + notify->callback(chan, opcode, pdu, pdu_len, notify->user_data); /* callback could remove all entries from notify list */ @@ -989,6 +1018,10 @@ static bool can_read_data(struct io *io, void *user_data) if (bytes_read < 0) return false; + util_debug(att->debug_callback, att->debug_data, + "(chan %p) ATT received: %zd", + chan, bytes_read); + util_hexdump('>', chan->buf, bytes_read, att->debug_callback, att->debug_data); @@ -1004,12 +1037,14 @@ static bool can_read_data(struct io *io, void *user_data) switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, - "ATT response received: 0x%02x", opcode); + "(chan %p) ATT response received: 0x%02x", + chan, opcode); handle_rsp(chan, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: util_debug(att->debug_callback, att->debug_data, - "ATT confirmation received: 0x%02x", opcode); + "(chan %p) ATT confirmation received: 0x%02x", + chan, opcode); handle_conf(chan, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: @@ -1020,8 +1055,9 @@ static bool can_read_data(struct io *io, void *user_data) */ if (chan->in_req) { util_debug(att->debug_callback, att->debug_data, - "Received request while another is " - "pending: 0x%02x", opcode); + "(chan %p) Received request while " + "another is pending: 0x%02x", + chan, opcode); io_shutdown(chan->io); bt_att_unref(chan->att); @@ -1040,7 +1076,8 @@ static bool can_read_data(struct io *io, void *user_data) * let them act on it. */ util_debug(att->debug_callback, att->debug_data, - "ATT PDU received: 0x%02x", opcode); + "(chan %p) ATT PDU received: 0x%02x", + chan, opcode); handle_notify(chan, opcode, pdu + 1, bytes_read - 1); break; } @@ -1098,16 +1135,19 @@ static void bt_att_free(struct bt_att *att) free(att); } -static uint16_t get_l2cap_mtu(int fd) +static uint16_t io_get_mtu(int fd) { socklen_t len; struct l2cap_options l2o; len = sizeof(l2o); - if (getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) - return 0; + if (!getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len)) + return l2o.omtu; + + if (!getsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len)) + return l2o.omtu; - return l2o.omtu; + return 0; } static uint8_t io_get_type(int fd) @@ -1158,7 +1198,7 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) chan->mtu = BT_ATT_DEFAULT_LE_MTU; break; default: - chan->mtu = get_l2cap_mtu(chan->fd); + chan->mtu = io_get_mtu(chan->fd); } if (chan->mtu < BT_ATT_DEFAULT_LE_MTU) @@ -1168,6 +1208,8 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) if (!chan->buf) goto fail; + chan->queue = queue_new(); + return chan; fail: @@ -1176,6 +1218,23 @@ fail: return NULL; } +static void bt_att_attach_chan(struct bt_att *att, struct bt_att_chan *chan) +{ + /* Push to head as EATT channels have higher priority */ + queue_push_head(att->chans, chan); + chan->att = att; + + if (chan->mtu > att->mtu) + att->mtu = chan->mtu; + + io_set_close_on_destroy(chan->io, att->close_on_unref); + + util_debug(att->debug_callback, att->debug_data, "Channel %p attached", + chan); + + wakeup_chan_writer(chan, NULL); +} + struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; @@ -1189,9 +1248,6 @@ struct bt_att *bt_att_new(int fd, bool ext_signed) att->chans = queue_new(); att->mtu = chan->mtu; - queue_push_head(att->chans, chan); - chan->att = att; - /* crypto is optional, if not available leave it NULL */ if (!ext_signed) att->crypto = bt_crypto_new(); @@ -1202,6 +1258,8 @@ struct bt_att *bt_att_new(int fd, bool ext_signed) att->notify_list = queue_new(); att->disconn_list = queue_new(); + bt_att_attach_chan(att, chan); + return bt_att_ref(att); } @@ -1260,13 +1318,7 @@ int bt_att_attach_fd(struct bt_att *att, int fd) if (!chan) return -EINVAL; - queue_push_tail(att->chans, chan); - chan->att = att; - - if (chan->mtu > att->mtu) - att->mtu = chan->mtu; - - io_set_close_on_destroy(chan->io, att->close_on_unref); + bt_att_attach_chan(att, chan); return 0; } @@ -1281,7 +1333,7 @@ int bt_att_get_fd(struct bt_att *att) if (queue_isempty(att->chans)) return -ENOTCONN; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); return chan->fd; } @@ -1329,7 +1381,8 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu) if (mtu < BT_ATT_DEFAULT_LE_MTU) return false; - chan = queue_peek_head(att->chans); + /* Original channel is always the last */ + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1354,7 +1407,7 @@ uint8_t bt_att_get_link_type(struct bt_att *att) if (!att) return -EINVAL; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1414,7 +1467,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id) return false; /* Check if disconnect is running */ - if (!queue_isempty(att->chans)) { + if (queue_isempty(att->chans)) { disconn = queue_find(att->disconn_list, match_disconn_id, UINT_TO_PTR(id)); if (!disconn) @@ -1483,6 +1536,33 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, return op->id; } +unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len, + bt_att_response_func_t callback, + void *user_data, + bt_att_destroy_func_t destroy) +{ + struct att_send_op *op; + + if (!chan || !chan->att) + return -EINVAL; + + op = create_att_send_op(chan->att, opcode, pdu, len, callback, + user_data, destroy); + if (!op) + return -EINVAL; + + if (!queue_push_tail(chan->queue, op)) { + free(op->pdu); + free(op); + return 0; + } + + wakeup_chan_writer(chan, NULL); + + return op->id; +} + static bool match_op_id(const void *a, const void *b) { const struct att_send_op *op = a; @@ -1491,6 +1571,33 @@ static bool match_op_id(const void *a, const void *b) return op->id == id; } +bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id) +{ + struct att_send_op *op; + + if (chan->pending_req && chan->pending_req->id == id) { + /* Don't cancel the pending request; remove it's handlers */ + cancel_att_send_op(chan->pending_req); + return true; + } + + if (chan->pending_ind && chan->pending_ind->id == id) { + /* Don't cancel the pending indication; remove it's handlers. */ + cancel_att_send_op(chan->pending_ind); + return true; + } + + op = queue_remove_if(chan->queue, match_op_id, UINT_TO_PTR(id)); + if (!op) + return false; + + destroy_att_send_op(op); + + wakeup_chan_writer(chan, NULL); + + return true; +} + bool bt_att_cancel(struct bt_att *att, unsigned int id) { const struct queue_entry *entry; @@ -1503,22 +1610,9 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id) entry = entry->next) { struct bt_att_chan *chan = entry->data; - if (chan->pending_req && chan->pending_req->id == id) { - /* Don't cancel the pending request; remove it's - * handlers - */ - cancel_att_send_op(chan->pending_req); - return true; - } - - if (chan->pending_ind && chan->pending_ind->id == id) { - /* Don't cancel the pending indication; remove it's - * handlers. - */ - cancel_att_send_op(chan->pending_ind); + if (bt_att_chan_cancel(chan, id)) return true; } - } op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id)); if (op) @@ -1602,14 +1696,14 @@ static uint8_t att_ecode_from_error(int err) return BT_ATT_ERROR_UNLIKELY; } -unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, +int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error) { struct bt_att_pdu_error_rsp pdu; uint8_t ecode; - if (!att || !opcode) - return 0; + if (!chan || !chan->att || !opcode) + return -EINVAL; ecode = att_ecode_from_error(error); @@ -1619,8 +1713,8 @@ unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, put_le16(handle, &pdu.handle); pdu.ecode = ecode; - return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), - NULL, NULL, NULL); + return bt_att_chan_send_rsp(chan, BT_ATT_OP_ERROR_RSP, &pdu, + sizeof(pdu)); } unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, @@ -1687,7 +1781,7 @@ int bt_att_get_security(struct bt_att *att, uint8_t *enc_size) if (!att) return -EINVAL; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1709,7 +1803,7 @@ bool bt_att_set_security(struct bt_att *att, int level) level > BT_ATT_SECURITY_HIGH) return false; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; diff --git a/src/shared/att.h b/src/shared/att.h index 2e2d4af..952d9ec 100755 --- a/src/shared/att.h +++ b/src/shared/att.h @@ -30,6 +30,7 @@ #endif struct bt_att; +struct bt_att_chan; struct bt_att *bt_att_new(int fd, bool ext_signed); @@ -46,7 +47,8 @@ int bt_att_get_channels(struct bt_att *att); typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); -typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu, +typedef void (*bt_att_notify_func_t)(struct bt_att_chan *chan, + uint8_t opcode, const void *pdu, uint16_t length, void *user_data); typedef void (*bt_att_destroy_func_t)(void *user_data); typedef void (*bt_att_debug_func_t)(const char *str, void *user_data); @@ -71,10 +73,18 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy); +unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len, + bt_att_response_func_t callback, + void *user_data, + bt_att_destroy_func_t destroy); +#define bt_att_chan_send_rsp(chan, opcode, pdu, len) \ + bt_att_chan_send(chan, opcode, pdu, len, NULL, NULL, NULL) +bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id); bool bt_att_cancel(struct bt_att *att, unsigned int id); bool bt_att_cancel_all(struct bt_att *att); -unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, +int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error); unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 16fdff9..654a782 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -63,6 +63,7 @@ struct ready_cb { struct bt_gatt_client { struct bt_att *att; int ref_count; + uint8_t features; struct bt_gatt_client *parent; struct queue *clones; @@ -146,7 +147,7 @@ struct noti { uint16_t length; }; -static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, +static void notify_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data); static void notification_free(void *data) @@ -355,6 +356,7 @@ struct discovery_op { struct queue *ext_prop_desc; struct gatt_db_attribute *cur_svc; struct gatt_db_attribute *hash; + uint8_t server_feat; bool success; uint16_t start; uint16_t end; @@ -1421,6 +1423,9 @@ static void notify_client_ready(struct bt_gatt_client *client, bool success, bt_gatt_client_ref(client); client->ready = success; + if (client->parent) + client->features = client->parent->features; + for (entry = queue_get_entries(client->ready_cbs); entry; entry = entry->next) { struct ready_cb *ready = entry->data; @@ -1574,6 +1579,67 @@ static bool read_db_hash(struct discovery_op *op) return true; } +static void db_server_feat_read(bool success, uint8_t att_ecode, + struct bt_gatt_result *result, void *user_data) +{ + struct discovery_op *op = user_data; + struct bt_gatt_client *client = op->client; + const uint8_t *value; + uint16_t len, handle; + struct bt_gatt_iter iter; + + if (!result) + return; + + bt_gatt_iter_init(&iter, result); + bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value); + + util_debug(client->debug_callback, client->debug_data, + "Server Features found: handle 0x%04x " + "length 0x%04x value 0x%02x", handle, len, + value[0]); + + op->server_feat = value[0]; +} + +static void server_feat_read_value(struct gatt_db_attribute *attrib, + int err, const uint8_t *value, + size_t length, void *user_data) +{ + const uint8_t **feat = user_data; + + if (err) + return; + + *feat = value; +} + +static void read_server_feat(struct discovery_op *op) +{ + struct bt_gatt_client *client = op->client; + struct gatt_db_attribute *attr = NULL; + const uint8_t *feat = NULL; + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (attr) { + /* Read stored value in the db */ + gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL, + server_feat_read_value, &feat); + if (feat) + return; + } + + if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid, + db_server_feat_read, + discovery_op_ref(op), + discovery_op_unref)) + discovery_op_unref(op); +} + static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data) { struct discovery_op *op = user_data; @@ -1610,9 +1676,11 @@ discover: #ifdef TIZEN_FEATURE_BLUEZ_MODIFY op->success = false; #endif + read_server_feat(op); + if (read_db_hash(op)) { op->success = false; - return; + return; } discover_all(op); @@ -1864,7 +1932,7 @@ done: struct noti *noti; while ((noti = queue_pop_head(client->pending_noti))) { - notify_cb(noti->opcode, noti->pdu, + notify_cb(NULL, noti->opcode, noti->pdu, noti->length, client); notification_free(noti); } @@ -2043,12 +2111,41 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value, queue_push_tail(client->svc_chngd_queue, op); } +static void server_feat_write_value(struct gatt_db_attribute *attrib, + int err, void *user_data) +{ + struct bt_gatt_client *client = user_data; + + util_debug(client->debug_callback, client->debug_data, + "Server Features Value set status: %d", err); +} + +static void write_server_features(struct bt_gatt_client *client, uint8_t feat) +{ + bt_uuid_t uuid; + struct gatt_db_attribute *attr = NULL; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (!attr) + return; + + /* Store value in the DB */ + if (!gatt_db_attribute_write(attr, 0, &feat, sizeof(feat), + 0, NULL, server_feat_write_value, + client)) + util_debug(client->debug_callback, client->debug_data, + "Unable to store Server Features"); +} + static void write_client_features(struct bt_gatt_client *client) { bt_uuid_t uuid; struct gatt_db_attribute *attr = NULL; uint16_t handle; - uint8_t value; + const uint8_t *feat = NULL; bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT); @@ -2058,10 +2155,27 @@ static void write_client_features(struct bt_gatt_client *client) return; handle = gatt_db_attribute_get_handle(attr); - value = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; + client->features = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + attr = NULL; + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (attr) { + /* Read stored value in the db */ + gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, + NULL, server_feat_read_value, + &feat); + if (feat && feat[0] & BT_GATT_CHRC_SERVER_FEAT_EATT) + client->features |= BT_GATT_CHRC_CLI_FEAT_EATT; + } + + util_debug(client->debug_callback, client->debug_data, + "Writing Client Features 0x%02x", client->features); - bt_gatt_client_write_value(client, handle, &value, sizeof(value), NULL, - NULL, NULL); + bt_gatt_client_write_value(client, handle, &client->features, + sizeof(client->features), NULL, NULL, NULL); } static void init_complete(struct discovery_op *op, bool success, @@ -2074,6 +2188,9 @@ static void init_complete(struct discovery_op *op, bool success, if (!success) goto fail; + if (op->server_feat) + write_server_features(client, op->server_feat); + write_client_features(client); if (register_service_changed(client)) @@ -2097,7 +2214,7 @@ done: struct noti *noti; while ((noti = queue_pop_head(client->pending_noti))) { - notify_cb(noti->opcode, noti->pdu, + notify_cb(NULL, noti->opcode, noti->pdu, noti->length, client); notification_free(noti); } @@ -2153,6 +2270,8 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu) return true; discover: + read_server_feat(op); + if (read_db_hash(op)) { op->success = false; goto done; @@ -2251,8 +2370,9 @@ static void notify_handler(void *data, void *user_data) notify_data->user_data); } -static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, - void *user_data) +static void notify_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_client *client = user_data; struct pdu_data pdu_data; @@ -2305,9 +2425,20 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, queue_foreach(client->notify_list, notify_handler, &pdu_data); - if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent) + if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent){ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!chan) bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, NULL, NULL, NULL); + else + bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, + NULL, NULL, NULL); + } +#else + bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, + NULL, NULL, NULL); + } +#endif bt_gatt_client_unref(client); } @@ -2381,7 +2512,8 @@ static void att_disconnect_cb(int err, void *user_data) } static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, - struct bt_att *att) + struct bt_att *att, + uint8_t features) { struct bt_gatt_client *client; @@ -2414,6 +2546,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, client->att = bt_att_ref(att); client->db = gatt_db_ref(db); + client->features = features; return client; @@ -2425,14 +2558,15 @@ fail: struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, - uint16_t mtu) + uint16_t mtu, + uint8_t features) { struct bt_gatt_client *client; if (!att || !db) return NULL; - client = gatt_client_new(db, att); + client = gatt_client_new(db, att, features); if (!client) return NULL; @@ -2451,7 +2585,7 @@ struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client) if (!client) return NULL; - clone = gatt_client_new(client->db, client->att); + clone = gatt_client_new(client->db, client->att, client->features); if (!clone) return NULL; @@ -2569,6 +2703,14 @@ uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client) return bt_att_get_mtu(client->att); } +struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client) +{ + if (!client) + return NULL; + + return client->att; +} + struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client) { if (!client || !client->db) @@ -2577,6 +2719,17 @@ struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client) return client->db; } +uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client) +{ + if (!client) + return 0; + + if (client->parent) + return client->parent->features; + + return client->features; +} + static bool match_req_id(const void *a, const void *b) { const struct request *req = a; diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h index d04b782..9b48eca 100755 --- a/src/shared/gatt-client.h +++ b/src/shared/gatt-client.h @@ -31,7 +31,8 @@ struct bt_gatt_client; struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, - uint16_t mtu); + uint16_t mtu, + uint8_t features); struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client); struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client); @@ -73,7 +74,9 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client, bt_gatt_client_destroy_func_t destroy); uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client); +struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client); struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client); +uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client); bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_cancel_all(struct bt_gatt_client *client); diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index fec66b3..ca4deb3 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -52,6 +52,7 @@ #define DEFAULT_MAX_PREP_QUEUE_LEN 30 struct async_read_op { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; bool done; @@ -62,6 +63,7 @@ struct async_read_op { }; struct async_write_op { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; }; @@ -249,8 +251,9 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, return true; } -static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_by_grp_type_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -318,15 +321,14 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, queue_destroy(q, NULL); - bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, - rsp_pdu, rsp_len, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, + rsp_pdu, rsp_len); return; error: queue_destroy(q, NULL); - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } static void async_read_op_destroy(struct async_read_op *op) @@ -360,7 +362,7 @@ static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr, /* Terminate the operation if there was an error */ if (err) { - bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, + bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, handle, err); async_read_op_destroy(op); return; @@ -461,10 +463,8 @@ static void process_read_by_type(struct async_read_op *op) attr = queue_pop_head(op->db_data); if (op->done || !attr) { - bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, - op->pdu_len, - NULL, NULL, - NULL); + bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP, + op->pdu, op->pdu_len); async_read_op_destroy(op); return; } @@ -482,13 +482,14 @@ static void process_read_by_type(struct async_read_op *op) ecode = BT_ATT_ERROR_UNLIKELY; error: - bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, + bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); } -static void read_by_type_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_by_type_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -545,6 +546,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu, goto error; } + op->chan = chan; op->opcode = opcode; op->server = server; op->db_data = q; @@ -555,7 +557,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu, return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } @@ -617,8 +619,9 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q, return true; } -static void find_info_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void find_info_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -667,14 +670,13 @@ static void find_info_cb(uint8_t opcode, const void *pdu, goto error; } - bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len); queue_destroy(q, NULL); return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } @@ -716,8 +718,9 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib, data->len += 4; } -static void find_by_type_val_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void find_by_type_val_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end, uuid16; @@ -762,13 +765,13 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu, if (data.ecode) goto error; - bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu, - data.len, NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP, + data.pdu, data.len); return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode); } static void async_write_op_destroy(struct async_write_op *op) @@ -786,6 +789,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err, struct bt_gatt_server *server = op->server; uint16_t handle; + util_debug(server->debug_callback, server->debug_data, + "Write Complete: err %d", err); + if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) { async_write_op_destroy(op); return; @@ -794,10 +800,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err, handle = gatt_db_attribute_get_handle(attr); if (err) - bt_att_send_error_rsp(server->att, op->opcode, handle, err); + bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); else - bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(op->chan, BT_ATT_OP_WRITE_RSP, NULL, 0); async_write_op_destroy(op); } @@ -812,7 +817,7 @@ static uint8_t authorize_req(struct bt_gatt_server *server, server->authorize_data); } -static void write_cb(uint8_t opcode, const void *pdu, +static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; @@ -860,6 +865,7 @@ static void write_cb(uint8_t opcode, const void *pdu, } op = new0(struct async_write_op, 1); + op->chan = chan; op->server = server; op->opcode = opcode; @@ -890,7 +896,7 @@ error: if (opcode == BT_ATT_OP_WRITE_CMD) return; - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } static uint8_t get_read_rsp_opcode(uint8_t opcode) @@ -926,6 +932,9 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err, uint16_t mtu; uint16_t handle; + util_debug(server->debug_callback, server->debug_data, + "Read Complete: err %d", err); + if (!server) { async_read_op_destroy(op); return; @@ -935,22 +944,21 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err, handle = gatt_db_attribute_get_handle(attr); if (err) { - bt_att_send_error_rsp(server->att, op->opcode, handle, err); + bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); async_read_op_destroy(op); return; } rsp_opcode = get_read_rsp_opcode(op->opcode); - bt_att_send(server->att, rsp_opcode, len ? value : NULL, - MIN((unsigned) mtu - 1, len), - NULL, NULL, NULL); + bt_att_chan_send_rsp(op->chan, rsp_opcode, len ? value : NULL, + MIN((unsigned int) mtu - 1, len)); async_read_op_destroy(op); } -static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, - uint16_t handle, - uint16_t offset) +static void handle_read_req(struct bt_att_chan *chan, + struct bt_gatt_server *server, uint8_t opcode, + uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; @@ -983,6 +991,7 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, } op = new0(struct async_read_op, 1); + op->chan = chan; op->opcode = opcode; op->server = server; server->pending_read_op = op; @@ -1003,34 +1012,35 @@ error: if (op) async_read_op_destroy(op); - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } -static void read_cb(uint8_t opcode, const void *pdu, +static void read_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle; if (length != 2) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } handle = get_le16(pdu); - handle_read_req(server, opcode, handle, 0); + handle_read_req(chan, server, opcode, handle, 0); } -static void read_blob_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle, offset; if (length != 4) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } @@ -1038,10 +1048,11 @@ static void read_blob_cb(uint8_t opcode, const void *pdu, handle = get_le16(pdu); offset = get_le16(pdu + 2); - handle_read_req(server, opcode, handle, offset); + handle_read_req(chan, server, opcode, handle, offset); } struct read_multiple_resp_data { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint16_t *handles; size_t cur_handle; @@ -1068,7 +1079,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, uint8_t ecode; if (err != 0) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; @@ -1078,7 +1089,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, handle, ecode); read_multiple_resp_data_free(data); return; @@ -1093,8 +1104,8 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { - bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, - data->rsp_data, data->length, NULL, NULL, NULL); + bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP, + data->rsp_data, data->length); read_multiple_resp_data_free(data); return; } @@ -1108,7 +1119,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, data->handles[data->cur_handle]); if (!next_attr) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); @@ -1119,7 +1130,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); @@ -1127,8 +1138,9 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, } } -static void read_multiple_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; @@ -1142,6 +1154,7 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, } data = new0(struct read_multiple_resp_data, 1); + data->chan = chan; data->handles = NULL; data->rsp_data = NULL; data->server = server; @@ -1178,7 +1191,7 @@ error: if (data) read_multiple_resp_data_free(data); - bt_att_send_error_rsp(server->att, opcode, 0, ecode); + bt_att_chan_send_error_rsp(chan, opcode, 0, ecode); } static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle, @@ -1269,6 +1282,7 @@ static bool store_prep_data(struct bt_gatt_server *server, } struct prep_write_complete_data { + struct bt_att_chan *chan; void *pdu; uint16_t length; struct bt_gatt_server *server; @@ -1284,8 +1298,8 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, handle = get_le16(pwcd->pdu); if (err) { - bt_att_send_error_rsp(pwcd->server->att, - BT_ATT_OP_PREP_WRITE_REQ, handle, err); + bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ, + handle, err); free(pwcd->pdu); free(pwcd); @@ -1296,19 +1310,20 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4, &((uint8_t *) pwcd->pdu)[4])) - bt_att_send_error_rsp(pwcd->server->att, - BT_ATT_OP_PREP_WRITE_RSP, handle, + bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, + handle, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); - bt_att_send(pwcd->server->att, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, - pwcd->length, NULL, NULL, NULL); + bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, + pwcd->length); free(pwcd->pdu); free(pwcd); } -static void prep_write_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void prep_write_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = 0; @@ -1346,6 +1361,7 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, goto error; pwcd = new0(struct prep_write_complete_data, 1); + pwcd->chan = chan; pwcd->pdu = malloc(length); memcpy(pwcd->pdu, pdu, length); pwcd->length = length; @@ -1362,23 +1378,28 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, ecode = BT_ATT_ERROR_UNLIKELY; error: - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } -static void exec_next_prep_write(struct bt_gatt_server *server, - uint16_t ehandle, int err); +struct exec_data { + struct bt_att_chan *chan; + struct bt_gatt_server *server; +}; + +static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, + int err); static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { - struct bt_gatt_server *server = user_data; + struct exec_data *data = user_data; uint16_t handle = gatt_db_attribute_get_handle(attr); - exec_next_prep_write(server, handle, err); + exec_next_prep_write(data, handle, err); } -static void exec_next_prep_write(struct bt_gatt_server *server, - uint16_t ehandle, int err) +static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, + int err) { struct prep_write_data *next = NULL; struct gatt_db_attribute *attr; @@ -1387,14 +1408,15 @@ static void exec_next_prep_write(struct bt_gatt_server *server, if (err) goto error; - next = queue_pop_head(server->prep_queue); + next = queue_pop_head(data->server->prep_queue); if (!next) { - bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_RSP, + NULL, 0); + free(data); return; } - attr = gatt_db_get_attribute(server->db, next->handle); + attr = gatt_db_get_attribute(data->server->db, next->handle); if (!attr) { err = BT_ATT_ERROR_UNLIKELY; goto error; @@ -1403,8 +1425,8 @@ static void exec_next_prep_write(struct bt_gatt_server *server, status = gatt_db_attribute_write(attr, next->offset, next->value, next->length, BT_ATT_OP_EXEC_WRITE_REQ, - server->att, - exec_write_complete_cb, server); + data->server->att, + exec_write_complete_cb, data); prep_write_data_destroy(next); @@ -1414,11 +1436,12 @@ static void exec_next_prep_write(struct bt_gatt_server *server, err = BT_ATT_ERROR_UNLIKELY; error: - queue_remove_all(server->prep_queue, NULL, NULL, + queue_remove_all(data->server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); + free(data); } static bool find_no_reliable_characteristic(const void *data, @@ -1429,10 +1452,12 @@ static bool find_no_reliable_characteristic(const void *data, return !prep_data->reliable_supported; } -static void exec_write_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void exec_write_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; + struct exec_data *data; uint8_t flags; uint8_t ecode; bool write; @@ -1460,8 +1485,7 @@ static void exec_write_cb(uint8_t opcode, const void *pdu, if (!write) { queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0); return; } @@ -1478,18 +1502,22 @@ static void exec_write_cb(uint8_t opcode, const void *pdu, } } - exec_next_prep_write(server, 0, 0); + data = new0(struct exec_data, 1); + data->chan = chan; + data->server = server; + exec_next_prep_write(data, 0, 0); return; error: queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } -static void exchange_mtu_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void exchange_mtu_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t client_rx_mtu; @@ -1497,7 +1525,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, uint8_t rsp_pdu[2]; if (length != 2) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } @@ -1508,8 +1536,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, /* Respond with the server MTU */ put_le16(server->mtu, rsp_pdu); - bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL, - NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2); /* Set MTU to be the minimum */ server->mtu = final_mtu; diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 56a13de..f08ca3a 100755 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -217,7 +217,7 @@ static struct client *client_create(int fd, uint16_t mtu) return NULL; } - cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu); + cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0); if (!cli->gatt) { fprintf(stderr, "Failed to create GATT client\n"); gatt_db_unref(cli->db); diff --git a/unit/test-gatt.c b/unit/test-gatt.c index 5169a51..440ea03 100755 --- a/unit/test-gatt.c +++ b/unit/test-gatt.c @@ -125,8 +125,16 @@ struct context { raw_pdu(0x03, 0x00, 0x02) #ifdef TIZEN_FEATURE_BLUEZ_MODIFY -#define SERVICE_DATA_1_PDUS \ +#define READ_SERVER_FEAT_PDUS \ + raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x3a, 0x2b), \ + raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a) + +#define CLIENT_INIT_PDUS \ MTU_EXCHANGE_CLIENT_PDUS, \ + READ_SERVER_FEAT_PDUS + +#define SERVICE_DATA_1_PDUS \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ @@ -153,7 +161,7 @@ struct context { raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29) #else #define SERVICE_DATA_1_PDUS \ - MTU_EXCHANGE_CLIENT_PDUS, \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ @@ -180,7 +188,7 @@ struct context { #ifdef TIZEN_FEATURE_BLUEZ_MODIFY #define SERVICE_DATA_2_PDUS \ - MTU_EXCHANGE_CLIENT_PDUS, \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ @@ -808,7 +816,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data) g_assert(context->client_db); context->client = bt_gatt_client_new(context->client_db, - context->att, mtu); + context->att, mtu, 0); g_assert(context->client); bt_gatt_client_set_debug(context->client, print_debug, @@ -2554,6 +2562,7 @@ int main(int argc, char *argv[]) define_test_att("/TP/GAD/CL/BV-01-C", test_search_primary, NULL, NULL, raw_pdu(0x02, 0x00, 0x02), raw_pdu(0x03, 0x00, 0x02), + MTU_EXCHANGE_CLIENT_PDUS, raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x00, 0x18, 0x20, 0x00, 0x29, 0x00, 0xb0, 0x68, @@ -3646,7 +3655,7 @@ int main(int argc, char *argv[]) define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db, &test_notification_1, - MTU_EXCHANGE_CLIENT_PDUS, + CLIENT_INIT_PDUS, SMALL_DB_DISCOVERY_PDUS, raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00), raw_pdu(0x13), @@ -3680,7 +3689,7 @@ int main(int argc, char *argv[]) define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db, &test_indication_1, - MTU_EXCHANGE_CLIENT_PDUS, + CLIENT_INIT_PDUS, SMALL_DB_DISCOVERY_PDUS, raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00), raw_pdu(0x13),