From da92020b1f19fdc5d6c63b27241546f6ae32afbb Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Fri, 19 Dec 2014 13:35:59 -0800 Subject: [PATCH] core/gatt: Implement GattCharacteristic1.WriteValue This patch implements the WriteValue method of org.bluez.GattCharacteristic1. The GATT write procedure that should be used is decided based on characteristic properties. --- src/gatt-client.c | 179 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/src/gatt-client.c b/src/gatt-client.c index 1ff14dc4e..9e4a8ef48 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, -- 2.47.3