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
char *path;
bool in_read;
+ bool in_write;
struct queue *descs;
};
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;
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)
{
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,