From 7ee72efb48ba4232cfd766da0b6a007c5061f806 Mon Sep 17 00:00:00 2001 From: Rahul Tank Date: Thu, 7 Apr 2022 15:03:46 +0530 Subject: [PATCH] nimble/host: Add HCI commands / events for LE Power control --- nimble/host/include/host/ble_gap.h | 126 ++++++++++++++ nimble/host/src/ble_gap.c | 155 ++++++++++++++++++ nimble/host/src/ble_gap_priv.h | 5 + nimble/host/src/ble_hs_hci_evt.c | 38 +++++ nimble/include/nimble/hci_common.h | 63 +++++++ nimble/syscfg.yml | 5 + .../examples/linux/include/syscfg/syscfg.h | 4 + .../linux_blemesh/include/syscfg/syscfg.h | 4 + .../examples/nuttx/include/syscfg/syscfg.h | 4 + porting/nimble/include/syscfg/syscfg.h | 4 + porting/npl/riot/include/syscfg/syscfg.h | 4 + 11 files changed, 412 insertions(+) diff --git a/nimble/host/include/host/ble_gap.h b/nimble/host/include/host/ble_gap.h index 63ae900da..d27b7f82c 100644 --- a/nimble/host/include/host/ble_gap.h +++ b/nimble/host/include/host/ble_gap.h @@ -135,6 +135,8 @@ struct hci_conn_update; #define BLE_GAP_EVENT_PERIODIC_SYNC_LOST 22 #define BLE_GAP_EVENT_SCAN_REQ_RCVD 23 #define BLE_GAP_EVENT_PERIODIC_TRANSFER 24 +#define BLE_GAP_EVENT_PATHLOSS_THRESHOLD 25 +#define BLE_GAP_EVENT_TRANSMIT_POWER 26 /*** Reason codes for the subscribe GAP event. */ @@ -973,6 +975,54 @@ struct ble_gap_event { uint8_t adv_clk_accuracy; } periodic_transfer; #endif + +#if MYNEWT_VAL(BLE_POWER_CONTROL) + /** + * Represents a change in either local transmit power or remote transmit + * power. Valid for the following event types: + * o BLE_GAP_EVENT_PATHLOSS_THRESHOLD + */ + + struct { + /** Connection handle */ + uint16_t conn_handle; + + /** Current Path Loss */ + uint8_t current_path_loss; + + /** Entered Zone */ + uint8_t zone_entered; + } pathloss_threshold; + + /** + * Represents crossing of path loss threshold set via LE Set Path Loss + * Reporting Parameter command. Valid for the following event types: + * o BLE_GAP_EVENT_TRANSMIT_POWER + */ + + struct { + /** BLE_ERR_SUCCESS on success or error code on failure */ + uint8_t status; + + /** Connection Handle */ + uint16_t conn_handle; + + /** Reason indicating why event was sent */ + uint8_t reason; + + /** Advertising PHY */ + uint8_t phy; + + /** Transmit power Level */ + uint8_t transmit_power_level; + + /** Transmit Power Level Flag */ + uint8_t transmit_power_level_flag; + + /** Delta indicating change in transmit Power Level */ + uint8_t delta; + } transmit_power; +#endif }; }; @@ -2097,6 +2147,82 @@ int ble_gap_event_listener_register(struct ble_gap_event_listener *listener, */ int ble_gap_event_listener_unregister(struct ble_gap_event_listener *listener); +#if MYNEWT_VAL(BLE_POWER_CONTROL) +/** + * Enable Set Path Loss Reporting. + * + * @param conn_handle Connection handle + * @params enable 1: Enable + * 0: Disable + * + * @return 0 on success; nonzero on failure. + */ + +int ble_gap_set_path_loss_reporting_enable(uint16_t conn_handle, uint8_t enable); + +/** + * Enable Reporting of Transmit Power + * + * @param conn_handle Connection handle + * @params local_enable 1: Enable local transmit power reports + * 0: Disable local transmit power reports + * + * @params remote_enable 1: Enable remote transmit power reports + * 0: Disable remote transmit power reports + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_transmit_power_reporting_enable(uint16_t conn_handle, + uint8_t local_enable, + uint8_t remote_enable); + +/** + * LE Enhanced Read Transmit Power Level + * + * @param conn_handle Connection handle + * @params phy Advertising Phy + * + * @params status 0 on success; nonzero on failure. + * @params conn_handle Connection handle + * @params phy Advertising Phy + * + * @params curr_tx_power_level Current trasnmit Power Level + * + * @params mx_tx_power_level Maximum transmit power level + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_enh_read_transmit_power_level(uint16_t conn_handle, uint8_t phy, + uint8_t *out_status, uint8_t *out_phy, + uint8_t *out_curr_tx_power_level, + uint8_t *out_max_tx_power_level); + +/** + * Read Remote Transmit Power Level + * + * @param conn_handle Connection handle + * @params phy Advertising Phy + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_read_remote_transmit_power_level(uint16_t conn_handle, uint8_t phy); + +/** + * Set Path Loss Reproting Param + * + * @param conn_handle Connection handle + * @params high_threshold High Threshold value for path loss + * @params high_hysteresis Hysteresis value for high threshold + * @params low_threshold Low Threshold value for path loss + * @params low_hysteresis Hysteresis value for low threshold + * @params min_time_spent Minimum time controller observes the path loss + * + * @return 0 on success; nonzero on failure. + */ +int ble_gap_set_path_loss_reporting_param(uint16_t conn_handle, uint8_t high_threshold, + uint8_t high_hysteresis, uint8_t low_threshold, + uint8_t low_hysteresis, uint16_t min_time_spent); +#endif #ifdef __cplusplus } #endif diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index 7e650fd9a..113a972d6 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -1641,6 +1641,42 @@ ble_gap_rx_periodic_adv_sync_lost(const struct ble_hci_ev_le_subev_periodic_adv_ } #endif +#if MYNEWT_VAL(BLE_POWER_CONTROL) +void +ble_gap_rx_le_pathloss_threshold(const struct ble_hci_ev_le_subev_path_loss_threshold *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof event); + + event.type = BLE_GAP_EVENT_PATHLOSS_THRESHOLD; + event.pathloss_threshold.conn_handle = le16toh(ev->conn_handle); + event.pathloss_threshold.current_path_loss = ev->current_path_loss; + event.pathloss_threshold.zone_entered = ev->zone_entered; + + ble_gap_event_listener_call(&event); +} + +void +ble_gap_rx_transmit_power_report(const struct ble_hci_ev_le_subev_transmit_power_report *ev) +{ + struct ble_gap_event event; + + memset(&event, 0, sizeof event); + + event.type = BLE_GAP_EVENT_TRANSMIT_POWER; + event.transmit_power.status = ev->status; + event.transmit_power.conn_handle = le16toh(ev->conn_handle); + event.transmit_power.reason = ev->reason; + event.transmit_power.phy = ev->phy; + event.transmit_power.transmit_power_level = ev->transmit_power_level; + event.transmit_power.transmit_power_level_flag = ev->transmit_power_level_flag; + event.transmit_power.delta = ev->delta; + + ble_gap_event_listener_call(&event); +} +#endif + #if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER) static int periodic_adv_transfer_disable(uint16_t conn_handle) @@ -6213,3 +6249,122 @@ ble_gap_init(void) err: return rc; } + +int +ble_gap_enh_read_transmit_power_level(uint16_t conn_handle, uint8_t phy, uint8_t *out_status, uint8_t *out_phy , + uint8_t *out_curr_tx_power_level, uint8_t *out_max_tx_power_level) + +{ +#if MYNEWT_VAL(BLE_POWER_CONTROL) + struct ble_hci_le_enh_read_transmit_power_level_cp cmd; + struct ble_hci_le_enh_read_transmit_power_level_rp rsp; + uint16_t opcode; + int rc; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ENH_READ_TRANSMIT_POWER_LEVEL); + + cmd.conn_handle = htole16(conn_handle); + cmd.phy = phy; + + rc = ble_hs_hci_cmd_tx(opcode, &cmd, sizeof(cmd), &rsp, sizeof(rsp)); + + if (rc!=0) { + return rc; + } + + *out_status = rsp.status; + *out_phy = rsp.phy; + *out_curr_tx_power_level = rsp.curr_tx_power_level; + *out_max_tx_power_level = rsp.max_tx_power_level; + + return 0; +#else + return BLE_HS_ENOTSUP; +#endif +} + +int +ble_gap_read_remote_transmit_power_level(uint16_t conn_handle, + uint8_t phy) +{ +#if MYNEWT_VAL(BLE_POWER_CONTROL) + struct ble_hci_le_read_remote_transmit_power_level_cp cmd; + uint16_t opcode; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL); + + cmd.conn_handle = htole16(conn_handle); + cmd.phy = phy; + + return ble_hs_hci_cmd_tx(opcode, &cmd, sizeof(cmd), NULL, 0); +#else + return BLE_HS_ENOTSUP; +#endif +} + +int +ble_gap_set_path_loss_reporting_param(uint16_t conn_handle, + uint8_t high_threshold, + uint8_t high_hysteresis, + uint8_t low_threshold, + uint8_t low_hysteresis, + uint16_t min_time_spent) +{ +#if MYNEWT_VAL(BLE_POWER_CONTROL) + struct ble_hci_le_set_path_loss_report_param_cp cmd; + uint16_t opcode; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PATH_LOSS_REPORT_PARAM); + + cmd.conn_handle = htole16(conn_handle); + cmd.high_threshold = high_threshold; + cmd.high_hysteresis = high_hysteresis; + cmd.low_threshold = low_threshold; + cmd.low_hysteresis = low_hysteresis; + cmd.min_time_spent = min_time_spent; + + return ble_hs_hci_cmd_tx(opcode, &cmd, sizeof(cmd), NULL, 0); +#else + return BLE_HS_ENOTSUP; +#endif +} + +int +ble_gap_set_path_loss_reporting_enable(uint16_t conn_handle, + uint8_t enable) +{ +#if MYNEWT_VAL(BLE_POWER_CONTROL) + struct ble_hci_le_set_path_loss_report_enable_cp cmd; + uint16_t opcode; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PATH_LOSS_REPORT_ENABLE); + + cmd.conn_handle = htole16(conn_handle); + cmd.enable = enable; + + return ble_hs_hci_cmd_tx(opcode, &cmd, sizeof(cmd), NULL, 0); +#else + return BLE_HS_ENOTSUP; +#endif +} + +int +ble_gap_set_transmit_power_reporting_enable(uint16_t conn_handle, + uint8_t local_enable, + uint8_t remote_enable) +{ +#if MYNEWT_VAL(BLE_POWER_CONTROL) + struct ble_hci_le_set_transmit_power_report_enable_cp cmd; + uint16_t opcode; + + opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_TRANS_PWR_REPORT_ENABLE); + + cmd.conn_handle = htole16(conn_handle); + cmd.local_enable = local_enable; + cmd.remote_enable = remote_enable; + + return ble_hs_hci_cmd_tx(opcode, &cmd, sizeof(cmd), NULL, 0); +#else + return BLE_HS_ENOTSUP; +#endif +} diff --git a/nimble/host/src/ble_gap_priv.h b/nimble/host/src/ble_gap_priv.h index 499823bc5..ca85db3dd 100644 --- a/nimble/host/src/ble_gap_priv.h +++ b/nimble/host/src/ble_gap_priv.h @@ -93,6 +93,11 @@ void ble_gap_rx_scan_req_rcvd(const struct ble_hci_ev_le_subev_scan_req_rcvd *ev void ble_gap_rx_adv_report(struct ble_gap_disc_desc *desc); void ble_gap_rx_rd_rem_sup_feat_complete(const struct ble_hci_ev_le_subev_rd_rem_used_feat *ev); +#if MYNEWT_VAL(BLE_POWER_CONTROL) +void ble_gap_rx_transmit_power_report(const struct ble_hci_ev_le_subev_transmit_power_report *ev); +void ble_gap_rx_le_pathloss_threshold(const struct ble_hci_ev_le_subev_path_loss_threshold *ev); +#endif + struct ble_gap_conn_complete { uint8_t status; diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index b3724a821..be64a3d3a 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -60,6 +60,10 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_rpt; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_lost; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_scan_req_rcvd; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_transfer; +#if MYNEWT_VAL(BLE_POWER_CONTROL) +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_pathloss_threshold; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_transmit_power_report; +#endif /* Statistics */ struct host_hci_stats @@ -116,6 +120,10 @@ static ble_hs_hci_evt_le_fn * const ble_hs_hci_evt_le_dispatch[] = { [BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED] = ble_hs_hci_evt_le_adv_set_terminated, [BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD] = ble_hs_hci_evt_le_scan_req_rcvd, [BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER] = ble_hs_hci_evt_le_periodic_adv_sync_transfer, +#if MYNEWT_VAL(BLE_POWER_CONTROL) + [BLE_HCI_LE_SUBEV_PATH_LOSS_THRESHOLD] = ble_hs_hci_evt_le_pathloss_threshold, + [BLE_HCI_LE_SUBEV_TRANSMIT_POWER_REPORT] = ble_hs_hci_evt_le_transmit_power_report, +#endif }; #define BLE_HS_HCI_EVT_LE_DISPATCH_SZ \ @@ -641,6 +649,36 @@ ble_hs_hci_evt_le_periodic_adv_sync_lost(uint8_t subevent, const void *data, return 0; } +#if MYNEWT_VAL(BLE_POWER_CONTROL) +static int +ble_hs_hci_evt_le_pathloss_threshold(uint8_t subevent, const void *data, + unsigned int len) +{ + const struct ble_hci_ev_le_subev_path_loss_threshold *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_EBADDATA; + } + + ble_gap_rx_le_pathloss_threshold(ev); + return 0; +} + +static int +ble_hs_hci_evt_le_transmit_power_report(uint8_t subevent, const void *data, + unsigned int len) +{ + const struct ble_hci_ev_le_subev_transmit_power_report *ev = data; + + if (len != sizeof(*ev)) { + return BLE_HS_EBADDATA; + } + + ble_gap_rx_transmit_power_report(ev); + return 0; +} +#endif + static int ble_hs_hci_evt_le_periodic_adv_sync_transfer(uint8_t subevent, const void *data, unsigned int len) diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index 58de8a6cd..aa3e3775a 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -1122,6 +1122,49 @@ struct ble_hci_vs_css_set_conn_slot_cp { uint16_t slot_idx; } __attribute__((packed)); +#define BLE_HCI_OCF_LE_ENH_READ_TRANSMIT_POWER_LEVEL (0x0076) +struct ble_hci_le_enh_read_transmit_power_level_cp { + uint16_t conn_handle; + uint8_t phy; +} __attribute__((packed)); +struct ble_hci_le_enh_read_transmit_power_level_rp { + uint8_t status; + uint16_t conn_handle; + uint8_t phy; + uint8_t curr_tx_power_level; + uint8_t max_tx_power_level; +} __attribute__((packed)); + +#define BLE_HCI_OCF_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL (0x0077) +struct ble_hci_le_read_remote_transmit_power_level_cp { + uint16_t conn_handle; + uint8_t phy; +} __attribute__((packed)); + +#define BLE_HCI_OCF_LE_SET_PATH_LOSS_REPORT_PARAM (0x0078) +struct ble_hci_le_set_path_loss_report_param_cp { + uint16_t conn_handle; + uint8_t high_threshold; + uint8_t high_hysteresis; + uint8_t low_threshold; + uint8_t low_hysteresis; + uint16_t min_time_spent; +} __attribute__((packed)); + +#define BLE_HCI_OCF_LE_SET_PATH_LOSS_REPORT_ENABLE (0x0079) +struct ble_hci_le_set_path_loss_report_enable_cp { + uint16_t conn_handle; + uint8_t enable; +} __attribute__((packed)); + +#define BLE_HCI_OCF_LE_SET_TRANS_PWR_REPORT_ENABLE (0x007A) +struct ble_hci_le_set_transmit_power_report_enable_cp { + uint16_t conn_handle; + uint8_t local_enable; + uint8_t remote_enable; +} __attribute__((packed)); + + /* Command Specific Definitions */ /* --- Set controller to host flow control (OGF 0x03, OCF 0x0031) --- */ #define BLE_HCI_CTLR_TO_HOST_FC_OFF (0) @@ -1823,6 +1866,26 @@ struct ble_hci_ev_le_subev_peer_sca_complete { uint8_t sca; } __attribute__((packed)); +#define BLE_HCI_LE_SUBEV_PATH_LOSS_THRESHOLD (0x20) +struct ble_hci_ev_le_subev_path_loss_threshold { + uint8_t subev_code; + uint16_t conn_handle; + uint8_t current_path_loss; + uint8_t zone_entered; +} __attribute__((packed)); + +#define BLE_HCI_LE_SUBEV_TRANSMIT_POWER_REPORT (0x21) +struct ble_hci_ev_le_subev_transmit_power_report { + uint8_t subev_code; + uint8_t status; + uint16_t conn_handle; + uint8_t reason; + uint8_t phy; + uint8_t transmit_power_level; + uint8_t transmit_power_level_flag; + uint8_t delta; +} __attribute__((packed)); + #define BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT (0x22) struct ble_hci_ev_le_subev_biginfo_adv_report { uint8_t subev_code; diff --git a/nimble/syscfg.yml b/nimble/syscfg.yml index 2d2d32927..f8c92ea09 100644 --- a/nimble/syscfg.yml +++ b/nimble/syscfg.yml @@ -108,6 +108,11 @@ syscfg.defs: implementations. value: 0 + BLE_POWER_CONTROL: + description: > + This enabled LE Power Control feature + value: 0 + # Allow periodic sync transfer only if 5.1 or higher syscfg.restrictions: - "'BLE_PERIODIC_ADV_SYNC_TRANSFER == 0' || 'BLE_VERSION >= 51'" diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index 903163ad6..14877d161 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -498,6 +498,10 @@ #define MYNEWT_VAL_BLE_WHITELIST (1) #endif +#ifndef MYNEWT_VAL_BLE_POWER_CONTROL +#define MYNEWT_VAL_BLE_POWER_CONTROL (0) +#endif + /*** @apache-mynewt-nimble/nimble/host */ #ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU #define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU (256) diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index 550ce5398..5fccff8a2 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -499,6 +499,10 @@ #define MYNEWT_VAL_BLE_WHITELIST (1) #endif +#ifndef MYNEWT_VAL_BLE_POWER_CONTROL +#define MYNEWT_VAL_BLE_POWER_CONTROL (0) +#endif + /*** @apache-mynewt-nimble/nimble/host */ #ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU #define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU (256) diff --git a/porting/examples/nuttx/include/syscfg/syscfg.h b/porting/examples/nuttx/include/syscfg/syscfg.h index 11fa149d6..435b3aebc 100644 --- a/porting/examples/nuttx/include/syscfg/syscfg.h +++ b/porting/examples/nuttx/include/syscfg/syscfg.h @@ -498,6 +498,10 @@ #define MYNEWT_VAL_BLE_WHITELIST (1) #endif +#ifndef MYNEWT_VAL_BLE_POWER_CONTROL +#define MYNEWT_VAL_BLE_POWER_CONTROL (0) +#endif + /*** @apache-mynewt-nimble/nimble/host */ #ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU #define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU (256) diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h index 55a7c8b32..ba2a292d7 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -497,6 +497,10 @@ #define MYNEWT_VAL_BLE_WHITELIST (1) #endif +#ifndef MYNEWT_VAL_BLE_POWER_CONTROL +#define MYNEWT_VAL_BLE_POWER_CONTROL (0) +#endif + /*** @apache-mynewt-nimble/nimble/host */ #ifndef MYNEWT_VAL_BLE_ATT_PREFERRED_MTU #define MYNEWT_VAL_BLE_ATT_PREFERRED_MTU (256) diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h index d7493392f..84bdea5ef 100644 --- a/porting/npl/riot/include/syscfg/syscfg.h +++ b/porting/npl/riot/include/syscfg/syscfg.h @@ -858,6 +858,10 @@ #define MYNEWT_VAL_BLE_WHITELIST (1) #endif +#ifndef MYNEWT_VAL_BLE_POWER_CONTROL +#define MYNEWT_VAL_BLE_POWER_CONTROL (0) +#endif + /*** @apache-mynewt-nimble/nimble/controller */ #ifndef MYNEWT_VAL_BLE_CONTROLLER #define MYNEWT_VAL_BLE_CONTROLLER (1)