* [PATCH BlueZ] client/btpclient: Add GATT read and write value supports
@ 2026-04-03 14:36 Frédéric Danis
2026-04-03 15:46 ` [BlueZ] " bluez.test.bot
0 siblings, 1 reply; 2+ messages in thread
From: Frédéric Danis @ 2026-04-03 14:36 UTC (permalink / raw)
To: linux-bluetooth
This stores the characteristics proxy and handle so we can call
org.bluez.GattCharacteristic1 ReadValue and WriteValue methods with
the "handle provided by PTS - 1".
This will allow to pass BAP/CL/CGGIT/CHA/BV-02-C and
BAP/UCL/CGGIT/CHA/BV-03-C.
---
client/btpclient/gatt.c | 285 ++++++++++++++++++++++++++++++++++++++++
src/shared/btp.h | 35 +++++
2 files changed, 320 insertions(+)
diff --git a/client/btpclient/gatt.c b/client/btpclient/gatt.c
index c6bb254b4..4a28a7b27 100644
--- a/client/btpclient/gatt.c
+++ b/client/btpclient/gatt.c
@@ -53,7 +53,10 @@ static void btp_gatt_read_commands(uint8_t index, const void *param,
}
commands |= (1 << BTP_OP_GATT_READ_SUPPORTED_COMMANDS);
+ commands |= (1 << BTP_OP_GATT_READ);
commands |= (1 << BTP_OP_GATT_READ_UUID);
+ commands |= (1 << BTP_OP_GATT_WRITE_WITHOUT_RSP);
+ commands |= (1 << BTP_OP_GATT_WRITE);
commands = L_CPU_TO_LE16(commands);
@@ -68,6 +71,13 @@ static bool match_attribute_uuid(const void *attr, const void *uuid)
return !bt_uuid_cmp(&attribute->uuid, uuid);
}
+static bool match_attribute_handle(const void *attr, const void *handle)
+{
+ const struct gatt_attribute *attribute = attr;
+
+ return attribute->handle == L_PTR_TO_UINT(handle);
+}
+
static void gatt_read_setup(struct l_dbus_message *message,
void *user_data)
{
@@ -82,6 +92,93 @@ static void gatt_read_setup(struct l_dbus_message *message,
l_dbus_message_builder_destroy(builder);
}
+static void gatt_read_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct btp_adapter *adapter = user_data;
+ struct btp_gatt_read_rp *rp;
+ struct l_dbus_message_iter iter;
+ uint8_t *data;
+ uint32_t n;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to read value (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GATT_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ if (!l_dbus_message_get_arguments(result, "ay", &iter))
+ goto failed;
+
+ if (!l_dbus_message_iter_get_fixed_array(&iter, &data, &n)) {
+ l_debug("Cannot read value");
+ goto failed;
+ }
+
+ rp = malloc(sizeof(struct btp_gatt_read_rp) + n);
+ rp->response = 0;
+ rp->len = n;
+ memcpy(rp->data, data, n);
+
+ btp_send(btp, BTP_GATT_SERVICE, BTP_OP_GATT_READ, adapter->index,
+ sizeof(struct btp_gatt_read_rp) + n, rp);
+
+ free(rp);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GATT_SERVICE, adapter->index, BTP_ERROR_FAIL);
+}
+
+static void btp_gatt_read(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct btp_device *device;
+ const struct btp_gatt_read_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ bool prop;
+ struct gatt_attribute *characteristic;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to read Handle */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b",
+ &prop) || !prop) {
+ goto failed;
+ }
+
+ device = find_device_by_address(adapter, &cp->address,
+ cp->address_type);
+ if (!device)
+ goto failed;
+
+ characteristic = l_queue_find(device->characteristics,
+ match_attribute_handle,
+ L_UINT_TO_PTR(cp->handle - 1));
+ if (!characteristic)
+ goto failed;
+
+ l_dbus_proxy_method_call(characteristic->proxy, "ReadValue",
+ gatt_read_setup, gatt_read_reply,
+ adapter, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GATT_SERVICE, index, status);
+}
+
static void gatt_read_uuid_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *result,
void *user_data)
@@ -187,6 +284,185 @@ failed:
btp_send_error(btp, BTP_GATT_SERVICE, index, status);
}
+struct gatt_write_req {
+ struct btp_adapter *adapter;
+ const struct btp_gatt_write_cp *cp;
+ const char *type;
+};
+
+static void gatt_write_setup(struct l_dbus_message *message,
+ void *user_data)
+{
+ struct gatt_write_req *req = user_data;
+ struct l_dbus_message_builder *builder;
+ uint16_t i;
+
+ builder = l_dbus_message_builder_new(message);
+ l_dbus_message_builder_enter_array(builder, "y");
+ for (i = 0; i < req->cp->len; i++)
+ l_dbus_message_builder_append_basic(builder, 'y',
+ &(req->cp->data[i]));
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ if (req->type) {
+ l_dbus_message_builder_append_basic(builder, 's', "type");
+ l_dbus_message_builder_enter_variant(builder, "s");
+ l_dbus_message_builder_append_basic(builder, 's', req->type);
+ l_dbus_message_builder_leave_variant(builder);
+ }
+ l_dbus_message_builder_leave_dict(builder);
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void gatt_write_without_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct gatt_write_req *req = user_data;
+ struct btp_adapter *adapter = req->adapter;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to write value (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GATT_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ btp_send(btp, BTP_GATT_SERVICE, BTP_OP_GATT_WRITE_WITHOUT_RSP,
+ adapter->index, 0, NULL);
+}
+
+static void btp_gatt_write_without_rsp(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct btp_device *device;
+ const struct btp_gatt_write_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ bool prop;
+ struct gatt_attribute *characteristic;
+ struct gatt_write_req *req;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to write to Handle */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b",
+ &prop) || !prop) {
+ goto failed;
+ }
+
+ device = find_device_by_address(adapter, &cp->address,
+ cp->address_type);
+ if (!device)
+ goto failed;
+
+ characteristic = l_queue_find(device->characteristics,
+ match_attribute_handle,
+ L_UINT_TO_PTR(cp->handle - 1));
+ if (!characteristic)
+ goto failed;
+
+ req = l_new(struct gatt_write_req, 1);
+ req->adapter = adapter;
+ req->cp = cp;
+ req->type = "command";
+
+ l_dbus_proxy_method_call(characteristic->proxy, "WriteValue",
+ gatt_write_setup,
+ gatt_write_without_reply,
+ req, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GATT_SERVICE, index, status);
+}
+
+static void gatt_write_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *result,
+ void *user_data)
+{
+ struct gatt_write_req *req = user_data;
+ struct btp_adapter *adapter = req->adapter;
+ struct btp_gatt_write_rp *rp;
+
+ if (l_dbus_message_is_error(result)) {
+ const char *name, *desc;
+
+ l_dbus_message_get_error(result, &name, &desc);
+ l_error("Failed to write value (%s), %s", name, desc);
+
+ btp_send_error(btp, BTP_GATT_SERVICE, adapter->index,
+ BTP_ERROR_FAIL);
+ return;
+ }
+
+ rp = l_new(struct btp_gatt_write_rp, 1);
+ rp->att_response = 0;
+
+ btp_send(btp, BTP_GATT_SERVICE, BTP_OP_GATT_WRITE, adapter->index,
+ sizeof(struct btp_gatt_write_rp), rp);
+
+ free(rp);
+}
+
+static void btp_gatt_write(uint8_t index, const void *param,
+ uint16_t length, void *user_data)
+{
+ struct btp_adapter *adapter = find_adapter_by_index(index);
+ struct btp_device *device;
+ const struct btp_gatt_write_cp *cp = param;
+ uint8_t status = BTP_ERROR_FAIL;
+ bool prop;
+ struct gatt_attribute *characteristic;
+ struct gatt_write_req *req;
+
+ if (!adapter) {
+ status = BTP_ERROR_INVALID_INDEX;
+ goto failed;
+ }
+
+ /* Adapter needs to be powered to be able to write to Handle */
+ if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b",
+ &prop) || !prop) {
+ goto failed;
+ }
+
+ device = find_device_by_address(adapter, &cp->address,
+ cp->address_type);
+ if (!device)
+ goto failed;
+
+ characteristic = l_queue_find(device->characteristics,
+ match_attribute_handle,
+ L_UINT_TO_PTR(cp->handle - 1));
+ if (!characteristic)
+ goto failed;
+
+ req = l_new(struct gatt_write_req, 1);
+ req->adapter = adapter;
+ req->cp = cp;
+
+ l_dbus_proxy_method_call(characteristic->proxy, "WriteValue",
+ gatt_write_setup, gatt_write_reply,
+ req, NULL);
+
+ return;
+
+failed:
+ btp_send_error(btp, BTP_GATT_SERVICE, index, status);
+}
+
bool gatt_register_service(struct btp *btp_, struct l_dbus *dbus_,
struct l_dbus_client *client)
{
@@ -195,9 +471,18 @@ bool gatt_register_service(struct btp *btp_, struct l_dbus *dbus_,
btp_register(btp, BTP_GATT_SERVICE, BTP_OP_GATT_READ_SUPPORTED_COMMANDS,
btp_gatt_read_commands, NULL, NULL);
+ btp_register(btp, BTP_GATT_SERVICE, BTP_OP_GATT_READ,
+ btp_gatt_read, NULL, NULL);
+
btp_register(btp, BTP_GATT_SERVICE, BTP_OP_GATT_READ_UUID,
btp_gatt_read_uuid, NULL, NULL);
+ btp_register(btp, BTP_GATT_SERVICE, BTP_OP_GATT_WRITE_WITHOUT_RSP,
+ btp_gatt_write_without_rsp, NULL, NULL);
+
+ btp_register(btp, BTP_GATT_SERVICE, BTP_OP_GATT_WRITE,
+ btp_gatt_write, NULL, NULL);
+
gatt_service_registered = true;
return true;
diff --git a/src/shared/btp.h b/src/shared/btp.h
index bdb5ad33e..8b5078f82 100644
--- a/src/shared/btp.h
+++ b/src/shared/btp.h
@@ -293,6 +293,19 @@ struct btp_gatt_char_value {
#define BTP_OP_GATT_READ_SUPPORTED_COMMANDS 0x01
+#define BTP_OP_GATT_READ 0x11
+struct btp_gatt_read_cp {
+ uint8_t address_type;
+ bdaddr_t address;
+ uint16_t handle;
+} __packed;
+
+struct btp_gatt_read_rp {
+ uint8_t response;
+ uint16_t len;
+ uint8_t data[];
+} __packed;
+
#define BTP_OP_GATT_READ_UUID 0x12
struct btp_gatt_read_uuid_cp {
uint8_t address_type;
@@ -309,6 +322,28 @@ struct btp_gatt_read_uuid_rp {
struct btp_gatt_char_value values[];
} __packed;
+#define BTP_OP_GATT_WRITE_WITHOUT_RSP 0x15
+struct btp_gatt_write_without_rsp_cp {
+ uint8_t address_type;
+ bdaddr_t address;
+ uint16_t handle;
+ uint16_t len;
+ uint8_t data[];
+} __packed;
+
+#define BTP_OP_GATT_WRITE 0x17
+struct btp_gatt_write_cp {
+ uint8_t address_type;
+ bdaddr_t address;
+ uint16_t handle;
+ uint16_t len;
+ uint8_t data[];
+} __packed;
+
+struct btp_gatt_write_rp {
+ uint8_t att_response;
+} __packed;
+
struct btp;
typedef void (*btp_destroy_func_t)(void *user_data);
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* RE: [BlueZ] client/btpclient: Add GATT read and write value supports
2026-04-03 14:36 [PATCH BlueZ] client/btpclient: Add GATT read and write value supports Frédéric Danis
@ 2026-04-03 15:46 ` bluez.test.bot
0 siblings, 0 replies; 2+ messages in thread
From: bluez.test.bot @ 2026-04-03 15:46 UTC (permalink / raw)
To: linux-bluetooth, frederic.danis
[-- Attachment #1: Type: text/plain, Size: 1525 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1077051
---Test result---
Test Summary:
CheckPatch PENDING 0.32 seconds
GitLint PENDING 0.28 seconds
BuildEll PASS 20.44 seconds
BluezMake PASS 655.61 seconds
MakeCheck PASS 18.65 seconds
MakeDistcheck PASS 246.54 seconds
CheckValgrind PASS 297.48 seconds
CheckSmatch WARNING 354.92 seconds
bluezmakeextell PASS 188.74 seconds
IncrementalBuild PENDING 0.31 seconds
ScanBuild PASS 1047.20 seconds
Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:
##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
client/btpclient/gatt.c: note: in included file:./src/shared/btp.h:322:42: warning: array of flexible structures
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:
https://github.com/bluez/bluez/pull/2012/checks
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-03 15:46 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-03 14:36 [PATCH BlueZ] client/btpclient: Add GATT read and write value supports Frédéric Danis
2026-04-03 15:46 ` [BlueZ] " bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox