Hi Amit, On Fri, Jun 5, 2020 at 7:30 AM Amitsi5x <amitx.k.singh@xxxxxxxxx> wrote: > > From: amit <amitx.k.singh@xxxxxxxxx> > > Changes made to add support for read charateristic value > by uuid in bluetoothctl. > > Signed-off-by: amit <amitx.k.singh@xxxxxxxxx> > --- > client/gatt.c | 70 ++++++++++++++++++++++++++++++++ > client/gatt.h | 1 + > client/main.c | 18 +++++++++ > src/gatt-client.c | 70 ++++++++++++++++++++++++++++++++ > src/shared/gatt-client.c | 86 +++++++++++++++++++++++++++++++++++++++- > src/shared/gatt-client.h | 5 +++ > 6 files changed, 249 insertions(+), 1 deletion(-) > > diff --git a/client/gatt.c b/client/gatt.c > index 53f875050..8c2844ed6 100644 > --- a/client/gatt.c > +++ b/client/gatt.c > @@ -681,6 +681,76 @@ void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]) > return bt_shell_noninteractive_quit(EXIT_FAILURE); > } > > +static void charreadbyuuid_reply(DBusMessage *message, void *user_data) > +{ > + DBusError error; > + DBusMessageIter iter, array; > + uint8_t *value; > + int len; > + > + dbus_error_init(&error); > + > + if (dbus_set_error_from_message(&error, message) == TRUE) { > + bt_shell_printf("Failed to read: %s\n", error.name); > + dbus_error_free(&error); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + dbus_message_iter_init(message, &iter); > + > + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { > + bt_shell_printf("Invalid response to read\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + dbus_message_iter_recurse(&iter, &array); > + dbus_message_iter_get_fixed_array(&array, &value, &len); > + > + if (len < 0) { > + bt_shell_printf("Unable to parse value\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + bt_shell_hexdump(value, len); > + > + return bt_shell_noninteractive_quit(EXIT_SUCCESS); > +} > + > +static void charreadbyuuid_setup(DBusMessageIter *iter, void *user_data) > +{ > + char *uuid = user_data; > + > + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); > +} > + > +static void charreadbyuuid_attribute(GDBusProxy *proxy, char *uuid) > +{ > + if (g_dbus_proxy_method_call(proxy, "CharReadByUUID", charreadbyuuid_setup, charreadbyuuid_reply, > + uuid, NULL) == FALSE) { > + bt_shell_printf("Failed to set uuid\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + bt_shell_printf("Attempting to read service handle %s\n", g_dbus_proxy_get_path(proxy)); > +} > + > +void gatt_charreadbyuuid_attribute(GDBusProxy *proxy, int argc, char *argv[]) > +{ > + const char *iface; > + > + iface = g_dbus_proxy_get_interface(proxy); > + > + if (!strcmp(iface, "org.bluez.GattCharacteristic1")) { > + charreadbyuuid_attribute(proxy, argv[1]); > + return; > + } > + > + bt_shell_printf("Unable to read attribute %s\n", > + g_dbus_proxy_get_path(proxy)); > + > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > +} > + > static void charbyuuid_reply(DBusMessage *message, void *user_data) > { > DBusError error; > diff --git a/client/gatt.h b/client/gatt.h > index 692fb5758..8f96d8665 100644 > --- a/client/gatt.h > +++ b/client/gatt.h > @@ -35,6 +35,7 @@ GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *path); > char *gatt_attribute_generator(const char *text, int state); > void gatt_servbyuuid_attribute(GDBusProxy *proxy, int argc, char *argv[]); > void gatt_charbyuuid_attribute(GDBusProxy *proxy, int argc, char *argv[]); > +void gatt_charreadbyuuid_attribute(GDBusProxy *proxy, int argc, char *argv[]); > void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]); > void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[]); > void gatt_notify_attribute(GDBusProxy *proxy, bool enable); > diff --git a/client/main.c b/client/main.c > index 10e64e17b..4dd1e593a 100644 > --- a/client/main.c > +++ b/client/main.c > @@ -2071,6 +2071,22 @@ static void cmd_attribute_info(int argc, char *argv[]) > return bt_shell_noninteractive_quit(EXIT_SUCCESS); > } > > +static void cmd_char_read_by_uuid(int argc, char *argv[]) > +{ > + GDBusProxy *proxy; > + > + proxy = find_attribute(argc, argv); > + > + set_default_attribute(proxy); > + > + if (!default_attr) { > + bt_shell_printf("No attribute selected\n"); > + return bt_shell_noninteractive_quit(EXIT_FAILURE); > + } > + > + gatt_charreadbyuuid_attribute(default_attr, argc, argv); > +} > + > static void cmd_char_by_uuid(int argc, char *argv[]) > { > GDBusProxy *proxy; > @@ -2718,6 +2734,8 @@ static const struct bt_shell_menu gatt_menu = { > "Discover Primary Services by UUID" }, > { "char-by-uuid", "[UUID]", cmd_char_by_uuid, > "Discover Characteristic Services by UUID" }, > + { "char-read-by-uuid", "[UUID]", cmd_char_read_by_uuid, > + "Read Characteristic by UUID" }, > { "select-attribute", "<attribute/UUID>", cmd_select_attribute, > "Select attribute", attribute_generator }, > { "attribute-info", "[attribute/UUID]", cmd_attribute_info, > diff --git a/src/gatt-client.c b/src/gatt-client.c > index da811ea4f..cd6d6dfde 100644 > --- a/src/gatt-client.c > +++ b/src/gatt-client.c > @@ -444,6 +444,27 @@ static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data) > return op; > } > > +static struct async_dbus_op *fetch_char_read_by_uuid(struct bt_gatt_client *gatt, > + DBusMessage *msg, > + char *uuid, > + bt_gatt_client_char_by_uuid_callback_t callback, > + void *data) > +{ > + struct async_dbus_op *op; > + > + op = async_dbus_op_new(msg, data); > + op->id = bt_gatt_client_char_read_by_uuid(gatt, uuid, callback, > + async_dbus_op_ref(op), > + async_dbus_op_unref); > + > + if (op->id) > + return op; > + > + async_dbus_op_free(op); > + > + return NULL; > +} > + > static struct async_dbus_op *fetch_char_by_uuid(struct bt_gatt_client *gatt, > DBusMessage *msg, > char *uuid, > @@ -972,6 +993,52 @@ fail: > chrc->read_op = NULL; > } > > +static void char_read_by_uuid_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 characteristic *opchar = op->data; > + > + if (!success) > + goto fail; > + > + async_dbus_op_reply(op, att_ecode, value, length); > + > + return; > + > +fail: > + async_dbus_op_reply(op, att_ecode, NULL, 0); > + > + opchar->type_op = NULL; > +} > + > +static DBusMessage *char_read_by_uuid(DBusConnection *conn, > + DBusMessage *msg, void *user_data) > +{ > + struct characteristic *chardata = user_data; > + struct bt_gatt_client *gatt = chardata->service->client->gatt; > + DBusMessageIter iter; > + > + char *uuid = 0; > + > + if (!gatt) > + return btd_error_failed(msg, "Not connected"); > + > + dbus_message_iter_init(msg, &iter); > + > + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) > + dbus_message_iter_get_basic(&iter,&uuid); > + else > + return NULL; > + > + chardata->type_op = fetch_char_read_by_uuid(gatt, msg,uuid, char_read_by_uuid_cb, chardata); > + > + if (!chardata->type_op) > + return btd_error_failed(msg, "Failed to send read request"); > + > + return NULL; > +} > + > static void characteristic_by_uuid_cb(bool success, uint8_t att_ecode, const uint8_t *value, > uint16_t length, void *user_data) > { > @@ -1786,6 +1853,9 @@ static const GDBusMethodTable characteristic_methods[] = { > { GDBUS_ASYNC_METHOD("CharByUUID", GDBUS_ARGS({ "options", "s" }), > GDBUS_ARGS({ "value", "ay" }), > chardiscover_by_uuid) }, > + { GDBUS_ASYNC_METHOD("CharReadByUUID", GDBUS_ARGS({ "options", "s" }), > + GDBUS_ARGS({ "value", "ay" }), > + char_read_by_uuid) }, It would have helped if you had communicated that you would be looking into add support for this type of operation, these procedures obviously cannot be part of Characteristic interface since that is only available after the discovery procedure then you can just lookup internally in the cache. So this type of procedure will probably need to be under a Device object and lets please agree on the documentation first before we move forward. > { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), > GDBUS_ARGS({ "value", "ay" }), > characteristic_read_value) }, > diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c > index 8a696c77f..7c9d25ec3 100644 > --- a/src/shared/gatt-client.c > +++ b/src/shared/gatt-client.c > @@ -2725,6 +2725,90 @@ done: > op->callback(success, att_ecode, value, length, op->user_data); > } > > +unsigned int bt_gatt_client_char_read_by_uuid(struct bt_gatt_client *client, > + char *uuid, > + bt_gatt_client_char_by_uuid_callback_t callback, > + void *user_data, > + bt_gatt_client_destroy_func_t destroy) > +{ > + struct request *req; > + struct char_by_uuid_op *op; > + unsigned char *pdu; > + uint16_t len ; > + uint16_t start_handle = 0x0001; > + uint16_t end_handle = 0xffff; > + bt_uuid_t btuuid; > + uint8_t uuid128[16]; > + > + /* Length of pdu will be vary according to uuid type > + for 2 byte uuid total length is 8 (start handle(2) + end handle(2) + uuid(2)) > + for 16 byte uuid total length is 22 (start handle(2) + end handle(2) + uuid(16)) > + */ > + uint16_t pdu_len_16bit_uuid = 6; > + uint16_t pdu_len_128bit_uuid = 20; > + > + if (bt_string_to_uuid(&btuuid, uuid) < 0) { > + return 0; > + } > + > + if (btuuid.type == BT_UUID16){ > + pdu = (unsigned char *) malloc(pdu_len_16bit_uuid); > + len = pdu_len_16bit_uuid; > + } else { > + pdu = (unsigned char *) malloc(pdu_len_128bit_uuid); > + len = pdu_len_128bit_uuid; > + } > + > + if (!client) > + return 0; > + > + op = new0(struct char_by_uuid_op, 1); > + req = request_create(client); > + if (!req) { > + free(op); > + return 0; > + } > + if (!client) > + return 0; > + > + op = new0(struct char_by_uuid_op, 1); > + req = request_create(client); > + > + if (!req) { > + free(op); > + return 0; > + } > + > + op->callback = callback; > + op->user_data = user_data; > + op->destroy = destroy; > + req->data = op; > + req->destroy = destroy_char_by_uuid_op; > + > + put_le16(start_handle, pdu); > + put_le16(end_handle, pdu+2); > + > + if (btuuid.type == BT_UUID16) > + put_le16(btuuid.value.u16, pdu+4); > + else { > + bswap_128(&btuuid.value.u128.data[0], &uuid128[0]); > + memcpy(pdu + 4, uuid128, 16); > + } > + > + req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, > + pdu, len, > + char_by_uuid_cb, req, > + request_unref); > + > + if (!req->att_id) { > + op->destroy = NULL; > + request_unref(req); > + return 0; > + } > + > + return req->id; > +} > + > unsigned int bt_gatt_client_char_by_uuid(struct bt_gatt_client *client, > char *uuid, > bt_gatt_client_char_by_uuid_callback_t callback, > @@ -2754,7 +2838,7 @@ unsigned int bt_gatt_client_char_by_uuid(struct bt_gatt_client *client, > if (btuuid.type == BT_UUID16){ > pdu = (unsigned char *) malloc(pdu_len_16bit_uuid); > len = pdu_len_16bit_uuid; > - } else { > + }else { > pdu = (unsigned char *) malloc(pdu_len_128bit_uuid); > len = pdu_len_128bit_uuid; > } > diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h > index f5d5169ce..50859ce52 100644 > --- a/src/shared/gatt-client.h > +++ b/src/shared/gatt-client.h > @@ -97,6 +97,11 @@ unsigned int bt_gatt_client_char_by_uuid(struct bt_gatt_client *client, > bt_gatt_client_read_callback_t callback, > void *user_data, > bt_gatt_client_destroy_func_t destroy); > +unsigned int bt_gatt_client_char_read_by_uuid(struct bt_gatt_client *client, > + char *uuid, > + bt_gatt_client_read_callback_t callback, > + void *user_data, > + bt_gatt_client_destroy_func_t destroy); > unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client, > uint16_t value_handle, > bt_gatt_client_read_callback_t callback, > -- > 2.17.1 > -- Luiz Augusto von Dentz