Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id BED6B200B5C for ; Thu, 11 Aug 2016 23:27:14 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id BD54D160A90; Thu, 11 Aug 2016 21:27:14 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 8CB40160A93 for ; Thu, 11 Aug 2016 23:27:12 +0200 (CEST) Received: (qmail 49335 invoked by uid 500); 11 Aug 2016 21:27:11 -0000 Mailing-List: contact commits-help@mynewt.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@mynewt.incubator.apache.org Delivered-To: mailing list commits@mynewt.incubator.apache.org Received: (qmail 49298 invoked by uid 99); 11 Aug 2016 21:27:11 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 11 Aug 2016 21:27:11 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 38F92C038F for ; Thu, 11 Aug 2016 21:27:11 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -3.739 X-Spam-Level: X-Spam-Status: No, score=-3.739 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.519] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id 1RutQYGhc8l2 for ; Thu, 11 Aug 2016 21:27:06 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 55D0560D25 for ; Thu, 11 Aug 2016 21:27:02 +0000 (UTC) Received: (qmail 46689 invoked by uid 99); 11 Aug 2016 21:27:01 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 11 Aug 2016 21:27:01 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 52C9AE0007; Thu, 11 Aug 2016 21:27:01 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: wes3@apache.org To: commits@mynewt.incubator.apache.org Date: Thu, 11 Aug 2016 21:27:07 -0000 Message-Id: <130658b94e07433d8225fdf3e3b34431@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [07/50] [abbrv] incubator-mynewt-core git commit: BLE Host - Rename HCI identifiers and files. archived-at: Thu, 11 Aug 2016 21:27:14 -0000 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_hci.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_hci.c b/net/nimble/host/src/ble_hs_hci.c new file mode 100644 index 0000000..cd5cc6c --- /dev/null +++ b/net/nimble/host/src/ble_hs_hci.c @@ -0,0 +1,528 @@ +/** + * 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 +#include +#include +#include +#include "os/os.h" +#include "nimble/ble_hci_trans.h" +#include "ble_hs_priv.h" +#include "ble_hs_dbg_priv.h" + +#define BLE_HCI_CMD_TIMEOUT (OS_TICKS_PER_SEC) + +static struct os_mutex ble_hs_hci_mutex; +static struct os_sem ble_hs_hci_sem; + +static uint8_t *ble_hs_hci_ack; +static uint16_t ble_hs_hci_buf_sz; +static uint8_t ble_hs_hci_max_pkts; + +#if PHONY_HCI_ACKS +static ble_hs_hci_phony_ack_fn *ble_hs_hci_phony_ack_cb; +#endif + +#if PHONY_HCI_ACKS +void +ble_hs_hci_set_phony_ack_cb(ble_hs_hci_phony_ack_fn *cb) +{ + ble_hs_hci_phony_ack_cb = cb; +} +#endif + +static void +ble_hs_hci_lock(void) +{ + int rc; + + rc = os_mutex_pend(&ble_hs_hci_mutex, 0xffffffff); + BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED); +} + +static void +ble_hs_hci_unlock(void) +{ + int rc; + + rc = os_mutex_release(&ble_hs_hci_mutex); + BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED); +} + +int +ble_hs_hci_set_buf_sz(uint16_t pktlen, uint8_t max_pkts) +{ + if (pktlen == 0 || max_pkts == 0) { + return BLE_HS_EINVAL; + } + + ble_hs_hci_buf_sz = pktlen; + ble_hs_hci_max_pkts = max_pkts; + + return 0; +} + +static int +ble_hs_hci_rx_cmd_complete(uint8_t event_code, uint8_t *data, int len, + struct ble_hs_hci_ack *out_ack) +{ + uint16_t opcode; + uint8_t *params; + uint8_t params_len; + uint8_t num_pkts; + + if (len < BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN) { + return BLE_HS_ECONTROLLER; + } + + num_pkts = data[2]; + opcode = le16toh(data + 3); + params = data + 5; + + /* XXX: Process num_pkts field. */ + (void)num_pkts; + + out_ack->bha_opcode = opcode; + + params_len = len - BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN; + if (params_len > 0) { + out_ack->bha_status = BLE_HS_HCI_ERR(params[0]); + } else if (opcode == BLE_HCI_OPCODE_NOP) { + out_ack->bha_status = 0; + } else { + out_ack->bha_status = BLE_HS_ECONTROLLER; + } + + /* Don't include the status byte in the parameters blob. */ + if (params_len > 1) { + out_ack->bha_params = params + 1; + out_ack->bha_params_len = params_len - 1; + } else { + out_ack->bha_params = NULL; + out_ack->bha_params_len = 0; + } + + return 0; +} + +static int +ble_hs_hci_rx_cmd_status(uint8_t event_code, uint8_t *data, int len, + struct ble_hs_hci_ack *out_ack) +{ + uint16_t opcode; + uint8_t num_pkts; + uint8_t status; + + if (len < BLE_HCI_EVENT_CMD_STATUS_LEN) { + return BLE_HS_ECONTROLLER; + } + + status = data[2]; + num_pkts = data[3]; + opcode = le16toh(data + 4); + + /* XXX: Process num_pkts field. */ + (void)num_pkts; + + out_ack->bha_opcode = opcode; + out_ack->bha_params = NULL; + out_ack->bha_params_len = 0; + out_ack->bha_status = BLE_HS_HCI_ERR(status); + + return 0; +} + +static int +ble_hs_hci_process_ack(uint16_t expected_opcode, + uint8_t *params_buf, uint8_t params_buf_len, + struct ble_hs_hci_ack *out_ack) +{ + uint8_t event_code; + uint8_t param_len; + uint8_t event_len; + int rc; + + BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL); + + /* Count events received */ + STATS_INC(ble_hs_stats, hci_event); + + /* Display to console */ + ble_hs_dbg_event_disp(ble_hs_hci_ack); + + event_code = ble_hs_hci_ack[0]; + param_len = ble_hs_hci_ack[1]; + event_len = param_len + 2; + + /* Clear ack fields up front to silence spurious gcc warnings. */ + memset(out_ack, 0, sizeof *out_ack); + + switch (event_code) { + case BLE_HCI_EVCODE_COMMAND_COMPLETE: + rc = ble_hs_hci_rx_cmd_complete(event_code, ble_hs_hci_ack, + event_len, out_ack); + break; + + case BLE_HCI_EVCODE_COMMAND_STATUS: + rc = ble_hs_hci_rx_cmd_status(event_code, ble_hs_hci_ack, + event_len, out_ack); + break; + + default: + BLE_HS_DBG_ASSERT(0); + rc = BLE_HS_EUNKNOWN; + break; + } + + if (rc == 0) { + if (params_buf == NULL) { + out_ack->bha_params_len = 0; + } else { + if (out_ack->bha_params_len > params_buf_len) { + out_ack->bha_params_len = params_buf_len; + rc = BLE_HS_ECONTROLLER; + } + memcpy(params_buf, out_ack->bha_params, out_ack->bha_params_len); + } + out_ack->bha_params = params_buf; + + if (out_ack->bha_opcode != expected_opcode) { + rc = BLE_HS_ECONTROLLER; + } + } + + if (rc != 0) { + STATS_INC(ble_hs_stats, hci_invalid_ack); + } + + return rc; +} + +static int +ble_hs_hci_wait_for_ack(void) +{ + int rc; + +#if PHONY_HCI_ACKS + if (ble_hs_hci_phony_ack_cb == NULL) { + rc = BLE_HS_ETIMEOUT_HCI; + } else { + ble_hs_hci_ack = + ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL); + rc = ble_hs_hci_phony_ack_cb(ble_hs_hci_ack, 260); + } +#else + rc = os_sem_pend(&ble_hs_hci_sem, BLE_HCI_CMD_TIMEOUT); + switch (rc) { + case 0: + BLE_HS_DBG_ASSERT(ble_hs_hci_ack != NULL); + break; + case OS_TIMEOUT: + rc = BLE_HS_ETIMEOUT_HCI; + STATS_INC(ble_hs_stats, hci_timeout); + break; + default: + rc = BLE_HS_EOS; + break; + } +#endif + + return rc; +} + +int +ble_hs_hci_cmd_tx(void *cmd, void *evt_buf, uint8_t evt_buf_len, + uint8_t *out_evt_buf_len) +{ + struct ble_hs_hci_ack ack; + uint16_t opcode; + int rc; + + opcode = le16toh(cmd); + + BLE_HS_DBG_ASSERT(ble_hs_hci_ack == NULL); + ble_hs_hci_lock(); + + rc = ble_hs_hci_cmd_send_buf(cmd); + if (rc != 0) { + goto done; + } + + rc = ble_hs_hci_wait_for_ack(); + if (rc != 0) { + ble_hs_sched_reset(rc); + goto done; + } + + rc = ble_hs_hci_process_ack(opcode, evt_buf, evt_buf_len, &ack); + if (rc != 0) { + ble_hs_sched_reset(rc); + goto done; + } + + if (out_evt_buf_len != NULL) { + *out_evt_buf_len = ack.bha_params_len; + } + + rc = ack.bha_status; + +done: + if (ble_hs_hci_ack != NULL) { + ble_hci_trans_buf_free(ble_hs_hci_ack); + ble_hs_hci_ack = NULL; + } + + ble_hs_hci_unlock(); + return rc; +} + +int +ble_hs_hci_cmd_tx_empty_ack(void *cmd) +{ + int rc; + + rc = ble_hs_hci_cmd_tx(cmd, NULL, 0, NULL); + if (rc != 0) { + return rc; + } + + return 0; +} + +void +ble_hs_hci_rx_ack(uint8_t *ack_ev) +{ + if (ble_hs_hci_sem.sem_tokens != 0) { + /* This ack is unexpected; ignore it. */ + ble_hci_trans_buf_free(ack_ev); + return; + } + BLE_HS_DBG_ASSERT(ble_hs_hci_ack == NULL); + + /* Unblock the application now that the HCI command buffer is populated + * with the acknowledgement. + */ + ble_hs_hci_ack = ack_ev; + os_sem_release(&ble_hs_hci_sem); +} + +int +ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg) +{ + int enqueue; + + BLE_HS_DBG_ASSERT(hci_ev != NULL); + + switch (hci_ev[0]) { + case BLE_HCI_EVCODE_COMMAND_COMPLETE: + case BLE_HCI_EVCODE_COMMAND_STATUS: + if (hci_ev[3] == 0 && hci_ev[4] == 0) { + enqueue = 1; + } else { + ble_hs_hci_rx_ack(hci_ev); + enqueue = 0; + } + break; + + default: + enqueue = 1; + break; + } + + if (enqueue) { + ble_hs_enqueue_hci_event(hci_ev); + } + + return 0; +} + + +/** + * Splits an appropriately-sized fragment from the front of an outgoing ACL + * data packet, if necessary. If the packet size is within the controller's + * buffer size requirements, no splitting is performed. The fragment data is + * removed from the data packet mbuf. + * + * @param om The ACL data packet. + * @param out_frag On success, this points to the fragment to + * send. If the entire packet can fit within + * a single fragment, this will point to the + * ACL data packet itself ('om'). + * + * @return BLE_HS_EDONE: success; this is the final + * fragment. + * BLE_HS_EAGAIN: success; more data remains in + * the original mbuf. + * Other BLE host core return code on error. + */ +static int +ble_hs_hci_split_frag(struct os_mbuf **om, struct os_mbuf **out_frag) +{ + struct os_mbuf *frag; + int rc; + + if (OS_MBUF_PKTLEN(*om) <= ble_hs_hci_buf_sz) { + /* Final fragment. */ + *out_frag = *om; + *om = NULL; + return BLE_HS_EDONE; + } + + frag = ble_hs_mbuf_acm_pkt(); + if (frag == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + /* Move data from the front of the packet into the fragment mbuf. */ + rc = os_mbuf_appendfrom(frag, *om, 0, ble_hs_hci_buf_sz); + if (rc != 0) { + rc = BLE_HS_ENOMEM; + goto err; + } + os_mbuf_adj(*om, ble_hs_hci_buf_sz); + + /* More fragments to follow. */ + *out_frag = frag; + return BLE_HS_EAGAIN; + +err: + os_mbuf_free_chain(frag); + return rc; +} + +static struct os_mbuf * +ble_hs_hci_acl_hdr_prepend(struct os_mbuf *om, uint16_t handle, + uint8_t pb_flag) +{ + struct hci_data_hdr hci_hdr; + struct os_mbuf *om2; + + hci_hdr.hdh_handle_pb_bc = + ble_hs_hci_util_handle_pb_bc_join(handle, pb_flag, 0); + htole16(&hci_hdr.hdh_len, OS_MBUF_PKTHDR(om)->omp_len); + + om2 = os_mbuf_prepend(om, sizeof hci_hdr); + if (om2 == NULL) { + return NULL; + } + + om = om2; + om = os_mbuf_pullup(om, sizeof hci_hdr); + if (om == NULL) { + return NULL; + } + + memcpy(om->om_data, &hci_hdr, sizeof hci_hdr); + + BLE_HS_LOG(DEBUG, "host tx hci data; handle=%d length=%d\n", handle, + le16toh(&hci_hdr.hdh_len)); + + return om; +} + +/** + * Transmits an HCI ACL data packet. This function consumes the supplied mbuf, + * regardless of the outcome. + * + * XXX: Ensure the controller has sufficient buffer capacity for the outgoing + * fragments. + */ +int +ble_hs_hci_acl_tx(struct ble_hs_conn *connection, struct os_mbuf *txom) +{ + struct os_mbuf *frag; + uint8_t pb; + int done; + int rc; + + /* The first fragment uses the first-non-flush packet boundary value. + * After sending the first fragment, pb gets set appropriately for all + * subsequent fragments in this packet. + */ + pb = BLE_HCI_PB_FIRST_NON_FLUSH; + + /* Send fragments until the entire packet has been sent. */ + done = 0; + while (!done) { + rc = ble_hs_hci_split_frag(&txom, &frag); + switch (rc) { + case BLE_HS_EDONE: + /* This is the final fragment. */ + done = 1; + break; + + case BLE_HS_EAGAIN: + /* More fragments to follow. */ + break; + + default: + goto err; + } + + frag = ble_hs_hci_acl_hdr_prepend(frag, connection->bhc_handle, pb); + if (frag == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + pb = BLE_HCI_PB_MIDDLE; + + BLE_HS_LOG(DEBUG, "ble_hs_hci_acl_tx(): "); + ble_hs_log_mbuf(frag); + BLE_HS_LOG(DEBUG, "\n"); + + /* XXX: Try to pullup the entire fragment. The controller currently + * requires the entire fragment to fit in a single buffer. When this + * restriction is removed from the controller, this operation can be + * removed. + */ + frag = os_mbuf_pullup(frag, OS_MBUF_PKTLEN(frag)); + if (frag == NULL) { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = ble_hs_tx_data(frag); + if (rc != 0) { + goto err; + } + + connection->bhc_outstanding_pkts++; + } + + return 0; + +err: + BLE_HS_DBG_ASSERT(rc != 0); + + os_mbuf_free_chain(txom); + return rc; +} + +void +ble_hs_hci_init(void) +{ + int rc; + + rc = os_sem_init(&ble_hs_hci_sem, 0); + BLE_HS_DBG_ASSERT_EVAL(rc == 0); + + rc = os_mutex_init(&ble_hs_hci_mutex); + BLE_HS_DBG_ASSERT_EVAL(rc == 0); +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_hci_cmd.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_hci_cmd.c b/net/nimble/host/src/ble_hs_hci_cmd.c new file mode 100644 index 0000000..82b442d --- /dev/null +++ b/net/nimble/host/src/ble_hs_hci_cmd.c @@ -0,0 +1,1375 @@ +/** + * 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 +#include +#include +#include +#include +#include "os/os.h" +#include "console/console.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "ble_hs_dbg_priv.h" +#include "ble_hs_priv.h" + +static int +ble_hs_hci_cmd_transport(uint8_t *cmdbuf) +{ + int rc; + + rc = ble_hci_trans_hs_cmd_tx(cmdbuf); + switch (rc) { + case 0: + return 0; + + case BLE_ERR_MEM_CAPACITY: + return BLE_HS_ENOMEM_EVT; + + default: + return BLE_HS_EUNKNOWN; + } +} + +void +ble_hs_hci_cmd_write_hdr(uint8_t ogf, uint8_t ocf, uint8_t len, void *buf) +{ + uint16_t opcode; + uint8_t *u8ptr; + + u8ptr = buf; + + opcode = (ogf << 10) | ocf; + htole16(u8ptr, opcode); + u8ptr[2] = len; +} + +int +ble_hs_hci_cmd_send(uint8_t ogf, uint8_t ocf, uint8_t len, const void *cmddata) +{ + uint8_t *buf; + int rc; + + buf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); + BLE_HS_DBG_ASSERT(buf != NULL); + + htole16(buf, ogf << 10 | ocf); + buf[2] = len; + if (len != 0) { + memcpy(buf + BLE_HCI_CMD_HDR_LEN, cmddata, len); + } + + BLE_HS_LOG(DEBUG, "ble_hs_hci_cmd_send: ogf=0x%02x ocf=0x%02x len=%d\n", + ogf, ocf, len); + ble_hs_log_flat_buf(buf, len + BLE_HCI_CMD_HDR_LEN); + BLE_HS_LOG(DEBUG, "\n"); + rc = ble_hs_hci_cmd_transport(buf); + + if (rc == 0) { + STATS_INC(ble_hs_stats, hci_cmd); + } else { + BLE_HS_LOG(DEBUG, "ble_hs_hci_cmd_send failure; rc=%d\n", rc); + } + + return rc; +} + +int +ble_hs_hci_cmd_send_buf(void *buf) +{ + uint16_t opcode; + uint8_t *u8ptr; + uint8_t len; + int rc; + + switch (ble_hs_sync_state) { + case BLE_HS_SYNC_STATE_BAD: + return BLE_HS_ENOTSYNCED; + + case BLE_HS_SYNC_STATE_BRINGUP: + if (!ble_hs_is_parent_task()) { + return BLE_HS_ENOTSYNCED; + } + break; + + case BLE_HS_SYNC_STATE_GOOD: + break; + + default: + BLE_HS_DBG_ASSERT(0); + return BLE_HS_EUNKNOWN; + } + + u8ptr = buf; + + opcode = le16toh(u8ptr + 0); + len = u8ptr[2]; + + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF(opcode), BLE_HCI_OCF(opcode), len, + u8ptr + BLE_HCI_CMD_HDR_LEN); + return rc; +} + + +/** + * Send a LE command from the host to the controller. + * + * @param ocf + * @param len + * @param cmddata + * + * @return int + */ +static int +ble_hs_hci_cmd_le_send(uint16_t ocf, uint8_t len, void *cmddata) +{ + int rc; + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF_LE, ocf, len, cmddata); + return rc; +} + +/** + * Read BD_ADDR + * + * OGF = 0x04 (Informational parameters) + * OCF = 0x0009 + */ +void +ble_hs_hci_cmd_build_read_bd_addr(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_INFO_PARAMS, + BLE_HCI_OCF_IP_RD_BD_ADDR, + 0, dst); +} + +static int +ble_hs_hci_cmd_body_le_whitelist_chg(const uint8_t *addr, uint8_t addr_type, + uint8_t *dst) +{ + if (addr_type > BLE_ADDR_TYPE_RANDOM) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = addr_type; + memcpy(dst + 1, addr, BLE_DEV_ADDR_LEN); + + return 0; +} + +static int +ble_hs_hci_cmd_body_le_set_adv_params(const struct hci_adv_params *adv, + uint8_t *dst) +{ + uint16_t itvl; + + BLE_HS_DBG_ASSERT(adv != NULL); + + /* Make sure parameters are valid */ + if ((adv->adv_itvl_min > adv->adv_itvl_max) || + (adv->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) || + (adv->peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX) || + (adv->adv_filter_policy > BLE_HCI_ADV_FILT_MAX) || + (adv->adv_type > BLE_HCI_ADV_TYPE_MAX) || + (adv->adv_channel_map == 0) || + ((adv->adv_channel_map & 0xF8) != 0)) { + /* These parameters are not valid */ + return -1; + } + + /* Make sure interval is valid for advertising type. */ + if ((adv->adv_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) || + (adv->adv_type == BLE_HCI_ADV_TYPE_ADV_SCAN_IND)) { + itvl = BLE_HCI_ADV_ITVL_NONCONN_MIN; + } else { + itvl = BLE_HCI_ADV_ITVL_MIN; + } + + /* Do not check if high duty-cycle directed */ + if (adv->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { + if ((adv->adv_itvl_min < itvl) || + (adv->adv_itvl_min > BLE_HCI_ADV_ITVL_MAX)) { + return -1; + } + } + + htole16(dst, adv->adv_itvl_min); + htole16(dst + 2, adv->adv_itvl_max); + dst[4] = adv->adv_type; + dst[5] = adv->own_addr_type; + dst[6] = adv->peer_addr_type; + memcpy(dst + 7, adv->peer_addr, BLE_DEV_ADDR_LEN); + dst[13] = adv->adv_channel_map; + dst[14] = adv->adv_filter_policy; + + return 0; +} + +int +ble_hs_hci_cmd_build_le_set_adv_params(const struct hci_adv_params *adv, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_PARAM_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_PARAMS, + BLE_HCI_SET_ADV_PARAM_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_set_adv_params(adv, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +/** + * Set advertising data + * + * OGF = 0x08 (LE) + * OCF = 0x0008 + * + * @param data + * @param len + * @param dst + * + * @return int + */ +static int +ble_hs_hci_cmd_body_le_set_adv_data(const uint8_t *data, uint8_t len, + uint8_t *dst) +{ + /* Check for valid parameters */ + if (((data == NULL) && (len != 0)) || (len > BLE_HCI_MAX_ADV_DATA_LEN)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + memset(dst, 0, BLE_HCI_SET_ADV_DATA_LEN); + dst[0] = len; + memcpy(dst + 1, data, len); + + return 0; +} + +/** + * Set advertising data + * + * OGF = 0x08 (LE) + * OCF = 0x0008 + * + * @param data + * @param len + * @param dst + * + * @return int + */ +int +ble_hs_hci_cmd_build_le_set_adv_data(const uint8_t *data, uint8_t len, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_DATA_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_DATA, + BLE_HCI_SET_ADV_DATA_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_set_adv_data(data, len, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_hs_hci_cmd_body_le_set_scan_rsp_data(const uint8_t *data, uint8_t len, + uint8_t *dst) +{ + /* Check for valid parameters */ + if (((data == NULL) && (len != 0)) || + (len > BLE_HCI_MAX_SCAN_RSP_DATA_LEN)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + memset(dst, 0, BLE_HCI_SET_SCAN_RSP_DATA_LEN); + dst[0] = len; + memcpy(dst + 1, data, len); + + return 0; +} + +int +ble_hs_hci_cmd_build_le_set_scan_rsp_data(const uint8_t *data, uint8_t len, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_RSP_DATA_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA, + BLE_HCI_SET_SCAN_RSP_DATA_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_set_scan_rsp_data(data, len, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +ble_hs_hci_cmd_body_set_event_mask(uint64_t event_mask, uint8_t *dst) +{ + htole64(dst, event_mask); +} + +void +ble_hs_hci_cmd_build_set_event_mask(uint64_t event_mask, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_EVENT_MASK_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND, + BLE_HCI_OCF_CB_SET_EVENT_MASK, + BLE_HCI_SET_EVENT_MASK_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_set_event_mask(event_mask, dst); +} + +void +ble_hs_hci_cmd_build_set_event_mask2(uint64_t event_mask, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_EVENT_MASK_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND, + BLE_HCI_OCF_CB_SET_EVENT_MASK2, + BLE_HCI_SET_EVENT_MASK_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_set_event_mask(event_mask, dst); +} + +static void +ble_hs_hci_cmd_body_disconnect(uint16_t handle, uint8_t reason, uint8_t *dst) +{ + htole16(dst + 0, handle); + dst[2] = reason; +} + +void +ble_hs_hci_cmd_build_disconnect(uint16_t handle, uint8_t reason, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_DISCONNECT_CMD_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LINK_CTRL, BLE_HCI_OCF_DISCONNECT_CMD, + BLE_HCI_DISCONNECT_CMD_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_disconnect(handle, reason, dst); +} + +int +ble_hs_hci_cmd_disconnect(uint16_t handle, uint8_t reason) +{ + uint8_t cmd[BLE_HCI_DISCONNECT_CMD_LEN]; + int rc; + + ble_hs_hci_cmd_body_disconnect(handle, reason, cmd); + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF_LINK_CTRL, + BLE_HCI_OCF_DISCONNECT_CMD, + BLE_HCI_DISCONNECT_CMD_LEN, + cmd); + return rc; +} + +static void +ble_hs_hci_cmd_body_le_set_event_mask(uint64_t event_mask, uint8_t *dst) +{ + htole64(dst, event_mask); +} + +void +ble_hs_hci_cmd_build_le_set_event_mask(uint64_t event_mask, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_LE_EVENT_MASK_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_SET_EVENT_MASK, + BLE_HCI_SET_LE_EVENT_MASK_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_set_event_mask(event_mask, dst); +} + +/** + * LE Read buffer size + * + * OGF = 0x08 (LE) + * OCF = 0x0002 + * + * @return int + */ +void +ble_hs_hci_cmd_build_le_read_buffer_size(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_BUF_SIZE, + 0, dst); +} + +/** + * LE Read buffer size + * + * OGF = 0x08 (LE) + * OCF = 0x0002 + * + * @return int + */ +int +ble_hs_hci_cmd_le_read_buffer_size(void) +{ + int rc; + + rc = ble_hs_hci_cmd_le_send(BLE_HCI_OCF_LE_RD_BUF_SIZE, 0, NULL); + return rc; +} + +/** + * OGF=LE, OCF=0x0003 + */ +void +ble_hs_hci_cmd_build_le_read_loc_supp_feat(uint8_t *dst, uint8_t dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT, + 0, dst); +} + +static void +ble_hs_hci_cmd_body_le_set_adv_enable(uint8_t enable, uint8_t *dst) +{ + dst[0] = enable; +} + +void +ble_hs_hci_cmd_build_le_set_adv_enable(uint8_t enable, uint8_t *dst, + int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADV_ENABLE_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_ENABLE, + BLE_HCI_SET_ADV_ENABLE_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_set_adv_enable(enable, dst); +} + +static int +ble_hs_hci_cmd_body_le_set_scan_params( + uint8_t scan_type, uint16_t scan_itvl, uint16_t scan_window, + uint8_t own_addr_type, uint8_t filter_policy, uint8_t *dst) { + + /* Make sure parameters are valid */ + if ((scan_type != BLE_HCI_SCAN_TYPE_PASSIVE) && + (scan_type != BLE_HCI_SCAN_TYPE_ACTIVE)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check interval and window */ + if ((scan_itvl < BLE_HCI_SCAN_ITVL_MIN) || + (scan_itvl > BLE_HCI_SCAN_ITVL_MAX) || + (scan_window < BLE_HCI_SCAN_WINDOW_MIN) || + (scan_window > BLE_HCI_SCAN_WINDOW_MAX) || + (scan_itvl < scan_window)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check own addr type */ + if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check scanner filter policy */ + if (filter_policy > BLE_HCI_SCAN_FILT_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = scan_type; + htole16(dst + 1, scan_itvl); + htole16(dst + 3, scan_window); + dst[5] = own_addr_type; + dst[6] = filter_policy; + + return 0; +} + +int +ble_hs_hci_cmd_build_le_set_scan_params(uint8_t scan_type, + uint16_t scan_itvl, + uint16_t scan_window, + uint8_t own_addr_type, + uint8_t filter_policy, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_PARAM_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_PARAMS, + BLE_HCI_SET_SCAN_PARAM_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_set_scan_params(scan_type, scan_itvl, + scan_window, own_addr_type, + filter_policy, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +ble_hs_hci_cmd_body_le_set_scan_enable(uint8_t enable, uint8_t filter_dups, + uint8_t *dst) +{ + dst[0] = enable; + dst[1] = filter_dups; +} + +void +ble_hs_hci_cmd_build_le_set_scan_enable(uint8_t enable, uint8_t filter_dups, + uint8_t *dst, uint8_t dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_SCAN_ENABLE_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_ENABLE, + BLE_HCI_SET_SCAN_ENABLE_LEN, dst); + + ble_hs_hci_cmd_body_le_set_scan_enable(enable, filter_dups, + dst + BLE_HCI_CMD_HDR_LEN); +} + +static int +ble_hs_hci_cmd_body_le_create_connection(const struct hci_create_conn *hcc, + uint8_t *cmd) +{ + /* Check scan interval and scan window */ + if ((hcc->scan_itvl < BLE_HCI_SCAN_ITVL_MIN) || + (hcc->scan_itvl > BLE_HCI_SCAN_ITVL_MAX) || + (hcc->scan_window < BLE_HCI_SCAN_WINDOW_MIN) || + (hcc->scan_window > BLE_HCI_SCAN_WINDOW_MAX) || + (hcc->scan_itvl < hcc->scan_window)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check initiator filter policy */ + if (hcc->filter_policy > BLE_HCI_CONN_FILT_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check peer addr type */ + if (hcc->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check own addr type */ + if (hcc->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection interval min */ + if ((hcc->conn_itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (hcc->conn_itvl_min > BLE_HCI_CONN_ITVL_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection interval max */ + if ((hcc->conn_itvl_max < BLE_HCI_CONN_ITVL_MIN) || + (hcc->conn_itvl_max > BLE_HCI_CONN_ITVL_MAX) || + (hcc->conn_itvl_max < hcc->conn_itvl_min)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection latency */ + if ((hcc->conn_latency < BLE_HCI_CONN_LATENCY_MIN) || + (hcc->conn_latency > BLE_HCI_CONN_LATENCY_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check supervision timeout */ + if ((hcc->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (hcc->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection event length */ + if (hcc->min_ce_len > hcc->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + htole16(cmd + 0, hcc->scan_itvl); + htole16(cmd + 2, hcc->scan_window); + cmd[4] = hcc->filter_policy; + cmd[5] = hcc->peer_addr_type; + memcpy(cmd + 6, hcc->peer_addr, BLE_DEV_ADDR_LEN); + cmd[12] = hcc->own_addr_type; + htole16(cmd + 13, hcc->conn_itvl_min); + htole16(cmd + 15, hcc->conn_itvl_max); + htole16(cmd + 17, hcc->conn_latency); + htole16(cmd + 19, hcc->supervision_timeout); + htole16(cmd + 21, hcc->min_ce_len); + htole16(cmd + 23, hcc->max_ce_len); + + return 0; +} + +int +ble_hs_hci_cmd_build_le_create_connection(const struct hci_create_conn *hcc, + uint8_t *cmd, int cmd_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + cmd_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CREATE_CONN_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN, + BLE_HCI_CREATE_CONN_LEN, cmd); + + rc = ble_hs_hci_cmd_body_le_create_connection(hcc, + cmd + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + + return 0; +} + +void +ble_hs_hci_cmd_build_le_clear_whitelist(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLEAR_WHITE_LIST, + 0, dst); +} + +int +ble_hs_hci_cmd_build_le_add_to_whitelist(const uint8_t *addr, + uint8_t addr_type, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CHG_WHITE_LIST_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_WHITE_LIST, + BLE_HCI_CHG_WHITE_LIST_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_whitelist_chg(addr, addr_type, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +void +ble_hs_hci_cmd_build_reset(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_RESET, + 0, dst); +} + +/** + * Reset the controller and link manager. + * + * @return int + */ +int +ble_hs_hci_cmd_reset(void) +{ + int rc; + + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_RESET, + 0, NULL); + return rc; +} + +void +ble_hs_hci_cmd_build_read_adv_pwr(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR, + 0, dst); +} + +/** + * Read the transmit power level used for LE advertising channel packets. + * + * @return int + */ +int +ble_hs_hci_cmd_read_adv_pwr(void) +{ + int rc; + + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR, + 0, NULL); + return rc; +} + +void +ble_hs_hci_cmd_build_le_create_conn_cancel(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN_CANCEL, + 0, dst); +} + +int +ble_hs_hci_cmd_le_create_conn_cancel(void) +{ + int rc; + + rc = ble_hs_hci_cmd_send(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN_CANCEL, + 0, NULL); + return rc; +} + +static int +ble_hs_hci_cmd_body_le_conn_update(const struct hci_conn_update *hcu, + uint8_t *dst) +{ + /* XXX: add parameter checking later */ + htole16(dst + 0, hcu->handle); + htole16(dst + 2, hcu->conn_itvl_min); + htole16(dst + 4, hcu->conn_itvl_max); + htole16(dst + 6, hcu->conn_latency); + htole16(dst + 8, hcu->supervision_timeout); + htole16(dst + 10, hcu->min_ce_len); + htole16(dst + 12, hcu->max_ce_len); + + return 0; +} + +int +ble_hs_hci_cmd_build_le_conn_update(const struct hci_conn_update *hcu, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_UPDATE_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CONN_UPDATE, + BLE_HCI_CONN_UPDATE_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_le_conn_update(hcu, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +ble_hs_hci_cmd_le_conn_update(const struct hci_conn_update *hcu) +{ + uint8_t cmd[BLE_HCI_CONN_UPDATE_LEN]; + int rc; + + rc = ble_hs_hci_cmd_body_le_conn_update(hcu, cmd); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_le_send(BLE_HCI_OCF_LE_CONN_UPDATE, + BLE_HCI_CONN_UPDATE_LEN, cmd); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +ble_hs_hci_cmd_body_le_lt_key_req_reply(const struct hci_lt_key_req_reply *hkr, + uint8_t *dst) +{ + htole16(dst + 0, hkr->conn_handle); + memcpy(dst + 2, hkr->long_term_key, sizeof hkr->long_term_key); +} + +/** + * Sends the long-term key (LTK) to the controller. + * + * Note: This function expects the 128-bit key to be in little-endian byte + * order. + * + * OGF = 0x08 (LE) + * OCF = 0x001a + * + * @param key + * @param pt + * + * @return int + */ +void +ble_hs_hci_cmd_build_le_lt_key_req_reply( + const struct hci_lt_key_req_reply *hkr, uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_REPLY_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY, + BLE_HCI_LT_KEY_REQ_REPLY_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_lt_key_req_reply(hkr, dst); +} + +void +ble_hs_hci_cmd_build_le_lt_key_req_neg_reply(uint16_t conn_handle, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_LT_KEY_REQ_NEG_REPLY, + BLE_HCI_LT_KEY_REQ_NEG_REPLY_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + htole16(dst + 0, conn_handle); +} + +static void +ble_hs_hci_cmd_body_le_conn_param_reply(const struct hci_conn_param_reply *hcr, + uint8_t *dst) +{ + htole16(dst + 0, hcr->handle); + htole16(dst + 2, hcr->conn_itvl_min); + htole16(dst + 4, hcr->conn_itvl_max); + htole16(dst + 6, hcr->conn_latency); + htole16(dst + 8, hcr->supervision_timeout); + htole16(dst + 10, hcr->min_ce_len); + htole16(dst + 12, hcr->max_ce_len); +} + +void +ble_hs_hci_cmd_build_le_conn_param_reply( + const struct hci_conn_param_reply *hcr, uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_PARAM_REPLY_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_RR, + BLE_HCI_CONN_PARAM_REPLY_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_conn_param_reply(hcr, dst); +} + +int +ble_hs_hci_cmd_le_conn_param_reply(const struct hci_conn_param_reply *hcr) +{ + uint8_t cmd[BLE_HCI_CONN_PARAM_REPLY_LEN]; + int rc; + + htole16(cmd + 0, hcr->handle); + htole16(cmd + 2, hcr->conn_itvl_min); + htole16(cmd + 4, hcr->conn_itvl_max); + htole16(cmd + 6, hcr->conn_latency); + htole16(cmd + 8, hcr->supervision_timeout); + htole16(cmd + 10, hcr->min_ce_len); + htole16(cmd + 12, hcr->max_ce_len); + + rc = ble_hs_hci_cmd_le_send(BLE_HCI_OCF_LE_REM_CONN_PARAM_RR, + BLE_HCI_CONN_PARAM_REPLY_LEN, cmd); + return rc; +} + +static void +ble_hs_hci_cmd_body_le_conn_param_neg_reply( + const struct hci_conn_param_neg_reply *hcn, uint8_t *dst) +{ + htole16(dst + 0, hcn->handle); + dst[2] = hcn->reason; +} + + +void +ble_hs_hci_cmd_build_le_conn_param_neg_reply( + const struct hci_conn_param_neg_reply *hcn, uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_PARAM_NEG_REPLY_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR, + BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_conn_param_neg_reply(hcn, dst); +} + +int +ble_hs_hci_cmd_le_conn_param_neg_reply( + const struct hci_conn_param_neg_reply *hcn) +{ + uint8_t cmd[BLE_HCI_CONN_PARAM_NEG_REPLY_LEN]; + int rc; + + ble_hs_hci_cmd_body_le_conn_param_neg_reply(hcn, cmd); + + rc = ble_hs_hci_cmd_le_send(BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR, + BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, cmd); + return rc; +} + +/** + * Get random data + * + * OGF = 0x08 (LE) + * OCF = 0x0018 + * + * @return int + */ +void +ble_hs_hci_cmd_build_le_rand(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RAND, 0, dst); +} + +static void +ble_hs_hci_cmd_body_le_start_encrypt(const struct hci_start_encrypt *cmd, + uint8_t *dst) +{ + htole16(dst + 0, cmd->connection_handle); + htole64(dst + 2, cmd->random_number); + htole16(dst + 10, cmd->encrypted_diversifier); + memcpy(dst + 12, cmd->long_term_key, sizeof cmd->long_term_key); +} + +/* + * OGF=0x08 OCF=0x0019 + */ +void +ble_hs_hci_cmd_build_le_start_encrypt(const struct hci_start_encrypt *cmd, + uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_START_ENCRYPT_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_START_ENCRYPT, + BLE_HCI_LE_START_ENCRYPT_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_le_start_encrypt(cmd, dst); +} + +/** + * Read the RSSI for a given connection handle + * + * NOTE: OGF=0x05 OCF=0x0005 + * + * @param handle + * + * @return int + */ +static void +ble_hs_hci_cmd_body_read_rssi(uint16_t handle, uint8_t *dst) +{ + htole16(dst, handle); +} + +void +ble_hs_hci_cmd_build_read_rssi(uint16_t handle, uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_READ_RSSI_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_STATUS_PARAMS, BLE_HCI_OCF_RD_RSSI, + BLE_HCI_READ_RSSI_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + ble_hs_hci_cmd_body_read_rssi(handle, dst); +} + +static int +ble_hs_hci_cmd_body_set_data_len(uint16_t connection_handle, + uint16_t tx_octets, + uint16_t tx_time, uint8_t *dst) +{ + + 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; + } + + htole16(dst + 0, connection_handle); + htole16(dst + 2, tx_octets); + htole16(dst + 4, tx_time); + + return 0; +} + +/* + * OGF=0x08 OCF=0x0022 + */ +int +ble_hs_hci_cmd_build_set_data_len(uint16_t connection_handle, + uint16_t tx_octets, uint16_t tx_time, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_DATALEN_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_DATA_LEN, + BLE_HCI_SET_DATALEN_LEN, dst); + dst += BLE_HCI_CMD_HDR_LEN; + + rc = ble_hs_hci_cmd_body_set_data_len(connection_handle, tx_octets, + tx_time, dst); + if (rc != 0) { + return rc; + } + + return 0; +} + +/** + * IRKs are in little endian. + */ +static int +ble_hs_hci_cmd_body_add_to_resolv_list(uint8_t addr_type, const uint8_t *addr, + const uint8_t *peer_irk, + const uint8_t *local_irk, + uint8_t *dst) +{ + if (addr_type > BLE_ADDR_TYPE_RANDOM) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = addr_type; + memcpy(dst + 1, addr, BLE_DEV_ADDR_LEN); + memcpy(dst + 1 + 6, peer_irk , 16); + memcpy(dst + 1 + 6 + 16, local_irk , 16); + /* 16 + 16 + 6 + 1 == 39 */ + return 0; +} + +/** + * OGF=0x08 OCF=0x0027 + * + * IRKs are in little endian. + */ +int +ble_hs_hci_cmd_build_add_to_resolv_list( + const struct hci_add_dev_to_resolving_list *padd, + uint8_t *dst, + int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_ADD_TO_RESOLV_LIST_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST, + BLE_HCI_ADD_TO_RESOLV_LIST_LEN, dst); + + rc = ble_hs_hci_cmd_body_add_to_resolv_list( + padd->addr_type, padd->addr, padd->peer_irk, padd->local_irk, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_hs_hci_cmd_body_remove_from_resolv_list(uint8_t addr_type, + const uint8_t *addr, + uint8_t *dst) +{ + if (addr_type > BLE_ADDR_TYPE_RANDOM) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = addr_type; + memcpy(dst + 1, addr, BLE_DEV_ADDR_LEN); + return 0; +} + + +int +ble_hs_hci_cmd_build_remove_from_resolv_list(uint8_t addr_type, + const uint8_t *addr, + uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_RMV_FROM_RESOLV_LIST_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RMV_RESOLV_LIST, + BLE_HCI_RMV_FROM_RESOLV_LIST_LEN, dst); + + rc = ble_hs_hci_cmd_body_remove_from_resolv_list(addr_type, addr, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + return 0; +} + +int +ble_hs_hci_cmd_build_clear_resolv_list(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLR_RESOLV_LIST, + 0, dst); + + return 0; +} + +int +ble_hs_hci_cmd_build_read_resolv_list_size(uint8_t *dst, int dst_len) +{ + BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_RD_RESOLV_LIST_SIZE, + 0, dst); + + return 0; +} + +static int +ble_hs_hci_cmd_body_read_peer_resolv_addr(uint8_t peer_identity_addr_type, + const uint8_t *peer_identity_addr, + uint8_t *dst) +{ + if (peer_identity_addr_type > BLE_ADDR_TYPE_RANDOM) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = peer_identity_addr_type; + memcpy(dst + 1, peer_identity_addr, BLE_DEV_ADDR_LEN); + return 0; +} + +int +ble_hs_hci_cmd_build_read_peer_resolv_addr(uint8_t peer_identity_addr_type, + const uint8_t *peer_identity_addr, + uint8_t *dst, + int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_RD_PEER_RESOLV_ADDR_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_RD_PEER_RESOLV_ADDR, + BLE_HCI_RD_PEER_RESOLV_ADDR_LEN, dst); + + rc = ble_hs_hci_cmd_body_read_peer_resolv_addr(peer_identity_addr_type, + peer_identity_addr, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + return 0; +} + +static int +ble_hs_hci_cmd_body_read_lcl_resolv_addr( + uint8_t local_identity_addr_type, + const uint8_t *local_identity_addr, + uint8_t *dst) +{ + if (local_identity_addr_type > BLE_ADDR_TYPE_RANDOM) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = local_identity_addr_type; + memcpy(dst + 1, local_identity_addr, BLE_DEV_ADDR_LEN); + return 0; +} + +/* + * OGF=0x08 OCF=0x002c + */ +int +ble_hs_hci_cmd_build_read_lcl_resolv_addr(uint8_t local_identity_addr_type, + const uint8_t *local_identity_addr, + uint8_t *dst, + int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_RD_LOC_RESOLV_ADDR_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, + BLE_HCI_OCF_LE_RD_LOCAL_RESOLV_ADDR, + BLE_HCI_RD_LOC_RESOLV_ADDR_LEN, dst); + + rc = ble_hs_hci_cmd_body_read_lcl_resolv_addr(local_identity_addr_type, + local_identity_addr, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + return 0; +} + +static int +ble_hs_hci_cmd_body_set_addr_res_en(uint8_t enable, uint8_t *dst) +{ + if (enable > 1) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + dst[0] = enable; + return 0; +} + +/* + * OGF=0x08 OCF=0x002d + */ +int +ble_hs_hci_cmd_build_set_addr_res_en(uint8_t enable, uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_ADDR_RESOL_ENA_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADDR_RES_EN, + BLE_HCI_SET_ADDR_RESOL_ENA_LEN, dst); + + rc = ble_hs_hci_cmd_body_set_addr_res_en(enable, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + return 0; +} + +static int +ble_hs_hci_cmd_body_set_resolv_priv_addr_timeout(uint16_t timeout, + uint8_t *dst) +{ + if (timeout == 0 || timeout > 0xA1B8) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + htole16(dst, timeout); + return 0; +} + +/* + * OGF=0x08 OCF=0x002e + */ +int +ble_hs_hci_cmd_build_set_resolv_priv_addr_timeout( + uint16_t timeout, uint8_t *dst, int dst_len) +{ + int rc; + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_RESOLV_PRIV_ADDR_TO_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_RPA_TMO, + BLE_HCI_SET_RESOLV_PRIV_ADDR_TO_LEN, dst); + + rc = ble_hs_hci_cmd_body_set_resolv_priv_addr_timeout( + timeout, dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + return 0; +} + +static int +ble_hs_hci_cmd_body_set_random_addr(const struct hci_rand_addr *paddr, + uint8_t *dst) +{ + memcpy(dst, paddr->addr, BLE_DEV_ADDR_LEN); + return 0; +} + +int +ble_hs_hci_cmd_build_set_random_addr(const uint8_t *addr, + uint8_t *dst, int dst_len) +{ + struct hci_rand_addr r_addr; + int rc; + + memcpy(r_addr.addr, addr, sizeof(r_addr.addr)); + + BLE_HS_DBG_ASSERT( + dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_RAND_ADDR_LEN); + + ble_hs_hci_cmd_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_RAND_ADDR, + BLE_HCI_SET_RAND_ADDR_LEN, dst); + + rc = ble_hs_hci_cmd_body_set_random_addr(&r_addr, + dst + BLE_HCI_CMD_HDR_LEN); + if (rc != 0) { + return rc; + } + + return 0; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_hci_evt.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_hci_evt.c b/net/nimble/host/src/ble_hs_hci_evt.c new file mode 100644 index 0000000..3d382dc --- /dev/null +++ b/net/nimble/host/src/ble_hs_hci_evt.c @@ -0,0 +1,677 @@ +/** + * 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 +#include +#include +#include +#include "os/os.h" +#include "console/console.h" +#include "nimble/hci_common.h" +#include "nimble/ble_hci_trans.h" +#include "host/ble_gap.h" +#include "ble_hs_priv.h" +#include "ble_hs_dbg_priv.h" + +_Static_assert(sizeof (struct hci_data_hdr) == BLE_HCI_DATA_HDR_SZ, + "struct hci_data_hdr must be 4 bytes"); + +typedef int ble_hs_hci_evt_fn(uint8_t event_code, uint8_t *data, int len); +static ble_hs_hci_evt_fn ble_hs_hci_evt_disconn_complete; +static ble_hs_hci_evt_fn ble_hs_hci_evt_encrypt_change; +static ble_hs_hci_evt_fn ble_hs_hci_evt_hw_error; +static ble_hs_hci_evt_fn ble_hs_hci_evt_num_completed_pkts; +static ble_hs_hci_evt_fn ble_hs_hci_evt_enc_key_refresh; +static ble_hs_hci_evt_fn ble_hs_hci_evt_le_meta; + +typedef int ble_hs_hci_evt_le_fn(uint8_t subevent, uint8_t *data, int len); +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_conn_complete; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_adv_rpt; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_conn_upd_complete; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_lt_key_req; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_conn_parm_req; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_dir_adv_rpt; + +/* Statistics */ +struct host_hci_stats +{ + uint32_t events_rxd; + uint32_t good_acks_rxd; + uint32_t bad_acks_rxd; + uint32_t unknown_events_rxd; +}; + +#define BLE_HS_HCI_EVT_TIMEOUT 50 /* Milliseconds. */ + +/** Dispatch table for incoming HCI events. Sorted by event code field. */ +struct ble_hs_hci_evt_dispatch_entry { + uint8_t event_code; + ble_hs_hci_evt_fn *cb; +}; + +static const struct ble_hs_hci_evt_dispatch_entry ble_hs_hci_evt_dispatch[] = { + { BLE_HCI_EVCODE_DISCONN_CMP, ble_hs_hci_evt_disconn_complete }, + { BLE_HCI_EVCODE_ENCRYPT_CHG, ble_hs_hci_evt_encrypt_change }, + { BLE_HCI_EVCODE_HW_ERROR, ble_hs_hci_evt_hw_error }, + { BLE_HCI_EVCODE_NUM_COMP_PKTS, ble_hs_hci_evt_num_completed_pkts }, + { BLE_HCI_EVCODE_ENC_KEY_REFRESH, ble_hs_hci_evt_enc_key_refresh }, + { BLE_HCI_EVCODE_LE_META, ble_hs_hci_evt_le_meta }, +}; + +#define BLE_HS_HCI_EVT_DISPATCH_SZ \ + (sizeof ble_hs_hci_evt_dispatch / sizeof ble_hs_hci_evt_dispatch[0]) + +/** Dispatch table for incoming LE meta events. Sorted by subevent field. */ +struct ble_hs_hci_evt_le_dispatch_entry { + uint8_t subevent; + ble_hs_hci_evt_le_fn *cb; +}; + +static const struct ble_hs_hci_evt_le_dispatch_entry + ble_hs_hci_evt_le_dispatch[] = { + { BLE_HCI_LE_SUBEV_CONN_COMPLETE, ble_hs_hci_evt_le_conn_complete }, + { BLE_HCI_LE_SUBEV_ADV_RPT, ble_hs_hci_evt_le_adv_rpt }, + { BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE, + ble_hs_hci_evt_le_conn_upd_complete }, + { BLE_HCI_LE_SUBEV_LT_KEY_REQ, ble_hs_hci_evt_le_lt_key_req }, + { BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ, ble_hs_hci_evt_le_conn_parm_req }, + { BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE, ble_hs_hci_evt_le_conn_complete }, + { BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT, ble_hs_hci_evt_le_dir_adv_rpt }, +}; + +#define BLE_HS_HCI_EVT_LE_DISPATCH_SZ \ + (sizeof ble_hs_hci_evt_le_dispatch / sizeof ble_hs_hci_evt_le_dispatch[0]) + +static const struct ble_hs_hci_evt_dispatch_entry * +ble_hs_hci_evt_dispatch_find(uint8_t event_code) +{ + const struct ble_hs_hci_evt_dispatch_entry *entry; + int i; + + for (i = 0; i < BLE_HS_HCI_EVT_DISPATCH_SZ; i++) { + entry = ble_hs_hci_evt_dispatch + i; + if (entry->event_code == event_code) { + return entry; + } + } + + return NULL; +} + +static const struct ble_hs_hci_evt_le_dispatch_entry * +ble_hs_hci_evt_le_dispatch_find(uint8_t event_code) +{ + const struct ble_hs_hci_evt_le_dispatch_entry *entry; + int i; + + for (i = 0; i < BLE_HS_HCI_EVT_LE_DISPATCH_SZ; i++) { + entry = ble_hs_hci_evt_le_dispatch + i; + if (entry->subevent == event_code) { + return entry; + } + } + + return NULL; +} + +static int +ble_hs_hci_evt_disconn_complete(uint8_t event_code, uint8_t *data, int len) +{ + struct hci_disconn_complete evt; + + if (len < BLE_HCI_EVENT_DISCONN_COMPLETE_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.status = data[2]; + evt.connection_handle = le16toh(data + 3); + evt.reason = data[5]; + + ble_gap_rx_disconn_complete(&evt); + + return 0; +} + +static int +ble_hs_hci_evt_encrypt_change(uint8_t event_code, uint8_t *data, int len) +{ + struct hci_encrypt_change evt; + + if (len < BLE_HCI_EVENT_ENCRYPT_CHG_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.status = data[2]; + evt.connection_handle = le16toh(data + 3); + evt.encryption_enabled = data[5]; + + ble_sm_enc_change_rx(&evt); + + return 0; +} + +static int +ble_hs_hci_evt_hw_error(uint8_t event_code, uint8_t *data, int len) +{ + uint8_t hw_code; + + if (len < BLE_HCI_EVENT_HW_ERROR_LEN) { + return BLE_HS_ECONTROLLER; + } + + hw_code = data[0]; + ble_hs_hw_error(hw_code); + + return 0; +} + +static int +ble_hs_hci_evt_enc_key_refresh(uint8_t event_code, uint8_t *data, int len) +{ + struct hci_encrypt_key_refresh evt; + + if (len < BLE_HCI_EVENT_ENC_KEY_REFRESH_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.status = data[2]; + evt.connection_handle = le16toh(data + 3); + + ble_sm_enc_key_refresh_rx(&evt); + + return 0; +} + +static int +ble_hs_hci_evt_num_completed_pkts(uint8_t event_code, uint8_t *data, int len) +{ + uint16_t num_pkts; + uint16_t handle; + uint8_t num_handles; + int off; + int i; + + if (len < BLE_HCI_EVENT_HDR_LEN + BLE_HCI_EVENT_NUM_COMP_PKTS_HDR_LEN) { + return BLE_HS_ECONTROLLER; + } + + off = BLE_HCI_EVENT_HDR_LEN; + num_handles = data[off]; + if (len < BLE_HCI_EVENT_NUM_COMP_PKTS_HDR_LEN + + num_handles * BLE_HCI_EVENT_NUM_COMP_PKTS_ENT_LEN) { + return BLE_HS_ECONTROLLER; + } + off++; + + for (i = 0; i < num_handles; i++) { + handle = le16toh(data + off + 2 * i); + num_pkts = le16toh(data + off + 2 * num_handles + 2 * i); + + /* XXX: Do something with these values. */ + (void)handle; + (void)num_pkts; + } + + return 0; +} + +static int +ble_hs_hci_evt_le_meta(uint8_t event_code, uint8_t *data, int len) +{ + const struct ble_hs_hci_evt_le_dispatch_entry *entry; + uint8_t subevent; + int rc; + + if (len < BLE_HCI_EVENT_HDR_LEN + BLE_HCI_LE_MIN_LEN) { + return BLE_HS_ECONTROLLER; + } + + subevent = data[2]; + entry = ble_hs_hci_evt_le_dispatch_find(subevent); + if (entry != NULL) { + rc = entry->cb(subevent, data + BLE_HCI_EVENT_HDR_LEN, + len - BLE_HCI_EVENT_HDR_LEN); + if (rc != 0) { + return rc; + } + } + + return 0; +} + +static int +ble_hs_hci_evt_le_conn_complete(uint8_t subevent, uint8_t *data, int len) +{ + struct hci_le_conn_complete evt; + int extended_offset = 0; + int rc; + + if (len < BLE_HCI_LE_CONN_COMPLETE_LEN) { + return BLE_HS_ECONTROLLER; + } + + /* this code processes two different events that are really similar */ + if ((subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE) && + ( len < BLE_HCI_LE_ENH_CONN_COMPLETE_LEN)) { + return BLE_HS_ECONTROLLER; + } + + evt.subevent_code = data[0]; + evt.status = data[1]; + evt.connection_handle = le16toh(data + 2); + evt.role = data[4]; + evt.peer_addr_type = data[5]; + memcpy(evt.peer_addr, data + 6, BLE_DEV_ADDR_LEN); + + /* enhanced connection event has the same information with these + * extra fields stuffed into the middle */ + if (subevent == BLE_HCI_LE_SUBEV_ENH_CONN_COMPLETE) { + memcpy(evt.local_rpa, data + 12, BLE_DEV_ADDR_LEN); + memcpy(evt.peer_rpa, data + 18, BLE_DEV_ADDR_LEN); + extended_offset = 12; + } else { + memset(evt.local_rpa, 0, BLE_DEV_ADDR_LEN); + memset(evt.peer_rpa, 0, BLE_DEV_ADDR_LEN); + } + + evt.conn_itvl = le16toh(data + 12 + extended_offset); + evt.conn_latency = le16toh(data + 14 + extended_offset); + evt.supervision_timeout = le16toh(data + 16 + extended_offset); + evt.master_clk_acc = data[18 + extended_offset]; + + if (evt.status == 0) { + if (evt.role != BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER && + evt.role != BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE) { + + return BLE_HS_EBADDATA; + } + } + + rc = ble_gap_rx_conn_complete(&evt); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_hs_hci_evt_le_adv_rpt_first_pass(uint8_t *data, int len, + uint8_t *out_num_reports, + int *out_rssi_off) +{ + uint8_t num_reports; + int data_len; + int off; + int i; + + if (len < BLE_HCI_LE_ADV_RPT_MIN_LEN) { + return BLE_HS_ECONTROLLER; + } + + num_reports = data[1]; + if (num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN || + num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX) { + + return BLE_HS_EBADDATA; + } + + off = 2 + /* Subevent code and num reports. */ + (1 + /* Event type. */ + 1 + /* Address type. */ + 6 /* Address. */ + ) * num_reports; + if (off + num_reports >= len) { + return BLE_HS_ECONTROLLER; + } + + data_len = 0; + for (i = 0; i < num_reports; i++) { + data_len += data[off]; + off++; + } + + off += data_len; + + /* Check if RSSI fields fit in the packet. */ + if (off + num_reports > len) { + return BLE_HS_ECONTROLLER; + } + + *out_num_reports = num_reports; + *out_rssi_off = off; + + return 0; +} + +static int +ble_hs_hci_evt_le_adv_rpt(uint8_t subevent, uint8_t *data, int len) +{ + struct ble_gap_disc_desc desc; + uint8_t num_reports; + int rssi_off; + int data_off; + int suboff; + int off; + int rc; + int i; + + rc = ble_hs_hci_evt_le_adv_rpt_first_pass(data, len, &num_reports, + &rssi_off); + if (rc != 0) { + return rc; + } + + /* Direct address fields not present in a standard advertising report. */ + desc.direct_addr_type = BLE_GAP_ADDR_TYPE_NONE; + memset(desc.direct_addr, 0, sizeof desc.direct_addr); + + data_off = 0; + for (i = 0; i < num_reports; i++) { + suboff = 0; + + off = 2 + suboff * num_reports + i; + desc.event_type = data[off]; + suboff++; + + off = 2 + suboff * num_reports + i; + desc.addr_type = data[off]; + suboff++; + + off = 2 + suboff * num_reports + i * 6; + memcpy(desc.addr, data + off, 6); + suboff += 6; + + off = 2 + suboff * num_reports + i; + desc.length_data = data[off]; + suboff++; + + off = 2 + suboff * num_reports + data_off; + desc.data = data + off; + data_off += desc.length_data; + + off = rssi_off + 1 * i; + desc.rssi = data[off]; + + ble_gap_rx_adv_report(&desc); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_dir_adv_rpt(uint8_t subevent, uint8_t *data, int len) +{ + struct ble_gap_disc_desc desc; + uint8_t num_reports; + int suboff; + int off; + int i; + + if (len < BLE_HCI_LE_ADV_DIRECT_RPT_LEN) { + return BLE_HS_ECONTROLLER; + } + + num_reports = data[1]; + if (len != 2 + num_reports * BLE_HCI_LE_ADV_DIRECT_RPT_SUB_LEN) { + return BLE_HS_ECONTROLLER; + } + + /* Data fields not present in a direct advertising report. */ + desc.data = NULL; + desc.fields = NULL; + + for (i = 0; i < num_reports; i++) { + suboff = 0; + + off = 2 + suboff * num_reports + i; + desc.event_type = data[off]; + suboff++; + + off = 2 + suboff * num_reports + i; + desc.addr_type = data[off]; + suboff++; + + off = 2 + suboff * num_reports + i * 6; + memcpy(desc.addr, data + off, 6); + suboff += 6; + + off = 2 + suboff * num_reports + i; + desc.direct_addr_type = data[off]; + suboff++; + + off = 2 + suboff * num_reports + i * 6; + memcpy(desc.direct_addr, data + off, 6); + suboff += 6; + + off = 2 + suboff * num_reports + i; + desc.rssi = data[off]; + suboff++; + + ble_gap_rx_adv_report(&desc); + } + + return 0; +} + +static int +ble_hs_hci_evt_le_conn_upd_complete(uint8_t subevent, uint8_t *data, int len) +{ + struct hci_le_conn_upd_complete evt; + + if (len < BLE_HCI_LE_CONN_UPD_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.subevent_code = data[0]; + evt.status = data[1]; + evt.connection_handle = le16toh(data + 2); + evt.conn_itvl = le16toh(data + 4); + evt.conn_latency = le16toh(data + 6); + evt.supervision_timeout = le16toh(data + 8); + + if (evt.status == 0) { + if (evt.conn_itvl < BLE_HCI_CONN_ITVL_MIN || + evt.conn_itvl > BLE_HCI_CONN_ITVL_MAX) { + + return BLE_HS_EBADDATA; + } + if (evt.conn_latency < BLE_HCI_CONN_LATENCY_MIN || + evt.conn_latency > BLE_HCI_CONN_LATENCY_MAX) { + + return BLE_HS_EBADDATA; + } + if (evt.supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN || + evt.supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX) { + + return BLE_HS_EBADDATA; + } + } + + ble_gap_rx_update_complete(&evt); + + return 0; +} + +static int +ble_hs_hci_evt_le_lt_key_req(uint8_t subevent, uint8_t *data, int len) +{ + struct hci_le_lt_key_req evt; + + if (len < BLE_HCI_LE_LT_KEY_REQ_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.subevent_code = data[0]; + evt.connection_handle = le16toh(data + 1); + evt.random_number = le64toh(data + 3); + evt.encrypted_diversifier = le16toh(data + 11); + + ble_sm_ltk_req_rx(&evt); + + return 0; +} + +static int +ble_hs_hci_evt_le_conn_parm_req(uint8_t subevent, uint8_t *data, int len) +{ + struct hci_le_conn_param_req evt; + + if (len < BLE_HCI_LE_REM_CONN_PARM_REQ_LEN) { + return BLE_HS_ECONTROLLER; + } + + evt.subevent_code = data[0]; + evt.connection_handle = le16toh(data + 1); + evt.itvl_min = le16toh(data + 3); + evt.itvl_max = le16toh(data + 5); + evt.latency = le16toh(data + 7); + evt.timeout = le16toh(data + 9); + + if (evt.itvl_min < BLE_HCI_CONN_ITVL_MIN || + evt.itvl_max > BLE_HCI_CONN_ITVL_MAX || + evt.itvl_min > evt.itvl_max) { + + return BLE_HS_EBADDATA; + } + if (evt.latency < BLE_HCI_CONN_LATENCY_MIN || + evt.latency > BLE_HCI_CONN_LATENCY_MAX) { + + return BLE_HS_EBADDATA; + } + if (evt.timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN || + evt.timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX) { + + return BLE_HS_EBADDATA; + } + + ble_gap_rx_param_req(&evt); + + return 0; +} + +int +ble_hs_hci_evt_process(uint8_t *data) +{ + const struct ble_hs_hci_evt_dispatch_entry *entry; + uint8_t event_code; + uint8_t param_len; + int event_len; + int rc; + + /* Count events received */ + STATS_INC(ble_hs_stats, hci_event); + + /* Display to console */ + ble_hs_dbg_event_disp(data); + + /* Process the event */ + event_code = data[0]; + param_len = data[1]; + + event_len = param_len + 2; + + entry = ble_hs_hci_evt_dispatch_find(event_code); + if (entry == NULL) { + STATS_INC(ble_hs_stats, hci_unknown_event); + rc = BLE_HS_ENOTSUP; + } else { + rc = entry->cb(event_code, data, event_len); + } + + ble_hci_trans_buf_free(data); + + return rc; +} + +/** + * Called when a data packet is received from the controller. This function + * consumes the supplied mbuf, regardless of the outcome. + * + * @param om The incoming data packet, beginning with the + * HCI ACL data header. + * + * @return 0 on success; nonzero on failure. + */ +int +ble_hs_hci_evt_acl_process(struct os_mbuf *om) +{ + struct hci_data_hdr hci_hdr; + struct ble_hs_conn *conn; + ble_l2cap_rx_fn *rx_cb; + struct os_mbuf *rx_buf; + uint16_t handle; + int rc; + + rc = ble_hs_hci_util_data_hdr_strip(om, &hci_hdr); + if (rc != 0) { + goto err; + } + +#if (BLETEST_THROUGHPUT_TEST == 0) + BLE_HS_LOG(DEBUG, "ble_hs_hci_evt_acl_process(): handle=%u pb=%x len=%u " + "data=", + BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc), + BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc), + hci_hdr.hdh_len); + ble_hs_log_mbuf(om); + BLE_HS_LOG(DEBUG, "\n"); +#endif + + if (hci_hdr.hdh_len != OS_MBUF_PKTHDR(om)->omp_len) { + rc = BLE_HS_EBADDATA; + goto err; + } + + handle = BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc); + + ble_hs_lock(); + + conn = ble_hs_conn_find(handle); + if (conn == NULL) { + rc = BLE_HS_ENOTCONN; + } else { + rc = ble_l2cap_rx(conn, &hci_hdr, om, &rx_cb, &rx_buf); + om = NULL; + } + + ble_hs_unlock(); + + switch (rc) { + case 0: + /* Final fragment received. */ + BLE_HS_DBG_ASSERT(rx_cb != NULL); + BLE_HS_DBG_ASSERT(rx_buf != NULL); + rc = rx_cb(handle, &rx_buf); + os_mbuf_free_chain(rx_buf); + break; + + case BLE_HS_EAGAIN: + /* More fragments on the way. */ + break; + + default: + goto err; + } + + return 0; + +err: + os_mbuf_free_chain(om); + return rc; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_hci_priv.h ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_hci_priv.h b/net/nimble/host/src/ble_hs_hci_priv.h new file mode 100644 index 0000000..7d2fb12 --- /dev/null +++ b/net/nimble/host/src/ble_hs_hci_priv.h @@ -0,0 +1,158 @@ +/** + * 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. + */ + +#ifndef H_BLE_HS_HCI_PRIV_ +#define H_BLE_HS_HCI_PRIV_ + +#include "nimble/hci_common.h" +struct ble_hs_conn; +struct os_mbuf; + +struct ble_hs_hci_ack { + int bha_status; /* A BLE_HS_E<...> error; NOT a naked HCI code. */ + uint8_t *bha_params; + int bha_params_len; + uint16_t bha_opcode; + uint8_t bha_hci_handle; +}; + +int ble_hs_hci_cmd_tx(void *cmd, void *evt_buf, uint8_t evt_buf_len, + uint8_t *out_evt_buf_len); +int ble_hs_hci_cmd_tx_empty_ack(void *cmd); +void ble_hs_hci_rx_ack(uint8_t *ack_ev); +void ble_hs_hci_init(void); + +#if PHONY_HCI_ACKS +typedef int ble_hs_hci_phony_ack_fn(uint8_t *ack, int ack_buf_len); +void ble_hs_hci_set_phony_ack_cb(ble_hs_hci_phony_ack_fn *cb); +#endif + +int ble_hs_hci_util_read_adv_tx_pwr(int8_t *out_pwr); +int ble_hs_hci_util_rand(void *dst, int len); +int ble_hs_hci_util_read_rssi(uint16_t conn_handle, int8_t *out_rssi); +int ble_hs_hci_util_set_random_addr(const uint8_t *addr); +int ble_hs_hci_util_set_data_len(uint16_t conn_handle, uint16_t tx_octets, + uint16_t tx_time); +int ble_hs_hci_util_data_hdr_strip(struct os_mbuf *om, + struct hci_data_hdr *out_hdr); + +int ble_hs_hci_evt_process(uint8_t *data); +uint16_t ble_hs_hci_util_opcode_join(uint8_t ogf, uint16_t ocf); +void ble_hs_hci_cmd_write_hdr(uint8_t ogf, uint8_t ocf, uint8_t len, + void *buf); +int ble_hs_hci_cmd_send(uint8_t ogf, uint8_t ocf, uint8_t len, + const void *cmddata); +int ble_hs_hci_cmd_send_buf(void *cmddata); +void ble_hs_hci_cmd_build_read_bd_addr(uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_set_event_mask(uint64_t event_mask, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_set_event_mask2(uint64_t event_mask, uint8_t *dst, + int dst_len); +void ble_hs_hci_cmd_build_disconnect(uint16_t handle, uint8_t reason, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_disconnect(uint16_t handle, uint8_t reason); +void ble_hs_hci_cmd_build_read_rssi(uint16_t handle, uint8_t *dst, + int dst_len); +int ble_hs_hci_cmd_read_rssi(uint16_t handle); +int ble_hs_hci_cmd_build_le_set_scan_rsp_data(const uint8_t *data, uint8_t len, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_le_set_adv_data(const uint8_t *data, uint8_t len, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_le_set_adv_params(const struct hci_adv_params *adv, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_set_event_mask(uint64_t event_mask, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_read_buffer_size(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_le_read_buffer_size(void); +void ble_hs_hci_cmd_build_le_read_loc_supp_feat(uint8_t *dst, uint8_t dst_len); +void ble_hs_hci_cmd_build_le_set_adv_enable(uint8_t enable, uint8_t *dst, + int dst_len); +int ble_hs_hci_cmd_le_set_adv_enable(uint8_t enable); +int ble_hs_hci_cmd_build_le_set_scan_params(uint8_t scan_type, + uint16_t scan_itvl, + uint16_t scan_window, + uint8_t own_addr_type, + uint8_t filter_policy, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_set_scan_enable(uint8_t enable, + uint8_t filter_dups, + uint8_t *dst, uint8_t dst_len); +int ble_hs_hci_cmd_le_set_scan_enable(uint8_t enable, uint8_t filter_dups); +int ble_hs_hci_cmd_build_le_create_connection( + const struct hci_create_conn *hcc, uint8_t *cmd, int cmd_len); +int ble_hs_hci_cmd_le_create_connection(const struct hci_create_conn *hcc); +void ble_hs_hci_cmd_build_le_clear_whitelist(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_le_add_to_whitelist(const uint8_t *addr, + uint8_t addr_type, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_reset(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_reset(void); +void ble_hs_hci_cmd_build_read_adv_pwr(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_read_adv_pwr(void); +void ble_hs_hci_cmd_build_le_create_conn_cancel(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_le_create_conn_cancel(void); +int ble_hs_hci_cmd_build_le_conn_update(const struct hci_conn_update *hcu, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_le_conn_update(const struct hci_conn_update *hcu); +void ble_hs_hci_cmd_build_le_lt_key_req_reply( + const struct hci_lt_key_req_reply *hkr, uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_lt_key_req_neg_reply(uint16_t conn_handle, + uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_conn_param_reply( + const struct hci_conn_param_reply *hcr, uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_le_conn_param_reply(const struct hci_conn_param_reply *hcr); +void ble_hs_hci_cmd_build_le_conn_param_neg_reply( + const struct hci_conn_param_neg_reply *hcn, uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_le_conn_param_neg_reply( + const struct hci_conn_param_neg_reply *hcn); +void ble_hs_hci_cmd_build_le_rand(uint8_t *dst, int dst_len); +void ble_hs_hci_cmd_build_le_start_encrypt(const struct hci_start_encrypt *cmd, + uint8_t *dst, int dst_len); +int ble_hs_hci_set_buf_sz(uint16_t pktlen, uint8_t max_pkts); + +uint16_t ble_hs_hci_util_handle_pb_bc_join(uint16_t handle, uint8_t pb, + uint8_t bc); + +int ble_hs_hci_acl_tx(struct ble_hs_conn *connection, struct os_mbuf *txom); + +int ble_hs_hci_cmd_build_set_data_len(uint16_t connection_handle, + uint16_t tx_octets, uint16_t tx_time, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_add_to_resolv_list( + const struct hci_add_dev_to_resolving_list *padd, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_remove_from_resolv_list( + uint8_t addr_type, const uint8_t *addr, uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_read_resolv_list_size(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_clear_resolv_list(uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_read_peer_resolv_addr( + uint8_t peer_identity_addr_type, const uint8_t *peer_identity_addr, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_read_lcl_resolv_addr( + uint8_t local_identity_addr_type, const uint8_t *local_identity_addr, + uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_set_addr_res_en( + uint8_t enable, uint8_t *dst, int dst_len); +int ble_hs_hci_cmd_build_set_resolv_priv_addr_timeout( + uint16_t timeout, uint8_t *dst, int dst_len); + +int ble_hs_hci_cmd_build_set_random_addr(const uint8_t *addr, + uint8_t *dst, int dst_len); + +#endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_hci_util.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_hci_util.c b/net/nimble/host/src/ble_hs_hci_util.c new file mode 100644 index 0000000..0d9f625 --- /dev/null +++ b/net/nimble/host/src/ble_hs_hci_util.c @@ -0,0 +1,196 @@ +/** + * 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 +#include "nimble/hci_common.h" +#include "ble_hs_priv.h" + +uint16_t +ble_hs_hci_util_opcode_join(uint8_t ogf, uint16_t ocf) +{ + return (ogf << 10) | ocf; +} + +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) +{ + uint8_t buf[BLE_HCI_CMD_HDR_LEN]; + uint8_t params_len; + int rc; + + ble_hs_hci_cmd_build_read_adv_pwr(buf, sizeof buf); + rc = ble_hs_hci_cmd_tx(buf, out_tx_pwr, 1, ¶ms_len); + if (rc != 0) { + return rc; + } + + if (params_len != 1 || + *out_tx_pwr < BLE_HCI_ADV_CHAN_TXPWR_MIN || + *out_tx_pwr > BLE_HCI_ADV_CHAN_TXPWR_MAX) { + + return BLE_HS_ECONTROLLER; + } + + return 0; +} + +int +ble_hs_hci_util_rand(void *dst, int len) +{ + uint8_t rsp_buf[BLE_HCI_LE_RAND_LEN]; + uint8_t req_buf[BLE_HCI_CMD_HDR_LEN]; + uint8_t params_len; + uint8_t *u8ptr; + int chunk_sz; + int rc; + + ble_hs_hci_cmd_build_le_rand(req_buf, sizeof req_buf); + + u8ptr = dst; + while (len > 0) { + rc = ble_hs_hci_cmd_tx(req_buf, rsp_buf, sizeof rsp_buf, ¶ms_len); + if (rc != 0) { + return rc; + } + if (params_len != sizeof rsp_buf) { + return BLE_HS_ECONTROLLER; + } + + chunk_sz = min(len, sizeof rsp_buf); + memcpy(u8ptr, rsp_buf, 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) +{ + uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_READ_RSSI_LEN]; + uint8_t params[BLE_HCI_READ_RSSI_ACK_PARAM_LEN]; + uint16_t params_conn_handle; + uint8_t params_len; + int rc; + + ble_hs_hci_cmd_build_read_rssi(conn_handle, buf, sizeof buf); + rc = ble_hs_hci_cmd_tx(buf, params, sizeof params, ¶ms_len); + if (rc != 0) { + return rc; + } + + if (params_len != BLE_HCI_READ_RSSI_ACK_PARAM_LEN) { + return BLE_HS_ECONTROLLER; + } + + params_conn_handle = le16toh(params + 0); + if (params_conn_handle != conn_handle) { + return BLE_HS_ECONTROLLER; + } + + *out_rssi = params[2]; + + return 0; +} + +int +ble_hs_hci_util_set_random_addr(const uint8_t *addr) +{ + uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_RAND_ADDR_LEN]; + int rc; + + /* set the address in the controller */ + + rc = ble_hs_hci_cmd_build_set_random_addr(addr, buf, sizeof(buf)); + if (rc != 0) { + return rc; + } + + rc = ble_hs_hci_cmd_tx_empty_ack(buf); + return rc; +} + +int +ble_hs_hci_util_set_data_len(uint16_t conn_handle, uint16_t tx_octets, + uint16_t tx_time) +{ + + uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_DATALEN_LEN]; + uint8_t params[BLE_HCI_SET_DATALEN_ACK_PARAM_LEN]; + uint16_t params_conn_handle; + uint8_t params_len; + int rc; + + rc = ble_hs_hci_cmd_build_set_data_len(conn_handle, tx_octets, tx_time, + buf, sizeof buf); + if (rc != 0) { + return BLE_HS_HCI_ERR(rc); + } + + rc = ble_hs_hci_cmd_tx(buf, params, BLE_HCI_SET_DATALEN_ACK_PARAM_LEN, + ¶ms_len); + if (rc != 0) { + return rc; + } + + if (params_len != BLE_HCI_SET_DATALEN_ACK_PARAM_LEN) { + return BLE_HS_ECONTROLLER; + } + + params_conn_handle = le16toh(params + 0); + if (params_conn_handle != conn_handle) { + return BLE_HS_ECONTROLLER; + } + + return 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 = le16toh(&out_hdr->hdh_handle_pb_bc); + out_hdr->hdh_len = le16toh(&out_hdr->hdh_len); + + return 0; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_id.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_id.c b/net/nimble/host/src/ble_hs_id.c index 6d7df2b..9ef384b 100644 --- a/net/nimble/host/src/ble_hs_id.c +++ b/net/nimble/host/src/ble_hs_id.c @@ -50,7 +50,7 @@ ble_hs_id_gen_rnd(int nrpa, uint8_t *out_addr) { int rc; - rc = ble_hci_util_rand(out_addr, 6); + rc = ble_hs_hci_util_rand(out_addr, 6); if (rc != 0) { return rc; } @@ -91,7 +91,7 @@ ble_hs_id_set_rnd(const uint8_t *rnd_addr) goto done; } - rc = ble_hci_util_set_random_addr(rnd_addr); + rc = ble_hs_hci_util_set_random_addr(rnd_addr); if (rc != 0) { goto done; } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/093cae09/net/nimble/host/src/ble_hs_priv.h ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/ble_hs_priv.h b/net/nimble/host/src/ble_hs_priv.h index d8b5e82..d61f0ba 100644 --- a/net/nimble/host/src/ble_hs_priv.h +++ b/net/nimble/host/src/ble_hs_priv.h @@ -26,7 +26,8 @@ #include "ble_att_priv.h" #include "ble_gap_priv.h" #include "ble_gatt_priv.h" -#include "ble_hci_priv.h" +#include "ble_hs_dbg_priv.h" +#include "ble_hs_hci_priv.h" #include "ble_hs_atomic_priv.h" #include "ble_hs_conn_priv.h" #include "ble_hs_atomic_priv.h" @@ -82,8 +83,8 @@ int ble_hs_tx_data(struct os_mbuf *om); void ble_hs_enqueue_hci_event(uint8_t *hci_evt); void ble_hs_event_enqueue(struct os_event *ev); -int host_hci_evt_rx(uint8_t *hci_ev, void *arg); -int host_hci_acl_process(struct os_mbuf *om); +int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg); +int ble_hs_hci_evt_acl_process(struct os_mbuf *om); int ble_hs_misc_malloc_mempool(void **mem, struct os_mempool *pool, int num_entries, int entry_size, char *name);