Diff between bfee50821910c4cb4cb36fe2f9047f268a893d3a and da92020b1f19fdc5d6c63b27241546f6ae32afbb

Changed Files

File Additions Deletions Status
src/gatt-client.c +177 -2 modified

Full Patch

diff --git a/src/gatt-client.c b/src/gatt-client.c
index 1ff14dc..9e4a8ef 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -76,6 +76,7 @@ struct characteristic {
 	char *path;
 
 	bool in_read;
+	bool in_write;
 
 	struct queue *descs;
 };
@@ -168,6 +169,35 @@ static gboolean descriptor_value_exists(const GDBusPropertyTable *property,
 	return ret;
 }
 
+static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
+							size_t *value_len)
+{
+	DBusMessageIter iter, array;
+	uint8_t *val;
+	int len;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return false;
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &val, &len);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
+		return false;
+
+	if (len < 0)
+		return false;
+
+	*value = val;
+	*value_len = len;
+
+	return true;
+}
+
 struct async_dbus_op {
 	DBusMessage *msg;
 	void *data;
@@ -294,6 +324,95 @@ static DBusMessage *descriptor_read_value(DBusConnection *conn,
 	return btd_error_failed(msg, "Failed to send read request");
 }
 
+static void write_result_cb(bool success, bool reliable_error,
+					uint8_t att_ecode, void *user_data)
+{
+	struct async_dbus_op *op = user_data;
+	DBusMessage *reply;
+
+	if (op->data) {
+		struct characteristic *chrc;
+
+		chrc = op->data;
+		chrc->in_write = false;
+	}
+
+	if (!success) {
+		if (reliable_error)
+			reply = btd_error_failed(op->msg,
+						"Reliable write failed");
+		else
+			reply = create_gatt_dbus_error(op->msg, att_ecode);
+
+		goto done;
+	}
+
+	reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+	if (!reply) {
+		error("Failed to allocate D-Bus message reply");
+		return;
+	}
+
+done:
+	g_dbus_send_message(btd_get_dbus_connection(), reply);
+}
+
+
+static void write_cb(bool success, uint8_t att_ecode, void *user_data)
+{
+	write_result_cb(success, false, att_ecode, user_data);
+}
+
+
+static bool 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)
+{
+	struct async_dbus_op *op;
+
+	op = new0(struct async_dbus_op, 1);
+	if (!op)
+		return false;
+
+	op->msg = dbus_message_ref(msg);
+	op->data = data;
+
+	if (bt_gatt_client_write_long_value(gatt, reliable, handle,
+							0, value, value_len,
+							write_result_cb, op,
+							async_dbus_op_free))
+		return true;
+
+	async_dbus_op_free(op);
+
+	return false;
+}
+
+static bool start_write_request(DBusMessage *msg, uint16_t handle,
+					struct bt_gatt_client *gatt,
+					const uint8_t *value, size_t value_len,
+					void *data)
+{
+	struct async_dbus_op *op;
+
+	op = new0(struct async_dbus_op, 1);
+	if (!op)
+		return false;
+
+	op->msg = dbus_message_ref(msg);
+	op->data = data;
+
+	if (bt_gatt_client_write_value(gatt, handle, value, value_len,
+							write_cb, op,
+							async_dbus_op_free))
+		return true;
+
+	async_dbus_op_free(op);
+
+	return false;
+}
+
 static DBusMessage *descriptor_write_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
@@ -553,8 +672,64 @@ static DBusMessage *characteristic_read_value(DBusConnection *conn,
 static DBusMessage *characteristic_write_value(DBusConnection *conn,
 					DBusMessage *msg, void *user_data)
 {
-	/* TODO: Implement */
-	return btd_error_failed(msg, "Not implemented");
+	struct characteristic *chrc = user_data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	uint8_t *value = NULL;
+	size_t value_len = 0;
+
+	if (chrc->in_write)
+		return btd_error_in_progress(msg);
+
+	if (!parse_value_arg(msg, &value, &value_len))
+		return btd_error_invalid_args(msg);
+
+	if (!(chrc->props & (BT_GATT_CHRC_PROP_WRITE |
+					BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)))
+		return btd_error_not_supported(msg);
+
+	/*
+	 * Decide which write to use based on characteristic properties. For now
+	 * we don't perform signed writes since gatt-client doesn't support them
+	 * and the user can always encrypt the through pairing. The procedure to
+	 * use is determined based on the following priority:
+	 *
+	 *   * "reliable-write" property set -> reliable long-write.
+	 *   * "write" property set -> write request.
+	 *     - If value is larger than MTU - 3: long-write
+	 *   * "write-without-response" property set -> write command.
+	 */
+	if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
+		uint16_t mtu;
+		bool result;
+
+		mtu = bt_gatt_client_get_mtu(gatt);
+		if (!mtu)
+			return btd_error_failed(msg, "No ATT transport");
+
+		if (value_len <= (unsigned) mtu - 3)
+			result = start_write_request(msg, chrc->value_handle,
+							gatt, value,
+							value_len, chrc);
+		else
+			result = start_long_write(msg, chrc->value_handle, gatt,
+						false, value, value_len, chrc);
+
+		if (result)
+			goto done_async;
+	}
+
+	if ((chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) &&
+			bt_gatt_client_write_without_response(gatt,
+							chrc->value_handle,
+							false, value,
+							value_len))
+		return dbus_message_new_method_return(msg);
+
+	return btd_error_failed(msg, "Failed to initiate write");
+
+done_async:
+	chrc->in_write = true;
+	return NULL;
 }
 
 static DBusMessage *characteristic_start_notify(DBusConnection *conn,