share/att: Add EATT support
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 9 May 2019 08:06:25 +0000 (11:06 +0300)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 12 Apr 2021 09:00:48 +0000 (14:30 +0530)
This adds EATT support to bt_att, EATT bearers are handled as
additional channels which auto allocated for queued requests.

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
src/gatt-database.c
src/shared/att-types.h
src/shared/att.c
src/shared/att.h
src/shared/gatt-client.c

index 927224e..21345de 100644 (file)
@@ -2525,10 +2525,10 @@ static void append_options(DBusMessageIter *iter, void *user_data)
        uint16_t mtu;
 
        switch (op->link_type) {
-       case BT_ATT_LINK_BREDR:
+       case BT_ATT_BREDR:
                link = "BR/EDR";
                break;
-       case BT_ATT_LINK_LE:
+       case BT_ATT_LE:
                link = "LE";
                break;
        default:
index d71e974..956c59c 100755 (executable)
 #define __packed __attribute__((packed))
 #endif
 
+#define BT_ATT_CID             4
+#define BT_ATT_PSM             31
+#define BT_ATT_EATT_PSM                0x27
+
 #define BT_ATT_SECURITY_AUTO   0
 #define BT_ATT_SECURITY_LOW    1
 #define BT_ATT_SECURITY_MEDIUM 2
 #define BT_ATT_MAX_LE_MTU      517
 #define BT_ATT_MAX_VALUE_LEN   512
 
-#define BT_ATT_LINK_BREDR      0x00
-#define BT_ATT_LINK_LE         0x01
-#define BT_ATT_LINK_LOCAL      0xff
+#define BT_ATT_BREDR           0x00
+#define BT_ATT_LE              0x01
+#define BT_ATT_EATT            0x02
+#define BT_ATT_LOCAL           0xff
 
 /* ATT protocol opcodes */
 #define BT_ATT_OP_ERROR_RSP                    0x01
@@ -170,3 +175,8 @@ struct bt_att_pdu_error_rsp {
 
 /* GATT Characteristic Client Features Bitfield values */
 #define BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING           0x01
+#define BT_GATT_CHRC_CLI_FEAT_EATT                     0x02
+#define BT_GATT_CHRC_CLI_FEAT_NFY_MULTI                        0x04
+
+/* GATT Characteristic Server Features Bitfield values */
+#define BT_GATT_CHRC_SERVER_FEAT_EATT                  0x01
index 1186803..74998b9 100755 (executable)
 
 struct att_send_op;
 
-struct bt_att {
-       int ref_count;
+struct bt_att_chan {
+       struct bt_att *att;
        int fd;
        struct io *io;
-       bool io_on_l2cap;
-       int io_sec_level;               /* Only used for non-L2CAP */
-       uint8_t enc_size;
+       uint8_t type;
+       int sec_level;                  /* Only used for non-L2CAP */
 
-       struct queue *req_queue;        /* Queued ATT protocol requests */
        struct att_send_op *pending_req;
-       struct queue *ind_queue;        /* Queued ATT protocol indications */
        struct att_send_op *pending_ind;
-       struct queue *write_queue;      /* Queue of PDUs ready to send */
        bool writer_active;
 
-       struct queue *notify_list;      /* List of registered callbacks */
-       struct queue *disconn_list;     /* List of disconnect handlers */
-
        bool in_req;                    /* There's a pending incoming request */
 
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       bdaddr_t bdaddr;
-       uint8_t bdaddr_type;
-       bool service_change_indication; /* Service changed indication status */
-#endif
-
        uint8_t *buf;
        uint16_t mtu;
+};
+
+struct bt_att {
+       int ref_count;
+       bool close_on_unref;
+       struct queue *chans;
+       uint8_t enc_size;
+       uint16_t mtu;                   /* Biggest possible MTU */
+
+       struct queue *notify_list;      /* List of registered callbacks */
+       struct queue *disconn_list;     /* List of disconnect handlers */
 
        unsigned int next_send_id;      /* IDs for "send" ops */
        unsigned int next_reg_id;       /* IDs for registered callbacks */
 
+       struct queue *req_queue;        /* Queued ATT protocol requests */
+       struct queue *ind_queue;        /* Queued ATT protocol indications */
+       struct queue *write_queue;      /* Queue of PDUs ready to send */
+
        bt_att_timeout_func_t timeout_callback;
        bt_att_destroy_func_t timeout_destroy;
        void *timeout_data;
 
        bt_att_debug_func_t debug_callback;
        bt_att_destroy_func_t debug_destroy;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       bool service_change_indication; /* Service changed indication status */
+#endif
+
        void *debug_data;
 
        struct bt_crypto *crypto;
@@ -374,8 +383,9 @@ static struct att_send_op *create_att_send_op(struct bt_att *att,
        return op;
 }
 
-static struct att_send_op *pick_next_send_op(struct bt_att *att)
+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 */
@@ -386,7 +396,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
        /* If there is no pending request, pick an operation from the
         * request queue.
         */
-       if (!att->pending_req) {
+       if (!chan->pending_req) {
                op = queue_pop_head(att->req_queue);
                if (op)
                        return op;
@@ -395,7 +405,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
        /* There is either a request pending or no requests queued. If there is
         * no pending indication, pick an operation from the indication queue.
         */
-       if (!att->pending_ind) {
+       if (!chan->pending_ind) {
                op = queue_pop_head(att->ind_queue);
                if (op)
                        return op;
@@ -405,22 +415,23 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att)
 }
 
 struct timeout_data {
-       struct bt_att *att;
+       struct bt_att_chan *chan;
        unsigned int id;
 };
 
 static bool timeout_cb(void *user_data)
 {
        struct timeout_data *timeout = user_data;
-       struct bt_att *att = timeout->att;
+       struct bt_att_chan *chan = timeout->chan;
+       struct bt_att *att = chan->att;
        struct att_send_op *op = NULL;
 
-       if (att->pending_req && att->pending_req->id == timeout->id) {
-               op = att->pending_req;
-               att->pending_req = NULL;
-       } else if (att->pending_ind && att->pending_ind->id == timeout->id) {
-               op = att->pending_ind;
-               att->pending_ind = NULL;
+       if (chan->pending_req && chan->pending_req->id == timeout->id) {
+               op = chan->pending_req;
+               chan->pending_req = NULL;
+       } else if (chan->pending_ind && chan->pending_ind->id == timeout->id) {
+               op = chan->pending_ind;
+               chan->pending_ind = NULL;
        }
 
        if (!op)
@@ -440,27 +451,28 @@ static bool timeout_cb(void *user_data)
         * This should trigger an io disconnect event which will clean up the
         * io and notify the upper layer.
         */
-       io_shutdown(att->io);
+       io_shutdown(chan->io);
 
        return false;
 }
 
 static void write_watch_destroy(void *user_data)
 {
-       struct bt_att *att = user_data;
+       struct bt_att_chan *chan = user_data;
 
-       att->writer_active = false;
+       chan->writer_active = false;
 }
 
 static bool can_write_data(struct io *io, void *user_data)
 {
-       struct bt_att *att = 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(att);
+       op = pick_next_send_op(chan);
        if (!op)
                return false;
 
@@ -490,10 +502,10 @@ static bool can_write_data(struct io *io, void *user_data)
         */
        switch (op->type) {
        case ATT_OP_TYPE_REQ:
-               att->pending_req = op;
+               chan->pending_req = op;
                break;
        case ATT_OP_TYPE_IND:
-               att->pending_ind = op;
+               chan->pending_ind = op;
                break;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        case ATT_OP_TYPE_CMD:
@@ -504,7 +516,7 @@ static bool can_write_data(struct io *io, void *user_data)
 #endif
        case ATT_OP_TYPE_RSP:
                /* Set in_req to false to indicate that no request is pending */
-               att->in_req = false;
+               chan->in_req = false;
 
                /* fall through */
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -519,7 +531,7 @@ static bool can_write_data(struct io *io, void *user_data)
        }
 
        timeout = new0(struct timeout_data, 1);
-       timeout->att = att;
+       timeout->chan = chan;
        timeout->id = op->id;
        op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb,
                                                                timeout, free);
@@ -528,25 +540,33 @@ static bool can_write_data(struct io *io, void *user_data)
        return true;
 }
 
-static void wakeup_writer(struct bt_att *att)
+static void wakeup_chan_writer(void *data, void *user_data)
 {
-       if (att->writer_active)
+       struct bt_att_chan *chan = data;
+       struct bt_att *att = chan->att;
+
+       if (chan->writer_active)
                return;
 
        /* Set the write handler only if there is anything that can be sent
         * at all.
         */
        if (queue_isempty(att->write_queue)) {
-               if ((att->pending_req || queue_isempty(att->req_queue)) &&
-                       (att->pending_ind || queue_isempty(att->ind_queue)))
+               if ((chan->pending_req || queue_isempty(att->req_queue)) &&
+                       (chan->pending_ind || queue_isempty(att->ind_queue)))
                        return;
        }
 
-       if (!io_set_write_handler(att->io, can_write_data, att,
+       if (!io_set_write_handler(chan->io, can_write_data, chan,
                                                        write_watch_destroy))
                return;
 
-       att->writer_active = true;
+       chan->writer_active = true;
+}
+
+static void wakeup_writer(struct bt_att *att)
+{
+       queue_foreach(att->chans, wakeup_chan_writer, NULL);
 }
 
 static void disconn_handler(void *data, void *user_data)
@@ -571,44 +591,66 @@ static void disc_att_send_op(void *data)
        destroy_att_send_op(op);
 }
 
+static void bt_att_chan_free(void *data)
+{
+       struct bt_att_chan *chan = data;
+
+       if (chan->pending_req)
+               destroy_att_send_op(chan->pending_req);
+
+       if (chan->pending_ind)
+               destroy_att_send_op(chan->pending_ind);
+
+       io_destroy(chan->io);
+
+       free(chan->buf);
+       free(chan);
+}
+
 static bool disconnect_cb(struct io *io, void *user_data)
 {
-       struct bt_att *att = user_data;
+       struct bt_att_chan *chan = user_data;
+       struct bt_att *att = chan->att;
        int err;
        socklen_t len;
 
        len = sizeof(err);
 
-       if (getsockopt(att->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
-               util_debug(att->debug_callback, att->debug_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));
                err = 0;
        }
 
-       util_debug(att->debug_callback, att->debug_data,
-                                       "Physical link disconnected: %s",
-                                       strerror(err));
+       util_debug(chan->att->debug_callback, chan->att->debug_data,
+                                       "Channel %p disconnected: %s",
+                                       chan, strerror(err));
 
-       io_destroy(att->io);
-       att->io = NULL;
-       att->fd = -1;
+       /* Dettach channel */
+       queue_remove(att->chans, chan);
 
        /* Notify request callbacks */
        queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
        queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
        queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);
 
-       if (att->pending_req) {
-               disc_att_send_op(att->pending_req);
-               att->pending_req = NULL;
+       if (chan->pending_req) {
+               disc_att_send_op(chan->pending_req);
+               chan->pending_req = NULL;
        }
 
-       if (att->pending_ind) {
-               disc_att_send_op(att->pending_ind);
-               att->pending_ind = NULL;
+       if (chan->pending_ind) {
+               disc_att_send_op(chan->pending_ind);
+               chan->pending_ind = NULL;
        }
 
+       bt_att_chan_free(chan);
+
+       /* Don't run disconnect callback if there are channels left */
+       if (!queue_isempty(att->chans))
+               return false;
+
        bt_att_ref(att);
 
        queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err));
@@ -619,14 +661,49 @@ static bool disconnect_cb(struct io *io, void *user_data)
        return false;
 }
 
-static bool change_security(struct bt_att *att, uint8_t ecode)
+static int bt_att_chan_get_security(struct bt_att_chan *chan)
+{
+       struct bt_security sec;
+       socklen_t len;
+
+       if (chan->type == BT_ATT_LOCAL)
+               return chan->sec_level;
+
+       memset(&sec, 0, sizeof(sec));
+       len = sizeof(sec);
+       if (getsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
+               return -EIO;
+
+       return sec.level;
+}
+
+static bool bt_att_chan_set_security(struct bt_att_chan *chan, int level)
+{
+       struct bt_security sec;
+
+       if (chan->type == BT_ATT_LOCAL) {
+               chan->sec_level = level;
+               return true;
+       }
+
+       memset(&sec, 0, sizeof(sec));
+       sec.level = level;
+
+       if (setsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
+                                                       sizeof(sec)) < 0)
+               return false;
+
+       return true;
+}
+
+static bool change_security(struct bt_att_chan *chan, uint8_t ecode)
 {
        int security;
 
-       if (att->io_sec_level != BT_ATT_SECURITY_AUTO)
+       if (chan->sec_level != BT_ATT_SECURITY_AUTO)
                return false;
 
-       security = bt_att_get_security(att, NULL);
+       security = bt_att_chan_get_security(chan);
 
        if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
                                        security < BT_ATT_SECURITY_MEDIUM) {
@@ -644,14 +721,15 @@ static bool change_security(struct bt_att *att, uint8_t ecode)
                return false;
        }
 
-       return bt_att_set_security(att, security);
+       return bt_att_chan_set_security(chan, security);
 }
 
-static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
+static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu,
                                        ssize_t pdu_len, uint8_t *opcode)
 {
+       struct bt_att *att = chan->att;
        const struct bt_att_pdu_error_rsp *rsp;
-       struct att_send_op *op = att->pending_req;
+       struct att_send_op *op = chan->pending_req;
 
        if (pdu_len != sizeof(*rsp)) {
                *opcode = 0;
@@ -663,7 +741,7 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
        *opcode = rsp->opcode;
 
        /* Attempt to change security */
-       if (!change_security(att, rsp->ecode))
+       if (!change_security(chan, rsp->ecode))
                return false;
 
        /* Remove timeout_id if outstanding */
@@ -675,16 +753,17 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
        util_debug(att->debug_callback, att->debug_data,
                                                "Retrying operation %p", op);
 
-       att->pending_req = NULL;
+       chan->pending_req = NULL;
 
        /* Push operation back to request queue */
        return queue_push_head(att->req_queue, op);
 }
 
-static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu,
                                                                ssize_t pdu_len)
 {
-       struct att_send_op *op = att->pending_req;
+       struct bt_att *att = chan->att;
+       struct att_send_op *op = chan->pending_req;
        uint8_t req_opcode;
        uint8_t rsp_opcode;
        uint8_t *rsp_pdu = NULL;
@@ -697,7 +776,7 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
        if (!op) {
                util_debug(att->debug_callback, att->debug_data,
                                        "Received unexpected ATT response");
-               io_shutdown(att->io);
+               io_shutdown(chan->io);
                return;
        }
 
@@ -707,8 +786,8 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
         */
        if (opcode == BT_ATT_OP_ERROR_RSP) {
                /* Return if error response cause a retry */
-               if (handle_error_rsp(att, pdu, pdu_len, &req_opcode)) {
-                       wakeup_writer(att);
+               if (handle_error_rsp(chan, pdu, pdu_len, &req_opcode)) {
+                       wakeup_chan_writer(chan, NULL);
                        return;
                }
        } else if (!(req_opcode = get_req_opcode(opcode)))
@@ -737,14 +816,15 @@ done:
                op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data);
 
        destroy_att_send_op(op);
-       att->pending_req = NULL;
+       chan->pending_req = NULL;
 
-       wakeup_writer(att);
+       wakeup_chan_writer(chan, NULL);
 }
 
-static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
+static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len)
 {
-       struct att_send_op *op = att->pending_ind;
+       struct bt_att *att = chan->att;
+       struct att_send_op *op = chan->pending_ind;
 
        /*
         * Disconnect the bearer if the confirmation is unexpected or the PDU is
@@ -753,7 +833,7 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
        if (!op || pdu_len) {
                util_debug(att->debug_callback, att->debug_data,
                                "Received unexpected/invalid ATT confirmation");
-               io_shutdown(att->io);
+               io_shutdown(chan->io);
                return;
        }
 
@@ -761,9 +841,9 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
                op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data);
 
        destroy_att_send_op(op);
-       att->pending_ind = NULL;
+       chan->pending_ind = NULL;
 
-       wakeup_writer(att);
+       wakeup_chan_writer(chan, NULL);
 }
 
 struct notify_data {
@@ -833,9 +913,10 @@ fail:
        return false;
 }
 
-static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
-                                                               ssize_t pdu_len)
+static void handle_notify(struct bt_att_chan *chan, uint8_t opcode,
+                                               uint8_t *pdu, ssize_t pdu_len)
 {
+       struct bt_att *att = chan->att;
        const struct queue_entry *entry;
        bool found;
 
@@ -867,7 +948,7 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
                 * link since the MTU size is negotiated using L2CAP channel
                 * configuration procedures.
                 */
-               if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR) {
+               if (bt_att_get_link_type(att) == BT_ATT_BREDR) {
                        switch (opcode) {
                        case BT_ATT_OP_MTU_REQ:
                                goto not_supported;
@@ -898,22 +979,23 @@ not_supported:
 
 static bool can_read_data(struct io *io, void *user_data)
 {
-       struct bt_att *att = user_data;
+       struct bt_att_chan *chan = user_data;
+       struct bt_att *att = chan->att;
        uint8_t opcode;
        uint8_t *pdu;
        ssize_t bytes_read;
 
-       bytes_read = read(att->fd, att->buf, att->mtu);
+       bytes_read = read(chan->fd, chan->buf, chan->mtu);
        if (bytes_read < 0)
                return false;
 
-       util_hexdump('>', att->buf, bytes_read,
-                                       att->debug_callback, att->debug_data);
+       util_hexdump('>', chan->buf, bytes_read,
+                               att->debug_callback, att->debug_data);
 
        if (bytes_read < ATT_MIN_PDU_LEN)
                return true;
 
-       pdu = att->buf;
+       pdu = chan->buf;
        opcode = pdu[0];
 
        bt_att_ref(att);
@@ -923,12 +1005,12 @@ static bool can_read_data(struct io *io, void *user_data)
        case ATT_OP_TYPE_RSP:
                util_debug(att->debug_callback, att->debug_data,
                                "ATT response received: 0x%02x", opcode);
-               handle_rsp(att, opcode, pdu + 1, bytes_read - 1);
+               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);
-               handle_conf(att, pdu + 1, bytes_read - 1);
+               handle_conf(chan, pdu + 1, bytes_read - 1);
                break;
        case ATT_OP_TYPE_REQ:
                /*
@@ -936,17 +1018,17 @@ static bool can_read_data(struct io *io, void *user_data)
                 * protocol was violated. Disconnect the bearer, which will
                 * promptly notify the upper layer via disconnect handlers.
                 */
-               if (att->in_req) {
+               if (chan->in_req) {
                        util_debug(att->debug_callback, att->debug_data,
                                        "Received request while another is "
                                        "pending: 0x%02x", opcode);
-                       io_shutdown(att->io);
-                       bt_att_unref(att);
+                       io_shutdown(chan->io);
+                       bt_att_unref(chan->att);
 
                        return false;
                }
 
-               att->in_req = true;
+               chan->in_req = true;
                /* fall through */
        case ATT_OP_TYPE_CMD:
        case ATT_OP_TYPE_NOT:
@@ -959,7 +1041,7 @@ static bool can_read_data(struct io *io, void *user_data)
                 */
                util_debug(att->debug_callback, att->debug_data,
                                        "ATT PDU received: 0x%02x", opcode);
-               handle_notify(att, opcode, pdu + 1, bytes_read - 1);
+               handle_notify(chan, opcode, pdu + 1, bytes_read - 1);
                break;
        }
 
@@ -995,21 +1077,8 @@ static bool is_io_l2cap_based(int fd)
 
 static void bt_att_free(struct bt_att *att)
 {
-       if (att->pending_req)
-               destroy_att_send_op(att->pending_req);
-
-       if (att->pending_ind)
-               destroy_att_send_op(att->pending_ind);
-
-       io_destroy(att->io);
        bt_crypto_unref(att->crypto);
 
-       queue_destroy(att->req_queue, NULL);
-       queue_destroy(att->ind_queue, NULL);
-       queue_destroy(att->write_queue, NULL);
-       queue_destroy(att->notify_list, NULL);
-       queue_destroy(att->disconn_list, NULL);
-
        if (att->timeout_destroy)
                att->timeout_destroy(att->timeout_data);
 
@@ -1019,7 +1088,12 @@ static void bt_att_free(struct bt_att *att)
        free(att->local_sign);
        free(att->remote_sign);
 
-       free(att->buf);
+       queue_destroy(att->req_queue, NULL);
+       queue_destroy(att->ind_queue, NULL);
+       queue_destroy(att->write_queue, NULL);
+       queue_destroy(att->notify_list, NULL);
+       queue_destroy(att->disconn_list, NULL);
+       queue_destroy(att->chans, bt_att_chan_free);
 
        free(att);
 }
@@ -1036,60 +1110,101 @@ static uint16_t get_l2cap_mtu(int fd)
        return l2o.omtu;
 }
 
-struct bt_att *bt_att_new(int fd, bool ext_signed)
+static uint8_t io_get_type(int fd)
 {
-       struct bt_att *att;
+       struct sockaddr_l2 src;
+       socklen_t len;
 
-       if (fd < 0)
-               return NULL;
+       if (!is_io_l2cap_based(fd))
+               return BT_ATT_LOCAL;
 
-       att = new0(struct bt_att, 1);
-       att->fd = fd;
+       len = sizeof(src);
+       memset(&src, 0, len);
+       if (getsockname(fd, (void *)&src, &len) < 0)
+               return -errno;
 
-       att->io = io_new(fd);
-       if (!att->io)
-               goto fail;
+       if (src.l2_bdaddr_type == BDADDR_BREDR)
+               return BT_ATT_BREDR;
 
-       /* crypto is optional, if not available leave it NULL */
-       if (!ext_signed)
-               att->crypto = bt_crypto_new();
+       return BT_ATT_LE;
+}
 
-       att->req_queue = queue_new();
-       att->ind_queue = queue_new();
-       att->write_queue = queue_new();
-       att->notify_list = queue_new();
-       att->disconn_list = queue_new();
+static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type)
+{
+       struct bt_att_chan *chan;
 
-       if (!io_set_read_handler(att->io, can_read_data, att, NULL))
+       if (fd < 0)
+               return NULL;
+
+       chan = new0(struct bt_att_chan, 1);
+       chan->fd = fd;
+
+       chan->io = io_new(fd);
+       if (!chan->io)
                goto fail;
 
-       if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL))
+       if (!io_set_read_handler(chan->io, can_read_data, chan, NULL))
                goto fail;
 
-       att->io_on_l2cap = is_io_l2cap_based(att->fd);
-       if (!att->io_on_l2cap)
-               att->io_sec_level = BT_ATT_SECURITY_LOW;
+       if (!io_set_disconnect_handler(chan->io, disconnect_cb, chan, NULL))
+               goto fail;
 
-       if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR)
-               att->mtu = get_l2cap_mtu(att->fd);
-       else
-               att->mtu = BT_ATT_DEFAULT_LE_MTU;
+       chan->type = type;
+       switch (chan->type) {
+       case BT_ATT_LOCAL:
+               chan->sec_level = BT_ATT_SECURITY_LOW;
+               /* fall through */
+       case BT_ATT_LE:
+               chan->mtu = BT_ATT_DEFAULT_LE_MTU;
+               break;
+       default:
+               chan->mtu = get_l2cap_mtu(chan->fd);
+       }
 
-       if (att->mtu < BT_ATT_DEFAULT_LE_MTU)
+       if (chan->mtu < BT_ATT_DEFAULT_LE_MTU)
                goto fail;
 
-       att->buf = malloc(att->mtu);
-       if (!att->buf)
+       chan->buf = malloc(chan->mtu);
+       if (!chan->buf)
                goto fail;
 
-       return bt_att_ref(att);
+       return chan;
 
 fail:
-       bt_att_free(att);
+       bt_att_chan_free(chan);
 
        return NULL;
 }
 
+struct bt_att *bt_att_new(int fd, bool ext_signed)
+{
+       struct bt_att *att;
+       struct bt_att_chan *chan;
+
+       chan = bt_att_chan_new(fd, io_get_type(fd));
+       if (!chan)
+               return NULL;
+
+       att = new0(struct bt_att, 1);
+       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();
+
+       att->req_queue = queue_new();
+       att->ind_queue = queue_new();
+       att->write_queue = queue_new();
+       att->notify_list = queue_new();
+       att->disconn_list = queue_new();
+
+       return bt_att_ref(att);
+}
+
 struct bt_att *bt_att_ref(struct bt_att *att)
 {
        if (!att)
@@ -1116,18 +1231,67 @@ void bt_att_unref(struct bt_att *att)
 
 bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
 {
-       if (!att || !att->io)
+       const struct queue_entry *entry;
+
+       if (!att)
                return false;
 
-       return io_set_close_on_destroy(att->io, do_close);
+       att->close_on_unref = do_close;
+
+       for (entry = queue_get_entries(att->chans); entry;
+                                               entry = entry->next) {
+               struct bt_att_chan *chan = entry->data;
+
+               if (!io_set_close_on_destroy(chan->io, do_close))
+                       return false;
+       }
+
+       return true;
+}
+
+int bt_att_attach_fd(struct bt_att *att, int fd)
+{
+       struct bt_att_chan *chan;
+
+       if (!att || fd < 0)
+               return -EINVAL;
+
+       chan = bt_att_chan_new(fd, BT_ATT_EATT);
+       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);
+
+       return 0;
 }
 
 int bt_att_get_fd(struct bt_att *att)
 {
+       struct bt_att_chan *chan;
+
        if (!att)
                return -1;
 
-       return att->fd;
+       if (queue_isempty(att->chans))
+               return -ENOTCONN;
+
+       chan = queue_peek_head(att->chans);
+
+       return chan->fd;
+}
+
+int bt_att_get_channels(struct bt_att *att)
+{
+       if (!att)
+               return 0;
+
+       return queue_length(att->chans);
 }
 
 bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
@@ -1156,6 +1320,7 @@ uint16_t bt_att_get_mtu(struct bt_att *att)
 
 bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
 {
+       struct bt_att_chan *chan;
        void *buf;
 
        if (!att)
@@ -1164,38 +1329,36 @@ 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);
+       if (!chan)
+               return -ENOTCONN;
+
        buf = malloc(mtu);
        if (!buf)
                return false;
 
-       free(att->buf);
+       free(chan->buf);
 
-       att->mtu = mtu;
-       att->buf = buf;
+       chan->mtu = mtu;
+       chan->buf = buf;
+       if (chan->mtu > att->mtu)
+               att->mtu = chan->mtu;
 
        return true;
 }
 
 uint8_t bt_att_get_link_type(struct bt_att *att)
 {
-       struct sockaddr_l2 src;
-       socklen_t len;
+       struct bt_att_chan *chan;
 
        if (!att)
                return -EINVAL;
 
-       if (!att->io_on_l2cap)
-               return BT_ATT_LINK_LOCAL;
-
-       len = sizeof(src);
-       memset(&src, 0, len);
-       if (getsockname(att->fd, (void *)&src, &len) < 0)
-               return -errno;
+       chan = queue_peek_head(att->chans);
+       if (!chan)
+               return -ENOTCONN;
 
-       if (src.l2_bdaddr_type == BDADDR_BREDR)
-               return BT_ATT_LINK_BREDR;
-
-       return BT_ATT_LINK_LE;
+       return chan->type;
 }
 
 bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
@@ -1222,7 +1385,7 @@ unsigned int bt_att_register_disconnect(struct bt_att *att,
 {
        struct att_disconn *disconn;
 
-       if (!att || !att->io)
+       if (!att || queue_isempty(att->chans))
                return 0;
 
        disconn = new0(struct att_disconn, 1);
@@ -1251,7 +1414,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
                return false;
 
        /* Check if disconnect is running */
-       if (!att->io) {
+       if (!queue_isempty(att->chans)) {
                disconn = queue_find(att->disconn_list, match_disconn_id,
                                                        UINT_TO_PTR(id));
                if (!disconn)
@@ -1278,7 +1441,7 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
        struct att_send_op *op;
        bool result;
 
-       if (!att || !att->io)
+       if (!att || queue_isempty(att->chans))
                return 0;
 
        op = create_att_send_op(att, opcode, pdu, length, callback, user_data,
@@ -1330,21 +1493,31 @@ static bool match_op_id(const void *a, const void *b)
 
 bool bt_att_cancel(struct bt_att *att, unsigned int id)
 {
+       const struct queue_entry *entry;
        struct att_send_op *op;
 
        if (!att || !id)
                return false;
 
-       if (att->pending_req && att->pending_req->id == id) {
-               /* Don't cancel the pending request; remove it's handlers */
-               cancel_att_send_op(att->pending_req);
-               return true;
-       }
+       for (entry = queue_get_entries(att->chans); entry;
+                                               entry = entry->next) {
+               struct bt_att_chan *chan = entry->data;
 
-       if (att->pending_ind && att->pending_ind->id == id) {
-               /* Don't cancel the pending indication; remove it's handlers */
-               cancel_att_send_op(att->pending_ind);
-               return true;
+               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(att->req_queue, match_op_id, UINT_TO_PTR(id));
@@ -1372,6 +1545,8 @@ done:
 
 bool bt_att_cancel_all(struct bt_att *att)
 {
+       const struct queue_entry *entry;
+
        if (!att)
                return false;
 
@@ -1379,13 +1554,22 @@ bool bt_att_cancel_all(struct bt_att *att)
        queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op);
        queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op);
 
-       if (att->pending_req)
-               /* Don't cancel the pending request; remove it's handlers */
-               cancel_att_send_op(att->pending_req);
-
-       if (att->pending_ind)
-               /* Don't cancel the pending request; remove it's handlers */
-               cancel_att_send_op(att->pending_ind);
+       for (entry = queue_get_entries(att->chans); entry;
+                                               entry = entry->next) {
+               struct bt_att_chan *chan = entry->data;
+
+               if (chan->pending_req)
+                       /* Don't cancel the pending request; remove it's
+                        * handlers
+                        */
+                       cancel_att_send_op(chan->pending_req);
+
+               if (chan->pending_ind)
+                       /* Don't cancel the pending request; remove it's
+                        * handlers
+                        */
+                       cancel_att_send_op(chan->pending_ind);
+       }
 
        return true;
 }
@@ -1446,7 +1630,7 @@ unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
 {
        struct att_notify *notify;
 
-       if (!att || !callback || !att->io)
+       if (!att || !callback || queue_isempty(att->chans))
                return 0;
 
        notify = new0(struct att_notify, 1);
@@ -1497,51 +1681,39 @@ bool bt_att_unregister_all(struct bt_att *att)
 
 int bt_att_get_security(struct bt_att *att, uint8_t *enc_size)
 {
-       struct bt_security sec;
-       socklen_t len;
+       struct bt_att_chan *chan;
+       int ret;
 
        if (!att)
                return -EINVAL;
 
-       if (!att->io_on_l2cap) {
-               if (enc_size)
-                       *enc_size = att->enc_size;
+       chan = queue_peek_head(att->chans);
+       if (!chan)
+               return -ENOTCONN;
 
-               return att->io_sec_level;
-       }
-
-       memset(&sec, 0, sizeof(sec));
-       len = sizeof(sec);
-       if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
-               return -EIO;
+       ret = bt_att_chan_get_security(chan);
+       if (ret < 0)
+               return ret;
 
        if (enc_size)
                *enc_size = att->enc_size;
 
-       return sec.level;
+       return ret;
 }
 
 bool bt_att_set_security(struct bt_att *att, int level)
 {
-       struct bt_security sec;
+       struct bt_att_chan *chan;
 
        if (!att || level < BT_ATT_SECURITY_AUTO ||
                                                level > BT_ATT_SECURITY_HIGH)
                return false;
 
-       if (!att->io_on_l2cap) {
-               att->io_sec_level = level;
-               return true;
-       }
+       chan = queue_peek_head(att->chans);
+       if (!chan)
+               return -ENOTCONN;
 
-       memset(&sec, 0, sizeof(sec));
-       sec.level = level;
-
-       if (setsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
-                                                       sizeof(sec)) < 0)
-               return false;
-
-       return true;
+       return bt_att_chan_set_security(chan, level);
 }
 
 void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size)
index e6cc8ed..2e2d4af 100755 (executable)
@@ -40,6 +40,10 @@ bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
 
 int bt_att_get_fd(struct bt_att *att);
 
+int bt_att_attach_fd(struct bt_att *att, int fd);
+
+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,
@@ -103,4 +107,4 @@ bool bt_att_get_remote_addr(struct bt_att *att,
                                bdaddr_t *bdaddr, uint8_t *bdaddr_type);
 bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value);
 bool bt_att_get_svc_changed_indication_registered(struct bt_att *att);
-#endif
\ No newline at end of file
+#endif
index 95cb9dd..16fdff9 100644 (file)
@@ -2130,7 +2130,7 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
         * the MTU size is negotiated using L2CAP channel configuration
         * procedures.
         */
-       if (bt_att_get_link_type(client->att) == BT_ATT_LINK_BREDR)
+       if (bt_att_get_link_type(client->att) == BT_ATT_BREDR)
                goto discover;
 
        /* Check if MTU needs to be send */