summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorArman Uguray <armansito@chromium.org>2014-10-13 14:09:58 -0700
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-10-20 15:02:34 +0300
commita95a75483158d00dcce2f7c724175aefa8783f17 (patch)
tree26a78a380f88d3762955cd2030276a2173168d55 /src
parentd674ddab6d3dfa06ce041703b89f23776a948373 (diff)
downloadbluez-a95a75483158d00dcce2f7c724175aefa8783f17.tar.gz
shared/att: Fix handling of multiple requests
With this patch, when bt_att is being used for the server role, it now makes sure that a second request drops the connection unless a response for a previous request has been sent.
Diffstat (limited to 'src')
-rw-r--r--src/shared/att.c102
1 files changed, 62 insertions, 40 deletions
diff --git a/src/shared/att.c b/src/shared/att.c
index de35aef9d..63cd19ba0 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -64,6 +64,8 @@ struct bt_att {
bool in_disconn;
bool need_disconn_cleanup;
+ bool in_req; /* There's a pending incoming request */
+
uint8_t *buf;
uint16_t mtu;
@@ -460,6 +462,9 @@ static bool can_write_data(struct io *io, void *user_data)
case ATT_OP_TYPE_IND:
att->pending_ind = op;
break;
+ case ATT_OP_TYPE_RSP:
+ /* Set in_req to false to indicate that no request is pending */
+ att->in_req = false;
default:
destroy_att_send_op(op);
return true;
@@ -604,6 +609,46 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
bt_att_unref(att);
}
+static void disconn_handler(void *data, void *user_data)
+{
+ struct att_disconn *disconn = data;
+
+ if (disconn->removed)
+ return;
+
+ if (disconn->callback)
+ disconn->callback(disconn->user_data);
+}
+
+static bool disconnect_cb(struct io *io, void *user_data)
+{
+ struct bt_att *att = user_data;
+
+ io_destroy(att->io);
+ att->io = NULL;
+
+ util_debug(att->debug_callback, att->debug_data,
+ "Physical link disconnected");
+
+ bt_att_ref(att);
+ att->in_disconn = true;
+ queue_foreach(att->disconn_list, disconn_handler, NULL);
+ att->in_disconn = false;
+
+ if (att->need_disconn_cleanup) {
+ queue_remove_all(att->disconn_list, match_disconn_removed, NULL,
+ destroy_att_disconn);
+ att->need_disconn_cleanup = false;
+ }
+
+ bt_att_cancel_all(att);
+ bt_att_unregister_all(att);
+
+ bt_att_unref(att);
+
+ return false;
+}
+
static bool can_read_data(struct io *io, void *user_data)
{
struct bt_att *att = user_data;
@@ -635,6 +680,23 @@ static bool can_read_data(struct io *io, void *user_data)
util_debug(att->debug_callback, att->debug_data,
"ATT opcode cannot be handled: 0x%02x", opcode);
break;
+ case ATT_OP_TYPE_REQ:
+ /*
+ * If a request is currently pending, then the sequential
+ * protocol was violated. Disconnect the bearer and notify the
+ * upper-layer.
+ */
+ if (att->in_req) {
+ util_debug(att->debug_callback, att->debug_data,
+ "Received request while another is "
+ "pending: 0x%02x", opcode);
+ disconnect_cb(att->io, att);
+ return false;
+ }
+
+ att->in_req = true;
+
+ /* Fall through to the next case */
default:
/* For all other opcodes notify the upper layer of the PDU and
* let them act on it.
@@ -648,46 +710,6 @@ static bool can_read_data(struct io *io, void *user_data)
return true;
}
-static void disconn_handler(void *data, void *user_data)
-{
- struct att_disconn *disconn = data;
-
- if (disconn->removed)
- return;
-
- if (disconn->callback)
- disconn->callback(disconn->user_data);
-}
-
-static bool disconnect_cb(struct io *io, void *user_data)
-{
- struct bt_att *att = user_data;
-
- io_destroy(att->io);
- att->io = NULL;
-
- util_debug(att->debug_callback, att->debug_data,
- "Physical link disconnected");
-
- bt_att_ref(att);
- att->in_disconn = true;
- queue_foreach(att->disconn_list, disconn_handler, NULL);
- att->in_disconn = false;
-
- if (att->need_disconn_cleanup) {
- queue_remove_all(att->disconn_list, match_disconn_removed, NULL,
- destroy_att_disconn);
- att->need_disconn_cleanup = false;
- }
-
- bt_att_cancel_all(att);
- bt_att_unregister_all(att);
-
- bt_att_unref(att);
-
- return false;
-}
-
struct bt_att *bt_att_new(int fd)
{
struct bt_att *att;