Diff between d8eee67e0a49d69c135542b2f9c2cd981cde543f and 0302fc9ebf769c3a7275d73c2356a87ba9f36b36

Changed Files

File Additions Deletions Status
src/gatt-client.c +201 -121 modified

Full Patch

diff --git a/src/gatt-client.c b/src/gatt-client.c
index d3f2daf..8e76503 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -87,6 +87,11 @@ struct descriptor {
 	uint16_t handle;
 	bt_uuid_t uuid;
 	char *path;
+
+	bool in_read;
+	bool value_known;
+	uint8_t *value;
+	size_t value_len;
 };
 
 static gboolean descriptor_get_uuid(const GDBusPropertyTable *property,
@@ -117,22 +122,201 @@ static gboolean descriptor_get_characteristic(
 static gboolean descriptor_get_value(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
+	struct descriptor *desc = data;
 	DBusMessageIter array;
+	size_t i;
 
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
 
-	/* TODO: Implement this once the value is cached */
+	if (desc->value_known) {
+		for (i = 0; i < desc->value_len; i++)
+			dbus_message_iter_append_basic(&array, DBUS_TYPE_BYTE,
+							desc->value + i);
+	}
 
 	dbus_message_iter_close_container(iter, &array);
 
 	return TRUE;
 }
 
+static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct descriptor *desc = data;
+
+	return desc->value_known ? TRUE : FALSE;
+}
+
+static bool resize_value_buffer(size_t new_len, uint8_t **value, size_t *len)
+{
+	uint8_t *ptr;
+
+	if (*len == new_len)
+		return true;
+
+	if (!new_len) {
+		free(*value);
+		*value = NULL;
+		*len = 0;
+
+		return true;
+	}
+
+	ptr = realloc(*value, sizeof(uint8_t) * new_len);
+	if (!ptr)
+		return false;
+
+	*value = ptr;
+	*len = new_len;
+
+	return true;
+}
+
+static void update_value_property(const uint8_t *value, size_t len,
+					uint8_t **cur_value, size_t *cur_len,
+					bool *value_known,
+					const char *path, const char *iface)
+{
+	/*
+	 * If the value is the same, then only updated it if wasn't previously
+	 * known.
+	 */
+	if (*value_known && *cur_len == len &&
+			!memcmp(*cur_value, value, sizeof(uint8_t) * len))
+		return;
+
+	if (resize_value_buffer(len, cur_value, cur_len)) {
+		*value_known = true;
+		memcpy(*cur_value, value, sizeof(uint8_t) * len);
+	} else {
+		/*
+		 * Failed to resize the buffer. Since we don't want to show a
+		 * stale value, if the value was previously known then free and
+		 * hide it.
+		 */
+		free(*cur_value);
+		*cur_value = NULL;
+		*cur_len = 0;
+		*value_known = false;
+	}
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), path, iface,
+								"Value");
+}
+
+struct async_dbus_op {
+	DBusMessage *msg;
+	void *data;
+};
+
+static void async_dbus_op_free(void *data)
+{
+	struct async_dbus_op *op = data;
+
+	dbus_message_unref(op->msg);
+	free(op);
+}
+
+static void message_append_byte_array(DBusMessage *msg, const uint8_t *bytes,
+								size_t len)
+{
+	DBusMessageIter iter, array;
+
+	dbus_message_iter_init_append(msg, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &bytes,
+									len);
+	dbus_message_iter_close_container(&iter, &array);
+}
+
+static DBusMessage *create_gatt_dbus_error(DBusMessage *msg, uint8_t att_ecode)
+{
+	switch (att_ecode) {
+	case BT_ATT_ERROR_READ_NOT_PERMITTED:
+		return btd_error_not_permitted(msg, "Read not permitted");
+	case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
+		return btd_error_not_permitted(msg, "Write not permitted");
+	case BT_ATT_ERROR_AUTHENTICATION:
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
+	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+		return btd_error_not_permitted(msg, "Not paired");
+	case BT_ATT_ERROR_INVALID_OFFSET:
+		return btd_error_invalid_args_str(msg, "Invalid offset");
+	case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
+		return btd_error_invalid_args_str(msg, "Invalid Length");
+	case BT_ATT_ERROR_AUTHORIZATION:
+		return btd_error_not_authorized(msg);
+	case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
+		return btd_error_not_supported(msg);
+	case 0:
+		return btd_error_failed(msg, "Operation failed");
+	default:
+		return g_dbus_create_error(msg, ERROR_INTERFACE,
+				"Operation failed with ATT error: 0x%02x",
+				att_ecode);
+	}
+
+	return NULL;
+}
+
+static void desc_read_long_cb(bool success, uint8_t att_ecode,
+					const uint8_t *value, uint16_t length,
+					void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	struct descriptor *desc = op->data;
+	DBusMessage *reply;
+
+	desc->in_read = false;
+
+	if (!success) {
+		reply = create_gatt_dbus_error(op->msg, att_ecode);
+		goto done;
+	}
+
+	update_value_property(value, length, &desc->value, &desc->value_len,
+						&desc->value_known, desc->path,
+						GATT_DESCRIPTOR_IFACE);
+
+	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+	if (!reply) {
+		error("Failed to allocate D-Bus message reply");
+		return;
+	}
+
+	message_append_byte_array(reply, value, length);
+
+done:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
 static DBusMessage *descriptor_read_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
-	/* TODO: Implement */
-	return btd_error_failed(msg, "Not implemented");
+	struct descriptor *desc = user_data;
+	struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+	struct async_dbus_op *op;
+
+	if (desc->in_read)
+		return btd_error_in_progress(msg);
+
+	op = new0(struct async_dbus_op, 1);
+	if (!op)
+		return btd_error_failed(msg, "Failed to initialize request");
+
+	op->msg = dbus_message_ref(msg);
+	op->data = desc;
+
+	if (bt_gatt_client_read_long_value(gatt, desc->handle, 0,
+							desc_read_long_cb, op,
+							async_dbus_op_free)) {
+		desc->in_read = true;
+		return NULL;
+	}
+
+	async_dbus_op_free(op);
+
+	return btd_error_failed(msg, "Failed to send read request");
 }
 
 static DBusMessage *descriptor_write_value(DBusConnection *conn,
@@ -147,7 +331,7 @@ static const GDBusPropertyTable descriptor_properties[] = {
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
-	{ "Value", "ay", descriptor_get_value, NULL, NULL,
+	{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
 					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
@@ -167,6 +351,7 @@ static void descriptor_free(void *data)
 {
 	struct descriptor *desc = data;
 
+	free(desc->value);
 	g_free(desc->path);
 	free(desc);
 }
@@ -321,137 +506,32 @@ static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
-struct chrc_dbus_op {
-	struct characteristic *chrc;
-	DBusMessage *msg;
-};
-
-static void chrc_dbus_op_free(void *data)
-{
-	struct chrc_dbus_op *op = data;
-
-	dbus_message_unref(op->msg);
-	free(op);
-}
-
-static bool chrc_resize_value(struct characteristic *chrc, size_t new_size)
-{
-	uint8_t *ptr;
-
-	if (chrc->value_len == new_size)
-		return true;
-
-	if (!new_size) {
-		free(chrc->value);
-		chrc->value = NULL;
-		chrc->value_len = 0;
-
-		return true;
-	}
-
-	ptr = realloc(chrc->value, sizeof(uint8_t) * new_size);
-	if (!ptr)
-		return false;
-
-	chrc->value = ptr;
-	chrc->value_len = new_size;
-
-	return true;
-}
-
-
-static DBusMessage *create_gatt_dbus_error(DBusMessage *msg, uint8_t att_ecode)
-{
-	switch (att_ecode) {
-	case BT_ATT_ERROR_READ_NOT_PERMITTED:
-		return btd_error_not_permitted(msg, "Read not permitted");
-	case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
-		return btd_error_not_permitted(msg, "Write not permitted");
-	case BT_ATT_ERROR_AUTHENTICATION:
-	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
-	case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
-		return btd_error_not_permitted(msg, "Not paired");
-	case BT_ATT_ERROR_INVALID_OFFSET:
-		return btd_error_invalid_args_str(msg, "Invalid offset");
-	case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
-		return btd_error_invalid_args_str(msg, "Invalid Length");
-	case BT_ATT_ERROR_AUTHORIZATION:
-		return btd_error_not_authorized(msg);
-	case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
-		return btd_error_not_supported(msg);
-	case 0:
-		return btd_error_failed(msg, "Operation failed");
-	default:
-		return g_dbus_create_error(msg, ERROR_INTERFACE,
-				"Operation failed with ATT error: 0x%02x",
-				att_ecode);
-	}
-
-	return NULL;
-}
-
 static void chrc_read_long_cb(bool success, uint8_t att_ecode,
 					const uint8_t *value, uint16_t length,
 					void *user_data)
 {
-	struct chrc_dbus_op *op = user_data;
-	struct characteristic *chrc = op->chrc;
-	DBusMessageIter iter, array;
+	struct async_dbus_op *op = user_data;
+	struct characteristic *chrc = op->data;
 	DBusMessage *reply;
-	int i;
 
-	op->chrc->in_read = false;
+	chrc->in_read = false;
 
 	if (!success) {
 		reply = create_gatt_dbus_error(op->msg, att_ecode);
 		goto done;
 	}
 
-	/*
-	 * If the value is the same, then only update it if it wasn't previously
-	 * known.
-	 */
-	if (chrc->value_known && chrc->value_len == length &&
-			!memcmp(chrc->value, value, sizeof(uint8_t) * length))
-		goto reply;
-
-	if (!chrc_resize_value(chrc, length)) {
-		/*
-		 * Failed to resize the buffer. Since we don't want to show a
-		 * stale value, if the value was previously known, then free and
-		 * hide it.
-		 */
-		free(chrc->value);
-		chrc->value = NULL;
-		chrc->value_len = 0;
-		chrc->value_known = false;
-
-		goto changed_signal;
-	}
-
-	chrc->value_known = true;
-	memcpy(chrc->value, value, sizeof(uint8_t) * length);
-
-changed_signal:
-	g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
-						GATT_CHARACTERISTIC_IFACE,
-						"Value");
+	update_value_property(value, length, &chrc->value, &chrc->value_len,
+						&chrc->value_known, chrc->path,
+						GATT_CHARACTERISTIC_IFACE);
 
-reply:
 	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
 	if (!reply) {
 		error("Failed to allocate D-Bus message reply");
 		return;
 	}
 
-	dbus_message_iter_init_append(reply, &iter);
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
-
-	for (i = 0; i < length; i++)
-		dbus_message_iter_append_basic(&array, DBUS_TYPE_BYTE,
-								value + i);
-
-	dbus_message_iter_close_container(&iter, &array);
+	message_append_byte_array(reply, value, length);
 
 done:
 	g_dbus_send_message(btd_get_dbus_connection(), reply);
@@ -462,26 +542,26 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn,
 {
 	struct characteristic *chrc = user_data;
 	struct bt_gatt_client *gatt = chrc->service->client->gatt;
-	struct chrc_dbus_op *op;
+	struct async_dbus_op *op;
 
 	if (chrc->in_read)
 		return btd_error_in_progress(msg);
 
-	op = new0(struct chrc_dbus_op, 1);
+	op = new0(struct async_dbus_op, 1);
 	if (!op)
 		return btd_error_failed(msg, "Failed to initialize request");
 
-	op->chrc = chrc;
 	op->msg = dbus_message_ref(msg);
+	op->data = chrc;
 
 	if (bt_gatt_client_read_long_value(gatt, chrc->value_handle, 0,
 							chrc_read_long_cb, op,
-							chrc_dbus_op_free)) {
+							async_dbus_op_free)) {
 		chrc->in_read = true;
 		return NULL;
 	}
 
-	chrc_dbus_op_free(op);
+	async_dbus_op_free(op);
 
 	return btd_error_failed(msg, "Failed to send read request");
 }