public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
From: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
To: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: linux-bluetooth@vger.kernel.org, quic_mohamull@quicinc.com,
	quic_hbandi@quicinc.com, quic_anubhavg@quicinc.com,
	prathibha.madugonde@oss.qualcomm.com
Subject: Re: [PATCH BlueZ v4 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role
Date: Wed, 15 Apr 2026 20:19:26 +0530	[thread overview]
Message-ID: <91d278eb-a78c-4185-9660-493920f358fe@oss.qualcomm.com> (raw)
In-Reply-To: <CABBYNZJoF1X0Rs+CV441to=xv8--C2XOwkBSa6MigfF-+q86Mg@mail.gmail.com>

Hi Luiz,

Thank you for the review comments.

On 4/13/2026 7:38 PM, Luiz Augusto von Dentz wrote:
> Hi Naga,
> 
> On Mon, Apr 13, 2026 at 6:41 AM Naga Bhavani Akella
> <naga.akella@oss.qualcomm.com> wrote:
>>
>> Open RAW HCI Channel for CS Event Handling
>> Parse the following HCI LE CS Events in reflector role
>> and route the events to RAP Profile.
>>  1. HCI_EVT_LE_CS_READ_RMT_SUPP_CAP_COMPLETE
>>  2. HCI_EVT_LE_CS_CONFIG_COMPLETE
>>  3. HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE
>>  4. HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE
>>  5. HCI_EVT_LE_CS_SUBEVENT_RESULT
>>  6. HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE
>> Send HCI_OP_LE_CS_SET_DEFAULT_SETTINGS to the controller
>> with default settings selected by the user.
>> Map connection handle received to device connection
>> ---
>>  Makefile.plugins           |    3 +-
>>  profiles/ranging/rap.c     |   83 ++-
>>  profiles/ranging/rap_hci.c | 1288 ++++++++++++++++++++++++++++++++++++
>>  3 files changed, 1367 insertions(+), 7 deletions(-)
>>  create mode 100644 profiles/ranging/rap_hci.c
>>
>> diff --git a/Makefile.plugins b/Makefile.plugins
>> index c9efadb45..ac667beda 100644
>> --- a/Makefile.plugins
>> +++ b/Makefile.plugins
>> @@ -89,7 +89,8 @@ builtin_modules += battery
>>  builtin_sources += profiles/battery/battery.c
>>
>>  builtin_modules += rap
>> -builtin_sources += profiles/ranging/rap.c
>> +builtin_sources += profiles/ranging/rap.c \
>> +               profiles/ranging/rap_hci.c
>>
>>  if SIXAXIS
>>  builtin_modules += sixaxis
>> diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
>> index f03454c72..63682e318 100644
>> --- a/profiles/ranging/rap.c
>> +++ b/profiles/ranging/rap.c
>> @@ -17,6 +17,7 @@
>>  #include "gdbus/gdbus.h"
>>
>>  #include "bluetooth/bluetooth.h"
>> +#include "bluetooth/l2cap.h"
>>  #include "bluetooth/uuid.h"
>>
>>  #include "src/plugin.h"
>> @@ -34,12 +35,17 @@
>>  #include "src/shared/rap.h"
>>  #include "attrib/att.h"
>>  #include "src/log.h"
>> +#include "src/btd.h"
>>
>> +#define USE_BT_HCI_RAW_CHANNEL 1
> 
> Didn't I already comment that we shouldn't use the likes of if
> USE_BT_HCI_RAW_CHANNEL?
> 
Addressed in v7.
>>  struct rap_data {
>>         struct btd_device *device;
>>         struct btd_service *service;
>>         struct bt_rap *rap;
>>         unsigned int ready_id;
>> +#if USE_BT_HCI_RAW_CHANNEL
>> +       struct bt_hci *hci;
>> +#endif
>>  };
>>
>>  static struct queue *sessions;
>> @@ -61,10 +67,10 @@ static void rap_debug(const char *str, void *user_data)
>>
>>  static void rap_data_add(struct rap_data *data)
>>  {
>> -       DBG("%p", data);
>> +       DBG("%p", (void *)data);
>>
>>         if (queue_find(sessions, NULL, data)) {
>> -               error("data %p already added", data);
>> +               error("data %p already added", (void *)data);
> 
> No idea why you are casting to void * on %p?
> 
Addressed in v7.
>>                 return;
>>         }
>>
>> @@ -95,13 +101,21 @@ static void rap_data_free(struct rap_data *data)
>>         }
>>
>>         bt_rap_ready_unregister(data->rap, data->ready_id);
>> +#if USE_BT_HCI_RAW_CHANNEL
>> +       if (data->hci) {
>> +               bt_rap_hci_sm_cleanup();
>> +               bt_hci_unref(data->hci);
>> +       }
>> +#endif
>> +       /* Clean up HCI connection mappings */
>> +       bt_rap_detach_hci(data->rap);
>>         bt_rap_unref(data->rap);
>>         free(data);
>>  }
>>
>>  static void rap_data_remove(struct rap_data *data)
>>  {
>> -       DBG("%p", data);
>> +       DBG("%p", (void *)data);
>>
>>         if (!queue_remove(sessions, data))
>>                 return;
>> @@ -118,7 +132,7 @@ static void rap_detached(struct bt_rap *rap, void *user_data)
>>  {
>>         struct rap_data *data;
>>
>> -       DBG("%p", rap);
>> +       DBG("%p", (void *)rap);
>>
>>         data = queue_find(sessions, match_data, rap);
>>         if (!data) {
>> @@ -131,7 +145,7 @@ static void rap_detached(struct bt_rap *rap, void *user_data)
>>
>>  static void rap_ready(struct bt_rap *rap, void *user_data)
>>  {
>> -       DBG("%p", rap);
>> +       DBG("%p", (void *)rap);
>>  }
>>
>>  static void rap_attached(struct bt_rap *rap, void *user_data)
>> @@ -140,7 +154,7 @@ static void rap_attached(struct bt_rap *rap, void *user_data)
>>         struct bt_att *att;
>>         struct btd_device *device;
>>
>> -       DBG("%p", rap);
>> +       DBG("%p", (void *)rap);
>>
>>         data = queue_find(sessions, match_data, rap);
>>         if (data) {
>> @@ -194,6 +208,22 @@ static int rap_probe(struct btd_service *service)
>>                 free(data);
>>                 return -EINVAL;
>>         }
>> +#if USE_BT_HCI_RAW_CHANNEL
>> +       int16_t hci_index = btd_adapter_get_index(adapter);
>> +
>> +       data->hci = bt_hci_new_raw_device(hci_index);
>> +       if (bt_rap_attach_hci(data->rap, data->hci)) {
>> +               DBG("HCI raw channel initialized, hci%d", hci_index);
>> +               bt_rap_hci_set_le_bcs_options(
>> +                                       btd_opts.defaults.bcs.role,
>> +                                       btd_opts.defaults.bcs.cs_sync_ant_sel,
>> +                                       btd_opts.defaults.bcs.max_tx_power);
> 
> BCS? Id called just bt_rap_set_options
> 
Addressed in v7.
>> +       } else {
>> +               error("HCI raw channel not available (may be in use)");
>> +       }
>> +#else /* USE_BT_HCI_RAW_CHANNEL */
>> +       DBG("MGMT Events");
>> +#endif /* USE_BT_HCI_RAW_CHANNEL */
>>
>>         rap_data_add(data);
>>
>> @@ -228,6 +258,10 @@ static int rap_accept(struct btd_service *service)
>>         struct btd_device *device = btd_service_get_device(service);
>>         struct bt_gatt_client *client = btd_device_get_gatt_client(device);
>>         struct rap_data *data = btd_service_get_user_data(service);
>> +       struct bt_att *att;
>> +       const bdaddr_t *bdaddr;
>> +       uint8_t bdaddr_type;
>> +       uint16_t handle;
>>         char addr[18];
>>
>>         ba2str(device_get_address(device), addr);
>> @@ -243,6 +277,43 @@ static int rap_accept(struct btd_service *service)
>>                 return -EINVAL;
>>         }
>>
>> +       /* Set up connection handle mapping for CS event routing */
>> +       att = bt_rap_get_att(data->rap);
>> +       bdaddr = device_get_address(device);
>> +       bdaddr_type = device_get_le_address_type(device);
>> +
>> +       if (att && data->hci) {
>> +               /* Use bt_hci_get_conn_info to find the connection handle
>> +                * by iterating through all connections and matching bdaddr
>> +                */
>> +               struct bt_hci_conn_info conn_info;
>> +               bool found = false;
>> +
>> +               /* Try handles from 0x0001 to 0x0EFF
>> +                * (valid LE connection handle range)
>> +                */
>> +               for (handle = 0x0001; handle <= 0x0EFF; handle++) {
>> +                       if (bt_hci_get_conn_info(data->hci, handle,
>> +                               &conn_info)) {
>> +                               /* Check if bdaddr matches */
>> +                               if (memcmp(conn_info.bdaddr, bdaddr, 6) == 0) {
>> +                                       found = true;
>> +                                       DBG("Found conn handle 0x%04X", handle);
>> +                                       break;
>> +                               }
>> +                       }
>> +               }
> 
> Please no, we should use something like HCIGETCONNLIST, not mindlessly
> iterate through all handles to check the connection handle.
> 
Addressed in v7.
>> +
>> +               if (found) {
>> +                       DBG("Setting up handle mapping: handle=0x%04X", handle);
>> +                       bt_rap_set_conn_handle(data->rap, handle,
>> +                                               (const uint8_t *)bdaddr,
>> +                                               bdaddr_type);
>> +               } else {
>> +                       error("Failed to find connection handle for device");
>> +               }
>> +       }
>> +
>>         btd_service_connecting_complete(service, 0);
>>
>>         return 0;
>> diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
>> new file mode 100644
>> index 000000000..b00719ae2
>> --- /dev/null
>> +++ b/profiles/ranging/rap_hci.c
>> @@ -0,0 +1,1288 @@
>> +// SPDX-License-Identifier: LGPL-2.1-or-later
>> +/*
>> + *  BlueZ - Bluetooth protocol stack for Linux
>> + *
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + */
>> +
>> +#ifdef HAVE_CONFIG_H
>> +#include <config.h>
>> +#endif
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdbool.h>
>> +#include <stddef.h>
>> +#include <unistd.h>
>> +#include <string.h>
>> +#include <endian.h>
>> +
>> +#include "lib/bluetooth/bluetooth.h"
>> +#include "src/shared/util.h"
>> +#include "src/shared/queue.h"
>> +#include "src/shared/rap.h"
>> +#include "src/log.h"
>> +#include "monitor/bt.h"
>> +
>> +#ifndef MIN
>> +#define MIN(a, b) ((a) < (b) ? (a) : (b))
>> +#endif
>> +
>> +/*  CS State Definitions */
>> +enum cs_state_t {
>> +       CS_INIT,
>> +       CS_STOPPED,
>> +       CS_STARTED,
>> +       CS_WAIT_CONFIG_CMPLT,
>> +       CS_WAIT_SEC_CMPLT,
>> +       CS_WAIT_PROC_CMPLT,
>> +       CS_HOLD,
>> +       CS_UNSPECIFIED
>> +};
>> +
>> +const char *state_names[] = {
>> +       "CS_INIT",
>> +       "CS_STOPPED",
>> +       "CS_STARTED",
>> +       "CS_WAIT_CONFIG_CMPLT",
>> +       "CS_WAIT_SEC_CMPLT",
>> +       "CS_WAIT_PROC_CMPLT",
>> +       "CS_HOLD",
>> +       "CS_UNSPECIFIED"
>> +};
>> +
>> +/* Callback Function Type */
>> +typedef void (*cs_callback_t)(uint16_t length,
>> +                       const void *param, void *user_data);
>> +
>> +/* State Machine Context */
>> +struct cs_state_machine_t {
>> +       enum cs_state_t current_state;
>> +       enum cs_state_t old_state;
>> +       struct bt_hci *hci;
>> +       struct bt_rap *rap;
>> +       unsigned int event_id;
>> +       bool initiator;
>> +       bool procedure_active;
>> +};
>> +
>> +struct cs_callback_map_t {
>> +       enum cs_state_t state;
>> +       cs_callback_t callback;
>> +};
>> +
>> +struct cs_callback_map_t cs_callback_map[] = {
>> +       { CS_WAIT_CONFIG_CMPLT, bt_rap_hci_cs_config_complete_callback },
>> +       { CS_WAIT_SEC_CMPLT, bt_rap_hci_cs_sec_enable_complete_callback },
>> +       { CS_WAIT_PROC_CMPLT, bt_rap_hci_cs_procedure_enable_complete_callback }
>> +};
>> +
>> +#define CS_CALLBACK_MAP_SIZE ARRAY_SIZE(cs_callback_map)
>> +
>> +struct bt_rap_hci_cs_options cs_opt;
>> +struct cs_state_machine_t *sm;
>> +
>> +/* Connection Handle Mapping */
>> +struct rap_conn_mapping {
>> +       uint16_t handle;
>> +       uint8_t bdaddr[6];
>> +       uint8_t bdaddr_type;
>> +       struct bt_att *att;
>> +       struct bt_rap *rap;
>> +};
>> +
>> +static struct queue *conn_mappings;
>> +
>> +/* Connection Mapping Helper Functions */
>> +static void mapping_free(void *data)
>> +{
>> +       struct rap_conn_mapping *mapping = data;
>> +
>> +       if (!mapping)
>> +               return;
>> +
>> +       free(mapping);
>> +}
>> +
>> +static bool match_mapping_handle(const void *a, const void *b)
>> +{
>> +       const struct rap_conn_mapping *mapping = a;
>> +       uint16_t handle = PTR_TO_UINT(b);
>> +
>> +       return mapping->handle == handle;
>> +}
>> +
>> +static bool match_mapping_rap(const void *a, const void *b)
>> +{
>> +       const struct rap_conn_mapping *mapping = a;
>> +       const struct bt_rap *rap = b;
>> +
>> +       return mapping->rap == rap;
>> +}
>> +
>> +static struct rap_conn_mapping *find_mapping_by_handle(uint16_t handle)
>> +{
>> +       if (!conn_mappings)
>> +               return NULL;
>> +
>> +       return queue_find(conn_mappings, match_mapping_handle,
>> +                               UINT_TO_PTR(handle));
>> +}
>> +
>> +static bool add_conn_mapping(uint16_t handle, const uint8_t *bdaddr,
>> +                               uint8_t bdaddr_type, struct bt_att *att,
>> +                               struct bt_rap *rap)
>> +{
>> +       struct rap_conn_mapping *mapping;
>> +
>> +       if (!conn_mappings) {
>> +               conn_mappings = queue_new();
>> +               if (!conn_mappings)
>> +                       return false;
>> +       }
>> +
>> +       /* Check if mapping already exists */
>> +       mapping = find_mapping_by_handle(handle);
>> +       if (mapping) {
>> +               /* Update existing mapping */
>> +               if (bdaddr)
>> +                       memcpy(mapping->bdaddr, bdaddr, 6);
>> +               mapping->bdaddr_type = bdaddr_type;
>> +               mapping->att = att;
>> +               mapping->rap = rap;
>> +               return true;
>> +       }
>> +
>> +       /* Create new mapping */
>> +       mapping = new0(struct rap_conn_mapping, 1);
>> +       if (!mapping)
>> +               return false;
>> +
>> +       mapping->handle = handle;
>> +       if (bdaddr)
>> +               memcpy(mapping->bdaddr, bdaddr, 6);
>> +       mapping->bdaddr_type = bdaddr_type;
>> +       mapping->att = att;
>> +       mapping->rap = rap;
>> +
>> +       return queue_push_tail(conn_mappings, mapping);
>> +}
>> +
>> +static void remove_conn_mapping(uint16_t handle)
>> +{
>> +       struct rap_conn_mapping *mapping;
>> +
>> +       if (!conn_mappings)
>> +               return;
>> +
>> +       mapping = queue_remove_if(conn_mappings, match_mapping_handle,
>> +                                       UINT_TO_PTR(handle));
>> +       if (mapping)
>> +               mapping_free(mapping);
>> +}
>> +
>> +static void remove_rap_mappings(struct bt_rap *rap)
>> +{
>> +       if (!conn_mappings)
>> +               return;
>> +
>> +       queue_remove_all(conn_mappings, match_mapping_rap, rap,
>> +                               mapping_free);
>> +}
>> +
>> +static struct bt_rap *resolve_handle_to_rap(uint16_t handle,
>> +                                               struct bt_hci *hci)
>> +{
>> +       struct rap_conn_mapping *mapping;
>> +       struct bt_hci_conn_info conn_info;
>> +
>> +       /* First try to find in mapping cache */
>> +       mapping = find_mapping_by_handle(handle);
>> +       if (mapping && mapping->rap) {
>> +               DBG("Found handle 0x%04X in mapping cache", handle);
>> +               return mapping->rap;
>> +       }
>> +
>> +       /* Fallback: Try to get connection info via ioctl */
>> +       if (hci && bt_hci_get_conn_info(hci, handle, &conn_info)) {
>> +               DBG("Got connection info via ioctl for handle 0x%04X:", handle);
>> +               DBG("  bdaddr=%02x:%02x:%02x:%02x:%02x:%02x link_type=0x%02x",
>> +                       conn_info.bdaddr[5], conn_info.bdaddr[4],
>> +                       conn_info.bdaddr[3], conn_info.bdaddr[2],
>> +                       conn_info.bdaddr[1], conn_info.bdaddr[0],
>> +                       conn_info.type);
>> +               DBG("  Note: Cannot determine RAP instance from ioctl alone");
> 
> Not sure what this is for? If it cannot be resolved with just the
> handle then why are you doing it?
> 
Addressed in v7.
>> +       }
>> +
>> +       /* Profile layer should have called bt_rap_set_conn_handle() during
>> +        * connection establishment. If we reach here, the mapping was not set.
>> +        */
>> +       DBG("No mapping found for handle 0x%04X", handle);
>> +       DBG("Profile layer should call bt_rap_set_conn_handle() on connect");
>> +
>> +       return NULL;
>> +}
>> +
>> +/*  State Machine Functions */
>> +void cs_state_machine_init(struct cs_state_machine_t *sm, struct bt_rap *rap,
>> +                               struct bt_hci *hci)
>> +{
>> +       if (!sm)
>> +               return;
>> +
>> +       memset(sm, 0, sizeof(struct cs_state_machine_t));
>> +       sm->current_state = CS_UNSPECIFIED;
>> +       sm->rap = rap;
>> +       sm->hci = hci;
>> +       sm->initiator = false;
>> +       sm->procedure_active = false;
>> +}
>> +
>> +void bt_rap_hci_sm_cleanup(void)
>> +{
>> +       if (!sm)
>> +               return;
>> +
>> +       if (sm->event_id)
>> +               bt_hci_unregister(sm->hci, sm->event_id);
>> +
>> +       sm->current_state = CS_UNSPECIFIED;
>> +       sm->rap = NULL;
>> +       sm->hci = NULL;
>> +       sm->procedure_active = false;
>> +
>> +       free(sm);
>> +}
>> +
>> +void bt_rap_hci_set_le_bcs_options(uint8_t role, uint8_t cs_sync_ant_sel,
>> +                                       int8_t max_tx_power)
>> +{
>> +       cs_opt.role = role;
>> +       cs_opt.cs_sync_ant_sel = cs_sync_ant_sel;
>> +       cs_opt.max_tx_power = max_tx_power;
>> +}
>> +
>> +/* State Transition Logic */
>> +void cs_set_state(struct cs_state_machine_t *sm, enum cs_state_t new_state)
>> +{
>> +       if (!sm)
>> +               return;
>> +
>> +       if (sm->current_state == new_state)
>> +               return;
>> +
>> +       /* Validate state values before array access */
>> +       if (sm->current_state > CS_UNSPECIFIED || new_state > CS_UNSPECIFIED) {
>> +               DBG("[ERROR] Invalid state transition attempted\n");
> 
> There is a error function to print errors.
> 
Addressed in v7.
>> +               return;
>> +       }
>> +
>> +       DBG("[STATE] Transition: %s → %s\n",
>> +               state_names[sm->current_state],
>> +               state_names[new_state]);
>> +
>> +       sm->old_state = sm->current_state;
>> +       sm->current_state = new_state;
>> +}
>> +
>> +enum cs_state_t cs_get_current_state(struct cs_state_machine_t *sm)
>> +{
>> +       return sm ? sm->current_state : CS_UNSPECIFIED;
>> +}
>> +
>> +bool cs_is_procedure_active(const struct cs_state_machine_t *sm)
>> +{
>> +       return sm ? sm->procedure_active : false;
>> +}
>> +
>> +/* HCI Event Callbacks */
>> +static void rap_def_settings_done_cb(const void *data, uint8_t size,
>> +                                       void *user_data)
>> +{
>> +       struct bt_hci_rsp_le_cs_set_def_settings *rp;
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_rsp_le_cs_set_def_settings))
>> +               return;
>> +
>> +       DBG("[EVENT] CS default Setting Complete (size=0x%02X)\n", size);
>> +
>> +       rp = (struct bt_hci_rsp_le_cs_set_def_settings *)data;
>> +
>> +       if (cs_get_current_state(sm) != CS_INIT) {
>> +               DBG("Event received in Wrong State!! Expected : CS_INIT");
>> +               return;
>> +       }
>> +
>> +       if (rp->status == 0) {
>> +               /* Success - proceed to configuration */
>> +               cs_set_state(sm, CS_WAIT_CONFIG_CMPLT);
>> +
>> +               /* Reflector role */
>> +               DBG("Waiting for CS Config Completed event...\n");
>> +               /* TODO: Initiator role - Send CS Config complete cmd */
>> +       } else {
>> +               /* Error - transition to stopped */
>> +               DBG("[ERROR]CS Set default setting failed with status 0x%02X\n",
>> +               rp->status);
> 
> User error
> 
Addressed in v7.
>> +               cs_set_state(sm, CS_STOPPED);
>> +       }
>> +}
>> +
>> +void rap_send_hci_def_settings_command(struct cs_state_machine_t *sm,
>> +               struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *ev)
>> +{
>> +       struct bt_hci_cmd_le_cs_set_def_settings cp;
>> +       unsigned int status;
>> +
>> +       memset(&cp, 0, sizeof(cp));
>> +
>> +       if (ev->handle)
>> +               cp.handle = ev->handle;
>> +       cp.role_enable = cs_opt.role;
>> +       cp.cs_sync_antenna_selection = cs_opt.cs_sync_ant_sel;
>> +       cp.max_tx_power = cs_opt.max_tx_power;
>> +
>> +       if (!sm || !sm->hci) {
>> +               DBG("[ERR] Set Def Settings: sm or hci is null");
>> +               return;
>> +       }
>> +
>> +       status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_DEF_SETTINGS,
>> +                               &cp, sizeof(cp), rap_def_settings_done_cb,
>> +                               sm, NULL);
>> +
>> +       DBG("sending set default settings case, status : %d", status);
>> +       if (!status)
>> +               DBG("Failed to send default settings cmd");
>> +}
>> +
>> +static void rap_rd_rmt_supp_cap_cmplt_evt(const uint8_t *data, uint8_t size,
>> +                                          void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *evt;
>> +       struct bt_rap *rap;
>> +       struct iovec iov;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       /* Pull the entire structure at once */
>> +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
>> +       if (!evt) {
>> +               DBG("[ERROR] Failed to pull remote cap complete struct\n");
>> +               return;
>> +       }
>> +
>> +       DBG("[EVENT] Remote Capabilities Complete\n");
>> +       DBG("status=0x%02X, handle=0x%04X\n", evt->status, evt->handle);
>> +
>> +       /* Check status */
>> +       if (evt->status != 0) {
>> +               DBG("[ERROR] Remote capabilities failed with status 0x%02X\n",
>> +                       evt->status);
>> +               cs_set_state(sm, CS_STOPPED);
>> +               return;
>> +       }
>> +
>> +       /* Resolve handle to RAP instance */
>> +       rap = resolve_handle_to_rap(evt->handle, sm->hci);
>> +       if (!rap) {
>> +               DBG("[WARN] Could not resolve handle 0x%04X to RAP instance\n",
>> +                       evt->handle);
>> +               /* Continue with state machine RAP for now */
>> +               rap = sm->rap;
>> +       }
>> +
>> +       DBG("[EVENT] Remote Capabilities: num_config=%u, ",
>> +               evt->num_config_supported);
>> +       DBG("max_consecutive_proc=%u, num_antennas=%u, ",
>> +               evt->max_consecutive_procedures_supported,
>> +               evt->num_antennas_supported);
>> +       DBG("max_antenna_paths=%u, roles=0x%02X, modes=0x%02X\n",
>> +               evt->max_antenna_paths_supported,
>> +               evt->roles_supported,
>> +               evt->modes_supported);
>> +
>> +       rap_send_hci_def_settings_command(sm,
>> +               (struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *)evt);
>> +       cs_set_state(sm, CS_INIT);
>> +}
>> +
>> +static void rap_cs_config_cmplt_evt(const uint8_t *data, uint8_t size,
>> +                                   void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       const struct bt_hci_evt_le_cs_config_complete *evt;
>> +       struct rap_ev_cs_config_cmplt rap_ev;
>> +       struct iovec iov;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_config_complete))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       DBG("[EVENT] Configuration Complete (size=0x%02X)\n", size);
>> +
>> +       /* State Check */
>> +       if (cs_get_current_state(sm) != CS_WAIT_CONFIG_CMPLT) {
>> +               DBG("Event received in Wrong State!! ");
>> +               DBG("Expected : CS_WAIT_CONFIG_CMPLT");
>> +               return;
>> +       }
>> +
>> +       /* Pull the entire structure at once */
>> +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
>> +       if (!evt) {
>> +               DBG("[ERROR] Failed to pull config complete struct\n");
>> +               return;
>> +       }
>> +
>> +       DBG("status=0x%02X, handle=0x%04X\n", evt->status, evt->handle);
>> +
>> +       /* Check status */
>> +       if (evt->status != 0) {
>> +               DBG("[ERROR] Configuration failed with status 0x%02X\n",
>> +                       evt->status);
>> +               cs_set_state(sm, CS_STOPPED);
>> +               return;
>> +       }
>> +
>> +       /* Copy fields to rap_ev structure */
>> +       rap_ev.status = evt->status;
>> +       rap_ev.conn_hdl = cpu_to_le16(evt->handle);
>> +       rap_ev.config_id = evt->config_id;
>> +       rap_ev.action = evt->action;
>> +       rap_ev.main_mode_type = evt->main_mode_type;
>> +       rap_ev.sub_mode_type = evt->sub_mode_type;
>> +       rap_ev.min_main_mode_steps = evt->min_main_mode_steps;
>> +       rap_ev.max_main_mode_steps = evt->max_main_mode_steps;
>> +       rap_ev.main_mode_rep = evt->main_mode_repetition;
>> +       rap_ev.mode_0_steps = evt->mode_0_steps;
>> +       rap_ev.role = evt->role;
>> +       rap_ev.rtt_type = evt->rtt_type;
>> +       rap_ev.cs_sync_phy = evt->cs_sync_phy;
>> +       memcpy(rap_ev.channel_map, evt->channel_map, 10);
>> +       rap_ev.channel_map_rep = evt->channel_map_repetition;
>> +       rap_ev.channel_sel_type = evt->channel_selection_type;
>> +       rap_ev.ch3c_shape = evt->ch3c_shape;
>> +       rap_ev.ch3c_jump = evt->ch3c_jump;
>> +       rap_ev.reserved = evt->reserved;
>> +       rap_ev.t_ip1_time = evt->t_ip1_time;
>> +       rap_ev.t_ip2_time = evt->t_ip2_time;
>> +       rap_ev.t_fcs_time = evt->t_fcs_time;
>> +       rap_ev.t_pm_time = evt->t_pm_time;
>> +
>> +       /* Store rtt_type in global options */
>> +       cs_opt.rtt_type = rap_ev.rtt_type;
>> +
>> +       DBG("[EVENT] Config Complete: config_id=%u, action=%u, ",
>> +               rap_ev.config_id, rap_ev.action);
>> +       DBG("main_mode=%u, sub_mode=%u, role=%u, rtt_type=%u\n",
>> +               rap_ev.main_mode_type, rap_ev.sub_mode_type,
>> +               rap_ev.role, rap_ev.rtt_type);
>> +
>> +       /* Success - proceed to Security enable complete */
>> +       cs_set_state(sm, CS_WAIT_SEC_CMPLT);
>> +
>> +       /* Reflector role */
>> +       DBG("Waiting for security enable event...\n");
>> +       /* TODO: Initiator role - Send CS Security enable cmd */
>> +
>> +       /* Send Callback to RAP Profile */
>> +       for (size_t i = 0; i < CS_CALLBACK_MAP_SIZE; i++) {
>> +               if (cs_callback_map[i].state == sm->old_state) {
>> +                       cs_callback_map[i].callback(size, &rap_ev, sm->rap);
>> +                       return;
>> +               }
>> +       }
>> +}
>> +
>> +static void rap_cs_sec_enable_cmplt_evt(const uint8_t *data, uint8_t size,
>> +                                        void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       struct rap_ev_cs_sec_enable_cmplt rap_ev;
>> +       struct iovec iov;
>> +       uint8_t status;
>> +       uint16_t handle;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_sec_enable_complete))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       DBG("[EVENT] Security Enable Complete (size=0x%02X)\n", size);
>> +
>> +       /* State Check */
>> +       if (cs_get_current_state(sm) != CS_WAIT_SEC_CMPLT) {
>> +               DBG("Event received in Wrong State!! ");
>> +               DBG("Expected : CS_WAIT_SEC_CMPLT");
>> +               return;
>> +       }
>> +
>> +       /* Parse all fields in order using iovec */
>> +       if (!util_iov_pull_u8(&iov, &status)) {
>> +               DBG("[ERROR] Failed to parse Status\n");
>> +               return;
>> +       }
>> +
>> +       if (!util_iov_pull_le16(&iov, &handle)) {
>> +               DBG("[ERROR] Failed to parse Connection_Handle\n");
>> +               return;
>> +       }
>> +
>> +       rap_ev.status = status;
>> +       rap_ev.conn_hdl = cpu_to_le16(handle);
>> +
>> +       DBG("[EVENT] Security Enable: status=0x%02X, handle=0x%04X\n",
>> +               rap_ev.status, handle);
>> +
>> +       if (rap_ev.status == 0) {
>> +               /* Success - proceed to configuration */
>> +               cs_set_state(sm, CS_WAIT_PROC_CMPLT);
>> +
>> +               /* Reflector role */
>> +               DBG("Waiting for CS Proc complete event...\n");
>> +               /* TODO: Initiator - Send CS Proc Set Parameter and enable */
>> +       } else {
>> +               /* Error - transition to stopped */
>> +               DBG("[ERROR] Security enable failed with status 0x%02X\n",
>> +                       rap_ev.status);
>> +               cs_set_state(sm, CS_STOPPED);
>> +       }
>> +
>> +       /* Send Callback to RAP Profile */
>> +       for (size_t i = 0; i < CS_CALLBACK_MAP_SIZE; i++) {
>> +               if (cs_callback_map[i].state == sm->old_state) {
>> +                       cs_callback_map[i].callback(size, &rap_ev, sm->rap);
>> +                       return;
>> +               }
>> +       }
>> +}
>> +
>> +static void rap_cs_proc_enable_cmplt_evt(const uint8_t *data, uint8_t size,
>> +                                         void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       const struct bt_hci_evt_le_cs_proc_enable_complete *evt;
>> +       struct rap_ev_cs_proc_enable_cmplt rap_ev;
>> +       struct iovec iov;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_proc_enable_complete))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       DBG("[EVENT] Procedure Enable Complete (size=0x%02X)\n", size);
> 
> DBG already prints the function name, so printing the event name again
> sounds repetitive.
> 
Addressed in v7.
>> +
>> +       /* State Check */
>> +       if (cs_get_current_state(sm) != CS_WAIT_PROC_CMPLT) {
>> +               DBG("Event received in Wrong State!! ");
>> +               DBG("Expected : CS_WAIT_PROC_CMPLT");
>> +               return;
>> +       }
>> +
>> +       /* Pull the entire structure at once */
>> +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
>> +       if (!evt) {
>> +               DBG("[ERROR] Failed to pull proc enable complete struct\n");
>> +               return;
>> +       }
>> +
>> +       DBG("status=0x%02X, handle=0x%04X\n", evt->status, evt->handle);
>> +
>> +       /* Check status */
>> +       if (evt->status != 0) {
>> +               DBG("[ERROR] Procedure enable failed with status 0x%02X\n",
>> +                       evt->status);
>> +               cs_set_state(sm, CS_STOPPED);
>> +               sm->procedure_active = false;
>> +               return;
>> +       }
>> +
>> +       /* Copy fields to rap_ev structure */
>> +       rap_ev.status = evt->status;
>> +       rap_ev.conn_hdl = cpu_to_le16(evt->handle);
>> +       rap_ev.config_id = evt->config_id;
>> +       rap_ev.state = evt->state;
>> +       rap_ev.tone_ant_config_sel = evt->tone_antenna_config_selection;
>> +       rap_ev.sel_tx_pwr = evt->selected_tx_power;
>> +       memcpy(rap_ev.sub_evt_len, evt->subevent_len, 3);
>> +       rap_ev.sub_evts_per_evt = evt->subevents_per_event;
>> +       rap_ev.sub_evt_intrvl = evt->subevent_interval;
>> +       rap_ev.evt_intrvl = evt->event_interval;
>> +       rap_ev.proc_intrvl = evt->procedure_interval;
>> +       rap_ev.proc_counter = evt->procedure_count;
>> +       rap_ev.max_proc_len = evt->max_procedure_len;
>> +
>> +       DBG("[EVENT] Procedure Enable: config_id=%u, state=%u, ",
>> +               rap_ev.config_id, rap_ev.state);
>> +       DBG("sub_evts_per_evt=%u, evt_intrvl=%u, proc_intrvl=%u\n",
>> +               rap_ev.sub_evts_per_evt, rap_ev.evt_intrvl,
>> +               rap_ev.proc_intrvl);
>> +
>> +       /* Success - procedure started */
>> +       cs_set_state(sm, CS_STARTED);
>> +       sm->procedure_active = true;
>> +
>> +       /* Send Callback to RAP Profile */
>> +       for (size_t i = 0; i < CS_CALLBACK_MAP_SIZE; i++) {
>> +               if (cs_callback_map[i].state == sm->old_state) {
>> +                       cs_callback_map[i].callback(size, &rap_ev, sm->rap);
>> +                       return;
>> +               }
>> +       }
>> +}
>> +
>> +static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
>> +                               int16_t *q_sample)
>> +{
>> +       uint8_t bytes[3];
>> +       uint32_t buffer;
>> +       uint32_t i12;
>> +       uint32_t q12;
>> +
>> +       /* Pull 3 bytes from iovec */
>> +       if (!util_iov_pull_u8(iov, &bytes[0]) ||
>> +               !util_iov_pull_u8(iov, &bytes[1]) ||
>> +               !util_iov_pull_u8(iov, &bytes[2])) {
>> +               *i_sample = 0;
>> +               *q_sample = 0;
>> +               return;
>> +       }
> 
> Oh please, use util_iov_pull_le24
> 
Addressed in v7.
>> +
>> +       /* Reconstruct 24-bit buffer from 3 bytes */
>> +       buffer = (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) |
>> +                               ((uint32_t)bytes[2] << 16);
> 
> No need to do this; just use util_iov_pull_le24
> 
Addressed in v7.
>> +       i12 =  buffer        & 0x0FFFU;   /* bits 0..11 */
>> +       q12 = (buffer >> 12) & 0x0FFFU;   /* bits 12..23 */
>>
>> +       /* Sign-extend 12-bit values to 16-bit */
>> +       *i_sample = (int16_t)((int32_t)(i12 << 20) >> 20);
>> +       *q_sample = (int16_t)((int32_t)(q12 << 20) >> 20);
> 
> This sound a little too complicated, please quote the spec why this
> needs to be done this way, also this should have a pack/unpack macro
> helps for bit manipulation.
> 
Addressed in v7.
Spec statements -
Tone PCT - Phase Correction Term (24 bits, including 12 LSBs to indicate the I sample
as a signed integer and 12 MSBs to indicate the Q sample as a signed
integer)

Packet PCT - Phase Correction Term (bits 0 to 11 are the I sample with type sint12, bits 12
to 23 are the Q sample with type sint12, and bits 24 to 31 are reserved for
future use)

>> +}
>> +
>> +/* Parse CS Mode 0 step data */
>> +static void parse_mode_zero_data(struct iovec *iov,
>> +                                struct cs_mode_zero_data *mode_data,
>> +                                uint8_t cs_role)
>> +{
>> +       uint32_t freq_offset;
>> +
>> +       if (iov->iov_len < 3) {
>> +               DBG("Mode 0: too short (<3)");
>> +               return;
>> +       }
>> +
>> +       util_iov_pull_u8(iov, &mode_data->packet_quality);
>> +       util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm);
>> +       util_iov_pull_u8(iov, &mode_data->packet_ant);
>> +       DBG("CS Step mode 0");
>> +
>> +       if (cs_role == CS_INITIATOR && iov->iov_len >= 4) {
>> +               util_iov_pull_le32(iov, &freq_offset);
>> +               mode_data->init_measured_freq_offset = freq_offset;
>> +       }
>> +}
>> +
>> +/* Parse CS Mode 1 step data */
>> +static void parse_mode_one_data(struct iovec *iov,
>> +                               struct cs_mode_one_data *mode_data,
>> +                               uint8_t cs_role, uint8_t cs_rtt_type)
>> +{
>> +       uint16_t time_val;
>> +
>> +       if (iov->iov_len < 4) {
>> +               DBG("Mode 1: too short (<4)");
>> +               return;
>> +       }
>> +
>> +       DBG("CS Step mode 1");
>> +       util_iov_pull_u8(iov, &mode_data->packet_quality);
>> +       util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm);
>> +       util_iov_pull_u8(iov, &mode_data->packet_ant);
>> +       util_iov_pull_u8(iov, &mode_data->packet_nadm);
>> +
>> +       if (iov->iov_len >= 2) {
>> +               util_iov_pull_le16(iov, &time_val);
>> +               if (cs_role == CS_REFLECTOR)
>> +                       mode_data->tod_toa_refl = time_val;
>> +               else
>> +                       mode_data->toa_tod_init = time_val;
>> +       }
>> +
>> +       if ((cs_rtt_type == 0x01 || cs_rtt_type == 0x02) &&
>> +               iov->iov_len >= 6) {
>> +               int16_t i_val, q_val;
>> +
>> +               parse_i_q_sample(iov, &i_val, &q_val);
>> +               mode_data->packet_pct1.i_sample = i_val;
>> +               mode_data->packet_pct1.q_sample = q_val;
>> +
>> +               parse_i_q_sample(iov, &i_val, &q_val);
>> +               mode_data->packet_pct2.i_sample = i_val;
>> +               mode_data->packet_pct2.q_sample = q_val;
>> +       }
>> +}
>> +
>> +/* Parse CS Mode 2 step data */
>> +static void parse_mode_two_data(struct iovec *iov,
>> +                               struct cs_mode_two_data *mode_data,
>> +                               uint8_t max_paths)
>> +{
>> +       uint8_t k;
>> +
>> +       if (iov->iov_len < 1) {
>> +               DBG("Mode 2: too short (<1)");
>> +               return;
>> +       }
>> +
>> +       util_iov_pull_u8(iov, &mode_data->ant_perm_index);
>> +       DBG("CS Step mode 2, max paths : %d", max_paths);
>> +
>> +       for (k = 0; k < max_paths; k++) {
>> +               int16_t i_val, q_val;
>> +
>> +               if (iov->iov_len < 4) {
>> +                       DBG("Mode 2: insufficient PCT for path %u (rem=%zu)",
>> +                               k, iov->iov_len);
>> +                       break;
>> +               }
>> +               parse_i_q_sample(iov, &i_val, &q_val);
>> +               mode_data->tone_pct[k].i_sample = i_val;
>> +               mode_data->tone_pct[k].q_sample = q_val;
>> +
>> +               util_iov_pull_u8(iov, &mode_data->tone_quality_indicator[k]);
>> +               DBG("tone_quality_indicator : %d",
>> +                       mode_data->tone_quality_indicator[k]);
>> +               DBG("[i, q] : %d, %d",
>> +                       mode_data->tone_pct[k].i_sample,
>> +                       mode_data->tone_pct[k].q_sample);
>> +       }
>> +}
>> +
>> +/* Parse CS Mode 3 step data */
>> +static void parse_mode_three_data(struct iovec *iov,
>> +                               struct cs_mode_three_data *mode_data,
>> +                               uint8_t cs_role, uint8_t cs_rtt_type,
>> +                               uint8_t max_paths)
>> +{
>> +       uint8_t k;
>> +       struct cs_mode_one_data *mode_one = &mode_data->mode_one_data;
>> +       struct cs_mode_two_data *mode_two = &mode_data->mode_two_data;
>> +
>> +       if (iov->iov_len < 4) {
>> +               DBG("Mode 3: mode1 too short (<4)");
>> +               return;
>> +       }
>> +
>> +       DBG("CS Step mode 3");
>> +
>> +       /* Parse Mode 1 portion */
>> +       parse_mode_one_data(iov, mode_one, cs_role, cs_rtt_type);
>> +
>> +       /* Parse Mode 2 portion */
>> +       if (iov->iov_len >= 1) {
>> +               util_iov_pull_u8(iov, &mode_two->ant_perm_index);
>> +               for (k = 0; k < max_paths; k++) {
>> +                       int16_t i_val, q_val;
>> +
>> +                       if (iov->iov_len < 4)
>> +                               break;
>> +                       parse_i_q_sample(iov, &i_val, &q_val);
>> +                       mode_two->tone_pct[k].i_sample = i_val;
>> +                       mode_two->tone_pct[k].q_sample = q_val;
>> +
>> +                       util_iov_pull_u8(iov,
>> +                                        &mode_two->tone_quality_indicator[k]);
>> +               }
>> +       }
>> +}
>> +
>> +/* Parse a single CS step */
>> +static void parse_cs_step(struct iovec *iov, struct cs_step_data *step,
>> +                       uint8_t cs_role, uint8_t cs_rtt_type,
>> +                       uint8_t max_paths)
>> +{
>> +       uint8_t mode;
>> +       uint8_t chnl;
>> +       uint8_t length;
>> +
>> +       /* Check if we have enough data for the 3-byte header */
>> +       if (iov->iov_len < 3) {
>> +               DBG("Truncated header for step");
>> +               return;
>> +       }
>> +
>> +       /* Read mode, channel, and length (3-byte header) */
>> +       if (!util_iov_pull_u8(iov, &mode) ||
>> +               !util_iov_pull_u8(iov, &chnl) ||
>> +               !util_iov_pull_u8(iov, &length)) {
>> +               DBG("Failed to read header for step");
>> +               return;
>> +       }
>> +
>> +       DBG("event->step_data_len : %d", length);
>> +
>> +       step->step_mode = mode;
>> +       step->step_chnl = chnl;
>> +       step->step_data_length = length;
>> +
>> +       DBG("Step: mode=%u chnl=%u data_len=%u", mode, chnl, length);
>> +
>> +       if (iov->iov_len < length) {
>> +               DBG("Truncated payload for step (need %u, have %zu)",
>> +                       length, iov->iov_len);
>> +               return;
>> +       }
>> +
>> +       /* Parse step data based on mode */
>> +       switch (mode) {
>> +       case CS_MODE_ZERO:
>> +               parse_mode_zero_data(iov, &step->step_mode_data.mode_zero_data,
>> +                                       cs_role);
>> +               break;
>> +       case CS_MODE_ONE:
>> +               parse_mode_one_data(iov, &step->step_mode_data.mode_one_data,
>> +                                       cs_role, cs_rtt_type);
>> +               break;
>> +       case CS_MODE_TWO:
>> +               parse_mode_two_data(iov, &step->step_mode_data.mode_two_data,
>> +                                       max_paths);
>> +               break;
>> +       case CS_MODE_THREE:
>> +               parse_mode_three_data(iov,
>> +                                       &step->step_mode_data.mode_three_data,
>> +                                       cs_role, cs_rtt_type, max_paths);
>> +               break;
>> +       default:
>> +               DBG("Unknown step mode %d", mode);
>> +               /* Skip the entire step data */
>> +               util_iov_pull(iov, length);
>> +               break;
>> +       }
>> +}
>> +
>> +static void rap_cs_subevt_result_evt(const uint8_t *data, uint8_t size,
>> +                               void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       struct rap_ev_cs_subevent_result *rap_ev;
>> +       struct iovec iov;
>> +       uint8_t cs_role;
>> +       uint8_t cs_rtt_type;
>> +       uint8_t max_paths;
>> +       uint8_t steps;
>> +       size_t send_len = 0;
>> +       uint16_t handle;
>> +       uint8_t config_id;
>> +       uint16_t start_acl_conn_evt_counter;
>> +       uint16_t proc_counter;
>> +       uint16_t freq_comp;
>> +       uint8_t ref_pwr_lvl;
>> +       uint8_t proc_done_status;
>> +       uint8_t subevt_done_status;
>> +       uint8_t abort_reason;
>> +       uint8_t num_ant_paths;
>> +       uint8_t num_steps_reported;
>> +       uint8_t i;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_subevent_result))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       /* Check if Procedure is active or not */
>> +       if (!sm->procedure_active) {
>> +               DBG("Received Subevent event when Procedure is inactive!");
>> +               return;
>> +       }
>> +
>> +       /* Parse header fields using iovec */
>> +       if (!util_iov_pull_le16(&iov, &handle)) {
>> +               DBG("[ERROR] Failed to parse Connection_Handle\n");
>> +               return;
>> +       }
>> +
>> +       if (!util_iov_pull_u8(&iov, &config_id) ||
>> +               !util_iov_pull_le16(&iov, &start_acl_conn_evt_counter) ||
>> +               !util_iov_pull_le16(&iov, &proc_counter) ||
>> +               !util_iov_pull_le16(&iov, &freq_comp) ||
>> +               !util_iov_pull_u8(&iov, &ref_pwr_lvl) ||
>> +               !util_iov_pull_u8(&iov, &proc_done_status) ||
>> +               !util_iov_pull_u8(&iov, &subevt_done_status) ||
>> +               !util_iov_pull_u8(&iov, &abort_reason) ||
>> +               !util_iov_pull_u8(&iov, &num_ant_paths) ||
>> +               !util_iov_pull_u8(&iov, &num_steps_reported)) {
>> +               DBG("[ERROR] Failed to parse subevent fields\n");
>> +               return;
>> +       }
>> +
>> +       cs_role = cs_opt.role;
>> +       cs_rtt_type = cs_opt.rtt_type;
>> +       max_paths = MIN((num_ant_paths + 1), CS_MAX_ANT_PATHS);
>> +       steps = MIN(num_steps_reported, CS_MAX_STEPS);
>> +       send_len = offsetof(struct rap_ev_cs_subevent_result, step_data) +
>> +                                       steps * sizeof(struct cs_step_data);
>> +       rap_ev = (struct rap_ev_cs_subevent_result *)malloc(send_len);
>> +       if (!rap_ev) {
>> +               DBG("[ERROR] Failed to allocate memory for subevent result\n");
>> +               return;
>> +       }
>> +
>> +       DBG("[EVENT] Subevent Result (length=%u)\n", size);
>> +       rap_ev->conn_hdl                     = le16_to_cpu(handle);
>> +       rap_ev->config_id                    = config_id;
>> +       rap_ev->start_acl_conn_evt_counter   = start_acl_conn_evt_counter;
>> +       rap_ev->proc_counter                 = proc_counter;
>> +       rap_ev->freq_comp                    = freq_comp;
>> +       rap_ev->ref_pwr_lvl                  = ref_pwr_lvl;
>> +       rap_ev->proc_done_status             = proc_done_status;
>> +       rap_ev->subevt_done_status           = subevt_done_status;
>> +       rap_ev->abort_reason                 = abort_reason;
>> +       rap_ev->num_ant_paths                = num_ant_paths;
>> +       rap_ev->num_steps_reported           = steps;
>> +
>> +       if (num_steps_reported > CS_MAX_STEPS) {
>> +               DBG("Too many steps reported: %u (max %u)",
>> +                       num_steps_reported, CS_MAX_STEPS);
>> +               goto send_event;
>> +       }
>> +
>> +       /* Early exit for error conditions */
>> +       if (rap_ev->subevt_done_status == 0xF ||
>> +           rap_ev->proc_done_status == 0xF) {
>> +               DBG("CS Procedure/Subevent aborted: ");
>> +               DBG("sub evt status = %d, proc status = %d, reason = %d",
>> +                       rap_ev->subevt_done_status, rap_ev->proc_done_status,
>> +                       rap_ev->abort_reason);
>> +               goto send_event;
>> +       }
>> +
>> +       /* Parse interleaved step data from remaining iovec data */
>> +       for (i = 0; i < steps; i++)
>> +               parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
>> +                       max_paths);
>> +
>> +send_event:
>> +       DBG("CS subevent result processed: %zu bytes, ", send_len);
>> +       bt_rap_hci_cs_subevent_result_callback(send_len, rap_ev, sm->rap);
>> +       free(rap_ev);
>> +}
>> +
>> +static void rap_cs_subevt_result_cont_evt(const uint8_t *data, uint8_t size,
>> +                                       void *user_data)
>> +{
>> +       struct cs_state_machine_t *sm = (struct cs_state_machine_t *)user_data;
>> +       struct rap_ev_cs_subevent_result_cont *rap_ev;
>> +       struct iovec iov;
>> +       uint8_t cs_role;
>> +       uint8_t cs_rtt_type;
>> +       uint8_t max_paths;
>> +       uint8_t steps;
>> +       size_t send_len = 0;
>> +       uint16_t handle;
>> +       uint8_t config_id;
>> +       uint8_t proc_done_status;
>> +       uint8_t subevt_done_status;
>> +       uint8_t abort_reason;
>> +       uint8_t num_ant_paths;
>> +       uint8_t num_steps_reported;
>> +       uint8_t i;
>> +
>> +       if (!sm || !data ||
>> +               size < sizeof(struct bt_hci_evt_le_cs_subevent_result_continue))
>> +               return;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       /* Check if Procedure is active or not */
>> +       if (!sm->procedure_active) {
>> +               DBG("Received Subevent when CS Procedure is inactive!");
>> +               return;
>> +       }
>> +
>> +       /* Parse header fields using iovec */
>> +       if (!util_iov_pull_le16(&iov, &handle)) {
>> +               DBG("[ERROR] Failed to parse Connection_Handle\n");
>> +               return;
>> +       }
>> +
>> +       if (!util_iov_pull_u8(&iov, &config_id) ||
>> +               !util_iov_pull_u8(&iov, &proc_done_status) ||
>> +               !util_iov_pull_u8(&iov, &subevt_done_status) ||
>> +               !util_iov_pull_u8(&iov, &abort_reason) ||
>> +               !util_iov_pull_u8(&iov, &num_ant_paths) ||
>> +               !util_iov_pull_u8(&iov, &num_steps_reported)) {
>> +               DBG("[ERROR] Failed to parse subevent continue fields ");
>> +               return;
>> +       }
>> +
>> +       cs_role = cs_opt.role;
>> +       cs_rtt_type = cs_opt.rtt_type;
>> +       max_paths = MIN((num_ant_paths + 1), CS_MAX_ANT_PATHS);
>> +       steps = MIN(num_steps_reported, CS_MAX_STEPS);
>> +       send_len = offsetof(struct rap_ev_cs_subevent_result_cont, step_data) +
>> +                                       steps * sizeof(struct cs_step_data);
>> +       rap_ev = (struct rap_ev_cs_subevent_result_cont *)malloc(send_len);
>> +       if (!rap_ev) {
>> +               DBG("[ERROR] Failed to allocate memory for subevent result\n");
>> +               return;
>> +       }
>> +
>> +       DBG("[EVENT] Subevent Result Cont (length=%u)\n", size);
>> +       rap_ev->conn_hdl                     = le16_to_cpu(handle);
>> +       rap_ev->config_id                    = config_id;
>> +       rap_ev->proc_done_status             = proc_done_status;
>> +       rap_ev->subevt_done_status           = subevt_done_status;
>> +       rap_ev->abort_reason                 = abort_reason;
>> +       rap_ev->num_ant_paths                = num_ant_paths;
>> +       rap_ev->num_steps_reported           = steps;
>> +
>> +       if (num_steps_reported > CS_MAX_STEPS) {
>> +               DBG("Too many steps reported: %u (max %u)",
>> +                       num_steps_reported, CS_MAX_STEPS);
>> +               goto send_event;
>> +       }
>> +
>> +       /* Early exit for error conditions */
>> +       if (rap_ev->subevt_done_status == 0xF ||
>> +           rap_ev->proc_done_status == 0xF) {
>> +               DBG("CS Procedure/Subevent aborted: ");
>> +               DBG("sub evt status = %d, proc status = %d, reason = %d",
>> +                       rap_ev->subevt_done_status, rap_ev->proc_done_status,
>> +                       rap_ev->abort_reason);
>> +               goto send_event;
>> +       }
>> +
>> +       /* Parse interleaved step data from remaining iovec data */
>> +       for (i = 0; i < steps; i++)
>> +               parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
>> +                       max_paths);
>> +
>> +send_event:
>> +       DBG("CS subevent result cont processed: %zu bytes, ", send_len);
>> +       bt_rap_hci_cs_subevent_result_cont_callback(send_len, rap_ev, sm->rap);
>> +       free(rap_ev);
>> +}
>> +
>> +/* Subevent handler function type */
>> +typedef void (*subevent_handler_t)(const uint8_t *data, uint8_t size,
>> +                                  void *user_data);
>> +
>> +/* Subevent table entry */
>> +struct subevent_entry {
>> +       uint8_t opcode;
>> +       uint8_t min_len;
>> +       uint8_t max_len;
>> +       subevent_handler_t handler;
>> +       const char *name;
>> +};
>> +
>> +/* Subevent dispatch table */
>> +static const struct subevent_entry subevent_table[] = {
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_RD_REM_SUPP_CAP_COMPLETE,
>> +               .min_len = sizeof(
>> +                       struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete),
>> +               .max_len = 0xFF,
>> +               .handler = rap_rd_rmt_supp_cap_cmplt_evt,
>> +               .name = "CS Read Remote Supported Capabilities Complete"
>> +       },
> 
> Have a macro to define each entry, HCI_EVT(...), that way we don't
> need to enter the struct field names.
> 
Addressed in v7.
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_CONFIG_COMPLETE,
>> +               .min_len = sizeof(struct bt_hci_evt_le_cs_config_complete),
>> +               .max_len = 0xFF,
>> +               .handler = rap_cs_config_cmplt_evt,
>> +               .name = "CS Config Complete"
>> +       },
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_SEC_ENABLE_COMPLETE,
>> +               .min_len = sizeof(struct bt_hci_evt_le_cs_sec_enable_complete),
>> +               .max_len = 0xFF,
>> +               .handler = rap_cs_sec_enable_cmplt_evt,
>> +               .name = "CS Security Enable Complete"
>> +       },
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_PROC_ENABLE_COMPLETE,
>> +               .min_len = sizeof(struct bt_hci_evt_le_cs_proc_enable_complete),
>> +               .max_len = 0xFF,
>> +               .handler = rap_cs_proc_enable_cmplt_evt,
>> +               .name = "CS Procedure Enable Complete"
>> +       },
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_SUBEVENT_RESULT,
>> +               .min_len = sizeof(struct bt_hci_evt_le_cs_subevent_result),
>> +               .max_len = 0xFF,
>> +               .handler = rap_cs_subevt_result_evt,
>> +               .name = "CS Subevent Result"
>> +       },
>> +       {
>> +               .opcode = BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE,
>> +               .min_len = sizeof(
>> +                       struct bt_hci_evt_le_cs_subevent_result_continue),
>> +               .max_len = 0xFF,
>> +               .handler = rap_cs_subevt_result_cont_evt,
>> +               .name = "CS Subevent Result Continue"
>> +       }
>> +};
>> +
>> +#define SUBEVENT_TABLE_SIZE ARRAY_SIZE(subevent_table)
>> +
>> +/* HCI Event Registration */
>> +static void rap_handle_hci_events(const void *data, uint8_t size,
>> +                               void *user_data)
>> +{
>> +       struct iovec iov;
>> +       uint8_t subevent;
>> +       const struct subevent_entry *entry = NULL;
>> +       size_t i;
>> +
>> +       /* Initialize iovec with the event data */
>> +       iov.iov_base = (void *)data;
>> +       iov.iov_len = size;
>> +
>> +       /* Pull the subevent code */
>> +       if (!util_iov_pull_u8(&iov, &subevent)) {
>> +               DBG("Failed to parse subevent code");
>> +               return;
>> +       }
>> +
>> +       /* Find the subevent in the table */
>> +       for (i = 0; i < SUBEVENT_TABLE_SIZE; i++) {
>> +               if (subevent_table[i].opcode == subevent) {
>> +                       entry = &subevent_table[i];
>> +                       break;
>> +               }
>> +       }
>> +
>> +       /* Check if subevent is supported */
>> +       if (!entry) {
>> +               DBG("Unknown subevent: 0x%02X", subevent);
>> +               return;
>> +       }
>> +
>> +       /* Validate payload length */
>> +       if (iov.iov_len < entry->min_len) {
>> +               DBG("%s: payload too short (%zu < %u)",
>> +                   entry->name, iov.iov_len, entry->min_len);
>> +               return;
>> +       }
>> +
>> +       if (entry->max_len != 0xFF && iov.iov_len > entry->max_len) {
>> +               DBG("%s: payload too long (%zu > %u)",
>> +                   entry->name, iov.iov_len, entry->max_len);
>> +               return;
>> +       }
>> +
>> +       /* Call the handler */
>> +       DBG("Handling %s (opcode=0x%02X, len=%zu)",
>> +           entry->name, subevent, iov.iov_len);
>> +
>> +       entry->handler(iov.iov_base, iov.iov_len, user_data);
>> +}
>> +
>> +void bt_rap_hci_register_events(struct bt_rap *rap, struct bt_hci *hci)
>> +{
>> +       if (!rap || !hci)
>> +               return;
>> +
>> +       sm = new0(struct cs_state_machine_t, 1);
>> +       if (!sm) {
>> +               DBG("[ERROR] Failed to allocate state machine\n");
>> +               return;
>> +       }
>> +
>> +       cs_state_machine_init(sm, rap, hci);
>> +       sm->event_id = bt_hci_register(hci, BT_HCI_EVT_LE_META_EVENT,
>> +                                       rap_handle_hci_events, sm, NULL);
>> +
>> +       DBG("bt_hci_register done, event_id : %d", sm->event_id);
>> +
>> +       if (!sm->event_id) {
>> +               DBG("Error: Failed to register hci le meta events ");
>> +               DBG("event_id=0x%02X\n", sm->event_id);
>> +               free(sm);
>> +               return;
>> +       }
>> +}
>> +
>> +bool bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci)
>> +{
>> +       if (!rap)
>> +               return false;
>> +
>> +       if (!hci) {
>> +               DBG("Failed to create HCI RAW channel ");
>> +               bt_hci_unref(hci);
>> +               return false;
>> +       }
>> +
>> +       bt_rap_hci_register_events(rap, hci);
>> +
>> +       return true;
>> +}
>> +
>> +bool bt_rap_set_conn_handle(struct bt_rap *rap, uint16_t handle,
>> +                               const uint8_t *bdaddr, uint8_t bdaddr_type)
>> +{
>> +       struct bt_att *att;
>> +
>> +       if (!rap)
>> +               return false;
>> +
>> +       att = bt_rap_get_att(rap);
>> +       if (!att)
>> +               return false;
>> +
>> +       DBG("Setting connection mapping: handle=0x%04X, ", handle);
>> +       if (bdaddr) {
>> +               DBG("bdaddr=%02x:%02x:%02x:%02x:%02x:%02x type=%u",
>> +                       bdaddr[5], bdaddr[4], bdaddr[3],
>> +                       bdaddr[2], bdaddr[1], bdaddr[0], bdaddr_type);
>> +       }
>> +
>> +       return add_conn_mapping(handle, bdaddr, bdaddr_type, att, rap);
>> +}
>> +
>> +void bt_rap_clear_conn_handle(struct bt_rap *rap, uint16_t handle)
>> +{
>> +       if (!rap)
>> +               return;
>> +
>> +       DBG("Clearing connection mapping: handle=0x%04X", handle);
>> +       remove_conn_mapping(handle);
>> +}
>> +
>> +void bt_rap_detach_hci(struct bt_rap *rap)
>> +{
>> +       if (!rap)
>> +               return;
>> +
>> +       DBG("Detaching RAP from HCI, cleaning up mappings");
>> +
>> +       /* Remove all mappings associated with this RAP instance */
>> +       remove_rap_mappings(rap);
>> +}
>> --
>>
> 
> 


      reply	other threads:[~2026-04-15 14:49 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-13 10:41 [PATCH BlueZ v4 0/3] Bluetooth: Add initial Channel Sounding Naga Bhavani Akella
2026-04-13 10:41 ` [PATCH BlueZ v4 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
2026-04-13 11:01   ` Bluetooth: Add initial Channel Sounding bluez.test.bot
2026-04-13 10:41 ` [PATCH BlueZ v4 2/3] bluetooth: Add Channel Sounding config parsing support Naga Bhavani Akella
2026-04-13 10:41 ` [PATCH BlueZ v4 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role Naga Bhavani Akella
2026-04-13 14:08   ` Luiz Augusto von Dentz
2026-04-15 14:49     ` Naga Bhavani Akella [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=91d278eb-a78c-4185-9660-493920f358fe@oss.qualcomm.com \
    --to=naga.akella@oss.qualcomm.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=prathibha.madugonde@oss.qualcomm.com \
    --cc=quic_anubhavg@quicinc.com \
    --cc=quic_hbandi@quicinc.com \
    --cc=quic_mohamull@quicinc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox