| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <string.h> |
| #include "nimble/hci_common.h" |
| #include "host/ble_hs_hci.h" |
| #include "ble_hs_priv.h" |
| |
| #ifndef min |
| #define min(a, b) ((a) < (b) ? (a) : (b)) |
| #endif |
| |
| #ifndef max |
| #define max(a, b) ((a) > (b) ? (a) : (b)) |
| #endif |
| |
| uint16_t |
| ble_hs_hci_util_handle_pb_bc_join(uint16_t handle, uint8_t pb, uint8_t bc) |
| { |
| BLE_HS_DBG_ASSERT(handle <= 0x0fff); |
| BLE_HS_DBG_ASSERT(pb <= 0x03); |
| BLE_HS_DBG_ASSERT(bc <= 0x03); |
| |
| return (handle << 0) | |
| (pb << 12) | |
| (bc << 14); |
| } |
| |
| int |
| ble_hs_hci_util_read_adv_tx_pwr(int8_t *out_tx_pwr) |
| { |
| struct ble_hci_le_rd_adv_chan_txpwr_rp rsp; |
| int rc; |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR), |
| NULL, 0, &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| *out_tx_pwr = rsp.power_level; |
| |
| if (*out_tx_pwr < BLE_HCI_ADV_CHAN_TXPWR_MIN || |
| *out_tx_pwr > BLE_HCI_ADV_CHAN_TXPWR_MAX) { |
| BLE_HS_LOG(WARN, "advertiser txpwr out of range\n"); |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_rand(void *dst, int len) |
| { |
| struct ble_hci_le_rand_rp rsp; |
| uint8_t *u8ptr; |
| int chunk_sz; |
| int rc; |
| |
| u8ptr = dst; |
| while (len > 0) { |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RAND), |
| NULL, 0, &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| chunk_sz = min(len, (int)sizeof(rsp)); |
| memcpy(u8ptr, &rsp.random_number, chunk_sz); |
| |
| len -= chunk_sz; |
| u8ptr += chunk_sz; |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_util_read_rssi(uint16_t conn_handle, int8_t *out_rssi) |
| { |
| struct ble_hci_rd_rssi_cp cmd; |
| struct ble_hci_rd_rssi_rp rsp; |
| |
| int rc; |
| |
| cmd.handle = htole16(conn_handle); |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_STATUS_PARAMS, |
| BLE_HCI_OCF_RD_RSSI), &cmd, sizeof(cmd), |
| &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (le16toh(rsp.handle) != conn_handle) { |
| return BLE_HS_ECONTROLLER; |
| } |
| |
| *out_rssi = rsp.rssi; |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_util_set_random_addr(const uint8_t *addr) |
| { |
| struct ble_hci_le_set_rand_addr_cp cmd; |
| |
| memcpy(cmd.addr, addr, BLE_DEV_ADDR_LEN); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_RAND_ADDR), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |
| |
| int |
| ble_hs_hci_util_set_data_len(uint16_t conn_handle, uint16_t tx_octets, |
| uint16_t tx_time) |
| { |
| struct ble_hci_le_set_data_len_cp cmd; |
| struct ble_hci_le_set_data_len_rp rsp; |
| int rc; |
| |
| if (tx_octets < BLE_HCI_SET_DATALEN_TX_OCTETS_MIN || |
| tx_octets > BLE_HCI_SET_DATALEN_TX_OCTETS_MAX) { |
| return BLE_HS_EINVAL; |
| } |
| |
| if (tx_time < BLE_HCI_SET_DATALEN_TX_TIME_MIN || |
| tx_time > BLE_HCI_SET_DATALEN_TX_TIME_MAX) { |
| return BLE_HS_EINVAL; |
| } |
| |
| cmd.conn_handle = htole16(conn_handle); |
| cmd.tx_octets = htole16(tx_octets); |
| cmd.tx_time = htole16(tx_time); |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_DATA_LEN), |
| &cmd, sizeof(cmd), &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (le16toh(rsp.conn_handle) != conn_handle) { |
| return BLE_HS_ECONTROLLER; |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_util_read_sugg_def_data_len(uint16_t *out_sugg_max_tx_octets, |
| uint16_t *out_sugg_max_tx_time) |
| { |
| struct ble_hci_le_rd_sugg_def_data_len_rp rsp; |
| int rc; |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_SUGG_DEF_DATA_LEN), |
| NULL, 0, &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| *out_sugg_max_tx_octets = le16toh(rsp.max_tx_octets); |
| *out_sugg_max_tx_time = le16toh(rsp.max_tx_time); |
| |
| if (*out_sugg_max_tx_octets < BLE_HCI_SUGG_DEF_DATALEN_TX_OCTETS_MIN || |
| *out_sugg_max_tx_octets > BLE_HCI_SUGG_DEF_DATALEN_TX_OCTETS_MAX) { |
| BLE_HS_LOG(WARN, |
| "received suggested maximum tx octets is out of range\n"); |
| } |
| |
| if (*out_sugg_max_tx_time < BLE_HCI_SUGG_DEF_DATALEN_TX_TIME_MIN || |
| *out_sugg_max_tx_time > BLE_HCI_SUGG_DEF_DATALEN_TX_TIME_MAX) { |
| BLE_HS_LOG(WARN, |
| "received suggested maximum tx time is out of range\n"); |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_util_write_sugg_def_data_len(uint16_t sugg_max_tx_octets, |
| uint16_t sugg_max_tx_time) |
| { |
| struct ble_hci_le_wr_sugg_def_data_len_cp cmd; |
| |
| if (sugg_max_tx_octets < BLE_HCI_SUGG_DEF_DATALEN_TX_OCTETS_MIN || |
| sugg_max_tx_octets > BLE_HCI_SUGG_DEF_DATALEN_TX_OCTETS_MAX) { |
| return BLE_HS_EINVAL; |
| } |
| |
| if (sugg_max_tx_time < BLE_HCI_SUGG_DEF_DATALEN_TX_TIME_MIN || |
| sugg_max_tx_time > BLE_HCI_SUGG_DEF_DATALEN_TX_TIME_MAX) { |
| return BLE_HS_EINVAL; |
| } |
| |
| cmd.max_tx_octets = htole16(sugg_max_tx_octets); |
| cmd.max_tx_time = htole16(sugg_max_tx_time); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_WR_SUGG_DEF_DATA_LEN), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |
| |
| int |
| ble_hs_hci_util_data_hdr_strip(struct os_mbuf *om, |
| struct hci_data_hdr *out_hdr) |
| { |
| int rc; |
| |
| rc = os_mbuf_copydata(om, 0, BLE_HCI_DATA_HDR_SZ, out_hdr); |
| if (rc != 0) { |
| return BLE_HS_ECONTROLLER; |
| } |
| |
| /* Strip HCI ACL data header from the front of the packet. */ |
| os_mbuf_adj(om, BLE_HCI_DATA_HDR_SZ); |
| |
| out_hdr->hdh_handle_pb_bc = get_le16(&out_hdr->hdh_handle_pb_bc); |
| out_hdr->hdh_len = get_le16(&out_hdr->hdh_len); |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_read_chan_map(uint16_t conn_handle, uint8_t *out_chan_map) |
| { |
| struct ble_hci_le_rd_chan_map_cp cmd; |
| struct ble_hci_le_rd_chan_map_rp rsp; |
| int rc; |
| |
| cmd.conn_handle = htole16(conn_handle); |
| |
| rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_CHAN_MAP), |
| &cmd, sizeof(cmd), &rsp, sizeof(rsp)); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| if (le16toh(rsp.conn_handle) != conn_handle) { |
| return BLE_HS_ECONTROLLER; |
| } |
| |
| memcpy(out_chan_map, rsp.chan_map, 5); |
| |
| return 0; |
| } |
| |
| int |
| ble_hs_hci_set_chan_class(const uint8_t *chan_map) |
| { |
| struct ble_hci_le_set_host_chan_class_cp cmd; |
| |
| memcpy(cmd.chan_map, chan_map, sizeof(cmd.chan_map)); |
| |
| return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS), |
| &cmd, sizeof(cmd), NULL, 0); |
| } |