public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [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