shared/gatt-client: Add support for EATT features
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 10 May 2019 07:35:00 +0000 (10:35 +0300)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 12 Apr 2021 09:00:48 +0000 (14:30 +0530)
This enables EATT in the Client Features if the EATT characteristic is
present in the database.

Change-Id: I174d644283b8026b2b82e2c1bd2d9aeb5f5bcdbe
Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
attrib/gattrib.c
lib/uuid.h
peripheral/gatt.c
src/device.c
src/shared/att.c
src/shared/att.h
src/shared/gatt-client.c
src/shared/gatt-client.h
src/shared/gatt-server.c
tools/btgatt-client.c
unit/test-gatt.c

index 2e1e39a..d5cee12 100755 (executable)
@@ -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;
index e4f0d23..5545b2f 100755 (executable)
@@ -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,
index 08541c4..bbbf3f5 100755 (executable)
@@ -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);
index 3e76b96..f683a6d 100644 (file)
@@ -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;
index 74998b9..9a05c98 100755 (executable)
@@ -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;
 
index 2e2d4af..952d9ec 100755 (executable)
@@ -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,
index 16fdff9..654a782 100644 (file)
@@ -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;
index d04b782..9b48eca 100755 (executable)
@@ -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);
index fec66b3..ca4deb3 100644 (file)
@@ -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;
index 56a13de..f08ca3a 100755 (executable)
@@ -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);
index 5169a51..440ea03 100755 (executable)
@@ -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),