client/btpclient: Add GATT read and write value supports
PATCH 1/1 client/btpclient: Add GATT read and write value supports
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(+)
client/btpclient/gatt.c +285 -0
@@ -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;
src/shared/btp.h +35 -0
@@ -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 0x12struct 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);
Signature · 1 line
2.43.0
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
This patch was applied to bluetooth/bluez.git (master) by Luiz Augusto von Dentz <[email protected]>:
Hello:
This patch was applied to bluetooth/bluez.git (master) by Luiz Augusto von Dentz <[email protected]>:
Quoted 8 lines · On Fri, 3 Apr 2026 16:36:31 +0200 you wrote:
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.
[...]
Here is the summary with links:
- [BlueZ] client/btpclient: Add GATT read and write value supports
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=15080642a414
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html