[PATCH BlueZ 4/4] core/gatt: Auto retry if read/write fails

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx>

This adds auto retry logic to ReadValue and WriteValue when a security
error happen, it will first attempt to elevate the security to match the
requirement and then retry sending the same operation.
---
 src/gatt-client.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 176 insertions(+), 6 deletions(-)

diff --git a/src/gatt-client.c b/src/gatt-client.c
index 2e26ed7..7fd814c 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -223,6 +223,9 @@ static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
 	return true;
 }
 
+struct async_dbus_op;
+
+typedef bool (*async_dbus_op_retry_t)(struct async_dbus_op *op, uint8_t ecode);
 typedef bool (*async_dbus_op_complete_t)(void *data);
 
 struct async_dbus_op {
@@ -230,6 +233,7 @@ struct async_dbus_op {
 	DBusMessage *msg;
 	void *data;
 	uint16_t offset;
+	async_dbus_op_retry_t retry;
 	async_dbus_op_complete_t complete;
 };
 
@@ -337,6 +341,23 @@ static void read_op_cb(struct gatt_db_attribute *attrib, int err,
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
 }
 
+static bool check_security(struct bt_gatt_client *gatt, uint8_t ecode)
+{
+	int security;
+
+	security = bt_gatt_client_get_security(gatt);
+	if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
+					security < BT_ATT_SECURITY_MEDIUM)
+		security = BT_ATT_SECURITY_MEDIUM;
+	else if (ecode == BT_ATT_ERROR_AUTHENTICATION &&
+					security < BT_ATT_SECURITY_HIGH)
+		security = BT_ATT_SECURITY_HIGH;
+	else
+		return false;
+
+	return bt_gatt_client_set_security(gatt, security);
+}
+
 static void desc_read_cb(bool success, uint8_t att_ecode,
 					const uint8_t *value, uint16_t length,
 					void *user_data)
@@ -346,8 +367,12 @@ static void desc_read_cb(bool success, uint8_t att_ecode,
 	struct service *service = desc->chrc->service;
 	DBusMessage *reply;
 
-	if (!success)
+	if (!success) {
+		if (op->retry && op->retry(op, att_ecode))
+			return;
+
 		goto fail;
+	}
 
 	if (!op->offset)
 		gatt_db_attribute_reset(desc->attr);
@@ -393,6 +418,28 @@ fail:
 	return;
 }
 
+static bool desc_read_retry(struct async_dbus_op *op, uint8_t ecode)
+{
+	struct descriptor *desc = op->data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+
+	if (!check_security(gatt, ecode))
+		return false;
+
+	desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
+							desc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+	if (desc->read_id) {
+		op->retry = NULL;
+		return true;
+	}
+
+	async_dbus_op_unref(op);
+
+	return false;
+}
+
 static DBusMessage *descriptor_read_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -412,6 +459,7 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn,
 
 	op->msg = dbus_message_ref(msg);
 	op->data = desc;
+	op->retry = desc_read_retry;
 
 	desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
 							desc_read_cb,
@@ -437,6 +485,9 @@ static void write_result_cb(bool success, bool reliable_error,
 	}
 
 	if (!success) {
+		if (op->retry && op->retry(op, att_ecode))
+			return;
+
 		if (reliable_error)
 			reply = btd_error_failed(op->msg,
 						"Reliable write failed");
@@ -466,6 +517,7 @@ static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
 					struct bt_gatt_client *gatt,
 					bool reliable, const uint8_t *value,
 					size_t value_len, void *data,
+					async_dbus_op_retry_t retry,
 					async_dbus_op_complete_t complete)
 {
 	struct async_dbus_op *op;
@@ -493,7 +545,7 @@ static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
 static unsigned int start_write_request(DBusMessage *msg, uint16_t handle,
 					struct bt_gatt_client *gatt,
 					const uint8_t *value, size_t value_len,
-					void *data,
+					void *data, async_dbus_op_retry_t retry,
 					async_dbus_op_complete_t complete)
 {
 	struct async_dbus_op *op;
@@ -529,6 +581,46 @@ static bool desc_write_complete(void *data)
 	return !!desc->chrc;
 }
 
+static bool desc_write_retry(struct async_dbus_op *op, uint8_t ecode)
+{
+	struct descriptor *desc = op->data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	uint8_t *value = NULL;
+	size_t value_len = 0;
+
+	if (!check_security(gatt, ecode))
+		return false;
+
+	if (!parse_value_arg(op->msg, &value, &value_len))
+		return false;
+
+	/*
+	 * Based on the value length and the MTU, either use a write or a long
+	 * write.
+	 */
+	if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
+		desc->write_id = bt_gatt_client_write_value(gatt, desc->handle,
+							value, value_len,
+							write_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_free);
+	else
+		desc->write_id = bt_gatt_client_write_long_value(gatt,
+							false, desc->handle,
+							0, value, value_len,
+							write_result_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_free);
+	if (desc->write_id) {
+		op->retry = NULL;
+		return true;
+	}
+
+	async_dbus_op_free(op);
+
+	return false;
+}
+
 static DBusMessage *descriptor_write_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -562,11 +654,13 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn,
 		desc->write_id = start_write_request(msg, desc->handle,
 							gatt, value,
 							value_len, desc,
+							desc_write_retry,
 							desc_write_complete);
 	else
 		desc->write_id = start_long_write(msg, desc->handle,
 							gatt, false, value,
 							value_len, desc,
+							desc_write_retry,
 							desc_write_complete);
 
 	if (!desc->write_id)
@@ -790,8 +884,12 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
 	struct service *service = chrc->service;
 	DBusMessage *reply;
 
-	if (!success)
+	if (!success) {
+		if (op->retry && op->retry(op, att_ecode))
+			return;
+
 		goto fail;
+	}
 
 	if (!op->offset)
 		gatt_db_attribute_reset(chrc->attr);
@@ -836,6 +934,29 @@ fail:
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
 }
 
+static bool chrc_read_retry(struct async_dbus_op *op, uint8_t ecode)
+{
+	struct characteristic *chrc = op->data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+	if (!check_security(gatt, ecode))
+		return false;
+
+	chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
+							chrc_read_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_unref);
+	if (chrc->read_id) {
+		/* Only retry once */
+		op->retry = NULL;
+		return true;
+	}
+
+	async_dbus_op_free(op);
+
+	return false;
+}
+
 static DBusMessage *characteristic_read_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -855,6 +976,7 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn,
 
 	op->msg = dbus_message_ref(msg);
 	op->data = chrc;
+	op->retry = chrc_read_retry;
 
 	chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
 							chrc_read_cb,
@@ -881,6 +1003,51 @@ static bool chrc_write_complete(void *data)
 	return !!chrc->service;
 }
 
+static bool chrc_write_retry(struct async_dbus_op *op, uint8_t ecode)
+{
+	struct characteristic *chrc = op->data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	uint8_t *value = NULL;
+	size_t value_len = 0;
+	bool reliable;
+
+	if (!check_security(gatt, ecode))
+		return false;
+
+	if (!parse_value_arg(op->msg, &value, &value_len))
+		return false;
+
+	reliable = (chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE);
+
+	/*
+	 * Based on the value length and the MTU, either use a write or a long
+	 * write.
+	 */
+	if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3 &&
+								!reliable)
+		chrc->write_id = bt_gatt_client_write_value(gatt,
+							chrc->value_handle,
+							value, value_len,
+							write_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_free);
+	else
+		chrc->write_id = bt_gatt_client_write_long_value(gatt, reliable,
+							chrc->value_handle,
+							0, value, value_len,
+							write_result_cb,
+							async_dbus_op_ref(op),
+							async_dbus_op_free);
+	if (chrc->write_id) {
+		op->retry = NULL;
+		return true;
+	}
+
+	async_dbus_op_free(op);
+
+	return false;
+}
+
 static DBusMessage *characteristic_write_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -914,7 +1081,8 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
 		supported = true;
 		chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
 						true, value, value_len,
-						chrc, chrc_write_complete);
+						chrc, chrc_write_retry,
+						chrc_write_complete);
 		if (chrc->write_id)
 			return NULL;
 	}
@@ -931,12 +1099,14 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
 			chrc->write_id = start_write_request(msg,
 						chrc->value_handle,
 						gatt, value, value_len,
-						chrc, chrc_write_complete);
+						chrc, chrc_write_retry,
+						chrc_write_complete);
 		else
 			chrc->write_id = start_long_write(msg,
 						chrc->value_handle, gatt,
 						false, value, value_len,
-						chrc, chrc_write_complete);
+						chrc, chrc_write_retry,
+						chrc_write_complete);
 
 		if (chrc->write_id)
 			return NULL;
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux