MYNEWT-87: add channel map update procedure. This also fixes a big in the data channel selection index code that was not picking the correct channels if any channels were masked out
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/9e54d5f5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/9e54d5f5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/9e54d5f5 Branch: refs/heads/develop Commit: 9e54d5f5e076b6f39f6babc11aa20988c45c6455 Parents: f1e3970 Author: wes3 <w...@micosa.io> Authored: Wed Feb 17 16:34:25 2016 -0800 Committer: wes3 <w...@micosa.io> Committed: Wed Feb 17 17:01:03 2016 -0800 ---------------------------------------------------------------------- .../controller/include/controller/ble_ll.h | 2 +- .../controller/include/controller/ble_ll_conn.h | 5 +- net/nimble/controller/src/ble_ll.c | 1 - net/nimble/controller/src/ble_ll_conn.c | 104 ++++++++++---- net/nimble/controller/src/ble_ll_conn_hci.c | 75 +++++++++- net/nimble/controller/src/ble_ll_conn_priv.h | 23 +++- net/nimble/controller/src/ble_ll_ctrl.c | 92 +++++++++++-- net/nimble/controller/src/ble_ll_hci.c | 137 +++++++++---------- net/nimble/host/include/host/host_hci.h | 2 + net/nimble/host/src/host_dbg.c | 11 ++ net/nimble/host/src/host_hci_cmd.c | 36 +++++ net/nimble/include/nimble/hci_common.h | 15 ++ net/nimble/src/hci_common.c | 80 +++++++++++ project/bletest/src/main.c | 40 ++++-- 14 files changed, 493 insertions(+), 130 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/include/controller/ble_ll.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll.h b/net/nimble/controller/include/controller/ble_ll.h index 402f078..e9000ab 100644 --- a/net/nimble/controller/include/controller/ble_ll.h +++ b/net/nimble/controller/include/controller/ble_ll.h @@ -348,7 +348,7 @@ uint8_t ble_ll_read_supp_features(void); * XXX: temporary LL debug log. Will get removed once we transition to real * log */ -#undef BLE_LL_LOG +#define BLE_LL_LOG #define BLE_LL_LOG_ID_PHY_SETCHAN (1) #define BLE_LL_LOG_ID_RX_START (2) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/include/controller/ble_ll_conn.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_conn.h b/net/nimble/controller/include/controller/ble_ll_conn.h index 172d076..b1bceca 100644 --- a/net/nimble/controller/include/controller/ble_ll_conn.h +++ b/net/nimble/controller/include/controller/ble_ll_conn.h @@ -76,7 +76,8 @@ union ble_ll_conn_sm_flags { uint16_t host_expects_upd_event:1; uint16_t version_ind_sent:1; uint16_t rxd_version_ind:1; - uint16_t reserved:4; + uint16_t chanmap_update_scheduled:1; + uint16_t reserved:3; } cfbit; uint16_t conn_flags; } __attribute__((packed)); @@ -107,6 +108,8 @@ struct ble_ll_conn_sm /* Used to calculate data channel index for connection */ uint8_t chanmap[BLE_LL_CONN_CHMAP_LEN]; + uint8_t req_chanmap[BLE_LL_CONN_CHMAP_LEN]; + uint16_t chanmap_instant; uint8_t hop_inc; uint8_t data_chan_index; uint8_t unmapped_chan; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll.c b/net/nimble/controller/src/ble_ll.c index 687e79d..5896918 100644 --- a/net/nimble/controller/src/ble_ll.c +++ b/net/nimble/controller/src/ble_ll.c @@ -837,7 +837,6 @@ ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr) ble_hdr->txinfo.hdr_byte = hdr; } - /** * Called to reset the controller. This performs a "software reset" of the link * layer; it does not perform a HW reset of the controller nor does it reset http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll_conn.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn.c b/net/nimble/controller/src/ble_ll_conn.c index a1e2d03..7dd18ad 100644 --- a/net/nimble/controller/src/ble_ll_conn.c +++ b/net/nimble/controller/src/ble_ll_conn.c @@ -118,16 +118,6 @@ static const uint16_t g_ble_sca_ppm_tbl[8] = 500, 250, 150, 100, 75, 50, 30, 20 }; -/* Global Link Layer connection parameters */ -struct ble_ll_conn_global_params -{ - uint8_t supp_max_tx_octets; - uint8_t supp_max_rx_octets; - uint8_t conn_init_max_tx_octets; - uint16_t conn_init_max_tx_time; - uint16_t supp_max_tx_time; - uint16_t supp_max_rx_time; -}; struct ble_ll_conn_global_params g_ble_ll_conn_params; /* Pointer to connection state machine we are trying to create */ @@ -464,6 +454,7 @@ ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn) { int i; int j; + uint8_t chan; uint8_t curchan; uint8_t remap_index; uint8_t bitpos; @@ -490,6 +481,7 @@ ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn) /* NOTE: possible to build a map but this would use memory. For now, we just calculate */ /* Iterate through channel map to find this channel */ + chan = 0; cntr = 0; for (i = 0; i < BLE_LL_CONN_CHMAP_LEN; i++) { usable_chans = conn->chanmap[i]; @@ -498,13 +490,14 @@ ble_ll_conn_calc_dci(struct ble_ll_conn_sm *conn) for (j = 0; j < 8; j++) { if (usable_chans & mask) { if (cntr == remap_index) { - return cntr; + return (chan + j); } ++cntr; } mask <<= 1; } } + chan += 8; } } @@ -940,13 +933,10 @@ ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm, connsm->max_ce_len = hcc->max_ce_len; } - /* - * XXX: for now, just set the channel map to all 1's. Needs to get - * set to default or initialized or something - */ - connsm->num_used_chans = BLE_PHY_NUM_DATA_CHANS; - memset(connsm->chanmap, 0xff, BLE_LL_CONN_CHMAP_LEN - 1); - connsm->chanmap[4] = 0x1f; + /* Set channel map to map requested by host */ + connsm->num_used_chans = g_ble_ll_conn_params.num_used_chans; + memcpy(connsm->chanmap, g_ble_ll_conn_params.master_chan_map, + BLE_LL_CONN_CHMAP_LEN); /* Calculate random access address and crc initialization value */ connsm->access_addr = ble_ll_conn_calc_access_addr(); @@ -1206,18 +1196,12 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) connsm->csmflags.cfbit.host_expects_upd_event = 0; } - /* - * XXX: not quite sure I am interpreting slave latency correctly here. - * The spec says if you applied slave latency and you dont hear a packet, - * you dont apply slave latency. Does that mean you dont apply slave - * latency until you hear a packet or on the next interval if you listen - * and dont hear anything, can you apply slave latency? - */ /* Set event counter to the next connection event that we will tx/rx in */ itvl = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; latency = 1; - if (connsm->csmflags.cfbit.allow_slave_latency && - !connsm->csmflags.cfbit.conn_update_scheduled) { + if (connsm->csmflags.cfbit.allow_slave_latency && + !connsm->csmflags.cfbit.conn_update_scheduled && + !connsm->csmflags.cfbit.chanmap_update_scheduled) { if (connsm->csmflags.cfbit.pkt_rxd) { latency += connsm->slave_latency; itvl = itvl * latency; @@ -1267,6 +1251,34 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) connsm->csmflags.cfbit.conn_update_scheduled = 0; } + /* + * If there is a channel map request pending and we have reached the + * instant, change to new channel map. Note there is a special case here. + * If we received a channel map update with an instant equal to the event + * counter, when we get here the event counter has already been + * incremented by 1. That is why we do a signed comparison and change to + * new channel map once the event counter equals or has passed channel + * map update instant. + */ + if (connsm->csmflags.cfbit.chanmap_update_scheduled && + ((int16_t)(connsm->chanmap_instant - connsm->event_cntr) <= 0)) { + + /* XXX: there is a chance that the control packet is still on + * the queue of the master. This means that we never successfully + * transmitted update request. Would end up killing connection + on slave side. Could ignore it or see if still enqueued. */ + connsm->num_used_chans = + ble_ll_conn_calc_used_chans(connsm->req_chanmap); + memcpy(connsm->chanmap, connsm->req_chanmap, BLE_LL_CONN_CHMAP_LEN); + + connsm->csmflags.cfbit.chanmap_update_scheduled = 0; + + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CHAN_MAP_UPD); + + /* XXX: host could have resent channel map command. Need to + check to make sure we dont have to restart! */ + } + /* Calculate data channel index of next connection event */ while (latency > 0) { connsm->last_unmapped_chan = connsm->unmapped_chan; @@ -2225,6 +2237,36 @@ ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t length) } /** + * Called to set the global channel mask that we use for all connections. + * + * @param num_used_chans + * @param chanmap + */ +void +ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, uint8_t *chanmap) +{ + struct ble_ll_conn_sm *connsm; + struct ble_ll_conn_global_params *conn_params; + + /* Do nothing if same channel map */ + conn_params = &g_ble_ll_conn_params; + if (!memcmp(conn_params->master_chan_map, chanmap, BLE_LL_CONN_CHMAP_LEN)) { + return; + } + + /* Change channel map and cause channel map update procedure to start */ + conn_params->num_used_chans = num_used_chans; + memcpy(conn_params->master_chan_map, chanmap, BLE_LL_CONN_CHMAP_LEN); + + /* Perform channel map update */ + SLIST_FOREACH(connsm, &g_ble_ll_conn_active_list, act_sle) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + ble_ll_ctrl_proc_start(connsm, BLE_LL_CTRL_PROC_CHAN_MAP_UPD); + } + } +} + +/** * Called when a device has received a connect request while advertising and * the connect request has passed the advertising filter policy and is for * us. This will start a connection in the slave role assuming that we dont @@ -2378,6 +2420,9 @@ ble_ll_conn_reset(void) } ble_ll_conn_end(connsm, BLE_ERR_SUCCESS); } + + /* Call conn module init */ + ble_ll_conn_module_init(); } /* Initialize the connection module */ @@ -2436,5 +2481,10 @@ ble_ll_conn_module_init(void) maxbytes = BLE_LL_CFG_CONN_INIT_MAX_TX_BYTES + BLE_LL_DATA_MIC_LEN; conn_params->conn_init_max_tx_time = BLE_TX_DUR_USECS_M(maxbytes); conn_params->conn_init_max_tx_octets = BLE_LL_CFG_CONN_INIT_MAX_TX_BYTES; + + /* Mask in all channels by default */ + conn_params->num_used_chans = BLE_PHY_NUM_DATA_CHANS; + memset(conn_params->master_chan_map, 0xff, BLE_LL_CONN_CHMAP_LEN - 1); + conn_params->master_chan_map[4] = 0x1f; } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll_conn_hci.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn_hci.c b/net/nimble/controller/src/ble_ll_conn_hci.c index 2ded499..0e9a9c9 100644 --- a/net/nimble/controller/src/ble_ll_conn_hci.c +++ b/net/nimble/controller/src/ble_ll_conn_hci.c @@ -435,7 +435,7 @@ ble_ll_conn_process_conn_params(uint8_t *cmdbuf, struct ble_ll_conn_sm *connsm) * @return int */ int -ble_ll_conn_read_rem_features(uint8_t *cmdbuf) +ble_ll_conn_hci_read_rem_features(uint8_t *cmdbuf) { uint16_t handle; struct ble_ll_conn_sm *connsm; @@ -521,6 +521,16 @@ ble_ll_conn_hci_update(uint8_t *cmdbuf) } } + /* + * If we are a slave and the master has initiated the channel map + * update procedure we should deny the slave request for now. + */ + if (connsm->csmflags.cfbit.chanmap_update_scheduled) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_DIFF_TRANS_COLL; + } + } + /* Retrieve command data */ hcu = &connsm->conn_param_req; hcu->handle = handle; @@ -788,3 +798,66 @@ ble_ll_conn_hci_rd_rssi(uint8_t *cmdbuf, uint8_t *rspbuf, uint8_t *rsplen) return rc; } +/** + * Called to read the current channel map of a connection + * + * @param cmdbuf + * @param rspbuf + * @param rsplen + * + * @return int + */ +int +ble_ll_conn_hci_rd_chan_map(uint8_t *cmdbuf, uint8_t *rspbuf, uint8_t *rsplen) +{ + int rc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + + handle = le16toh(cmdbuf); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + rc = BLE_ERR_UNK_CONN_ID; + } else { + if (connsm->csmflags.cfbit.chanmap_update_scheduled) { + memcpy(rspbuf + 2, &connsm->req_chanmap[0], BLE_LL_CONN_CHMAP_LEN); + } else { + memcpy(rspbuf + 2, &connsm->chanmap[0], BLE_LL_CONN_CHMAP_LEN); + } + rc = BLE_ERR_SUCCESS; + } + + htole16(rspbuf, handle); + *rsplen = sizeof(uint16_t) + BLE_LL_CONN_CHMAP_LEN; + return rc; +} + +/** + * Called when the host issues the LE command "set host channel classification" + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_hci_set_chan_class(uint8_t *cmdbuf) +{ + int rc; + uint8_t num_used_chans; + + /* + * The HCI command states that the host is allowed to mask in just one + * channel but the Link Layer needs minimum two channels to operate. So + * I will not allow this command if there are less than 2 channels masked. + */ + rc = BLE_ERR_SUCCESS; + num_used_chans = ble_ll_conn_calc_used_chans(cmdbuf); + if ((num_used_chans < 2) || ((cmdbuf[4] & 0xe0) != 0)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Set the host channel mask */ + ble_ll_conn_set_global_chanmap(num_used_chans, cmdbuf); + return rc; +} + http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll_conn_priv.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn_priv.h b/net/nimble/controller/src/ble_ll_conn_priv.h index f31aeae..5d29337 100644 --- a/net/nimble/controller/src/ble_ll_conn_priv.h +++ b/net/nimble/controller/src/ble_ll_conn_priv.h @@ -51,6 +51,21 @@ /* Offset (in bytes) of advertising address in connect request */ #define BLE_LL_CONN_REQ_ADVA_OFF (BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN) +/* Global Link Layer connection parameters */ +struct ble_ll_conn_global_params +{ + uint8_t master_chan_map[BLE_LL_CONN_CHMAP_LEN]; + uint8_t num_used_chans; + uint8_t supp_max_tx_octets; + uint8_t supp_max_rx_octets; + uint8_t conn_init_max_tx_octets; + uint8_t reserved; + uint16_t conn_init_max_tx_time; + uint16_t supp_max_tx_time; + uint16_t supp_max_rx_time; +}; +extern struct ble_ll_conn_global_params g_ble_ll_conn_params; + /* Some data structures used by other LL routines */ SLIST_HEAD(ble_ll_conn_active_list, ble_ll_conn_sm); STAILQ_HEAD(ble_ll_conn_free_list, ble_ll_conn_sm); @@ -80,6 +95,7 @@ int ble_ll_conn_slave_start(uint8_t *rxbuf, uint32_t conn_req_end); /* Link Layer interface */ void ble_ll_conn_module_init(void); +void ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, uint8_t *chanmap); void ble_ll_conn_reset(void); void ble_ll_conn_event_end(void *arg); void ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t len); @@ -93,15 +109,16 @@ void ble_ll_conn_wfr_timer_exp(void); int ble_ll_conn_is_lru(struct ble_ll_conn_sm *s1, struct ble_ll_conn_sm *s2); uint32_t ble_ll_conn_get_ce_end_time(void); void ble_ll_conn_event_halt(void); +uint8_t ble_ll_conn_calc_used_chans(uint8_t *chmap); /* HCI */ void ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t reason); - int ble_ll_conn_hci_disconnect_cmd(uint8_t *cmdbuf); int ble_ll_conn_hci_rd_rem_ver_cmd(uint8_t *cmdbuf); int ble_ll_conn_create(uint8_t *cmdbuf); int ble_ll_conn_hci_update(uint8_t *cmdbuf); +int ble_ll_conn_hci_set_chan_class(uint8_t *cmdbuf); int ble_ll_conn_hci_param_reply(uint8_t *cmdbuf, int negative_reply); int ble_ll_conn_create_cancel(void); void ble_ll_conn_num_comp_pkts_event_send(void); @@ -109,7 +126,9 @@ void ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status); void ble_ll_conn_timeout(struct ble_ll_conn_sm *connsm, uint8_t ble_err); int ble_ll_conn_hci_chk_conn_params(uint16_t itvl_min, uint16_t itvl_max, uint16_t latency, uint16_t spvn_tmo); -int ble_ll_conn_read_rem_features(uint8_t *cmdbuf); +int ble_ll_conn_hci_read_rem_features(uint8_t *cmdbuf); int ble_ll_conn_hci_rd_rssi(uint8_t *cmdbuf, uint8_t *rspbuf, uint8_t *rsplen); +int ble_ll_conn_hci_rd_chan_map(uint8_t *cmdbuf, uint8_t *rspbuf, + uint8_t *rsplen); #endif /* H_BLE_LL_CONN_PRIV_ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll_ctrl.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_ctrl.c b/net/nimble/controller/src/ble_ll_ctrl.c index 59b5e6f..7925a64 100644 --- a/net/nimble/controller/src/ble_ll_ctrl.c +++ b/net/nimble/controller/src/ble_ll_ctrl.c @@ -386,6 +386,27 @@ ble_ll_ctrl_version_ind_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld) } /** + * Called to make a LL control channel map request PDU. + * + * @param connsm Pointer to connection state machine + * @param pyld Pointer to payload of LL control PDU + */ +static void +ble_ll_ctrl_chanmap_req_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld) +{ + /* Copy channel map that host desires into request */ + memcpy(pyld, g_ble_ll_conn_params.master_chan_map, BLE_LL_CONN_CHMAP_LEN); + memcpy(connsm->req_chanmap, pyld, BLE_LL_CONN_CHMAP_LEN); + + /* Place instant into request */ + connsm->chanmap_instant = connsm->event_cntr + connsm->slave_latency + 6 + 1; + htole16(pyld + BLE_LL_CONN_CHMAP_LEN, connsm->chanmap_instant); + + /* Set scheduled flag */ + connsm->csmflags.cfbit.chanmap_update_scheduled = 1; +} + +/** * Called to make a connection update request LL control PDU * * Context: Link Layer @@ -633,6 +654,18 @@ ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr, } } + /* + * If we are a master and we currently performing a channel map + * update procedure we need to return an error + */ + if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) && + (connsm->csmflags.cfbit.chanmap_update_scheduled)) { + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = BLE_LL_CTRL_CONN_PARM_REQ; + rspbuf[2] = BLE_ERR_DIFF_TRANS_COLL; + return rsp_opcode; + } + /* Process the received connection parameter request */ rsp_opcode = ble_ll_ctrl_conn_param_pdu_proc(connsm, dptr, rspbuf, BLE_LL_CTRL_CONN_PARM_REQ); @@ -709,6 +742,32 @@ ble_ll_ctrl_rx_version_ind(struct ble_ll_conn_sm *connsm, uint8_t *dptr, } /** + * Called to process a received channel map request control pdu. + * + * Context: Link Layer task + * + * @param connsm + * @param dptr + */ +static void +ble_ll_ctrl_rx_chanmap_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr) +{ + uint16_t instant; + uint16_t conn_events; + + /* If instant is in the past, we have to end the connection */ + instant = le16toh(dptr + BLE_LL_CONN_CHMAP_LEN); + conn_events = (instant - connsm->event_cntr) & 0xFFFF; + if (conn_events >= 32767) { + ble_ll_conn_timeout(connsm, BLE_ERR_INSTANT_PASSED); + } else { + connsm->chanmap_instant = instant; + memcpy(connsm->req_chanmap, dptr, BLE_LL_CONN_CHMAP_LEN); + connsm->csmflags.cfbit.chanmap_update_scheduled = 1; + } +} + +/** * Callback when LL control procedure times out (for a given connection). If * this is called, it means that we need to end the connection because it * has not responded to a LL control request. @@ -751,6 +810,10 @@ ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc) opcode = BLE_LL_CTRL_CONN_UPDATE_REQ; ble_ll_ctrl_conn_upd_make(connsm, dptr + 1, NULL); break; + case BLE_LL_CTRL_PROC_CHAN_MAP_UPD: + opcode = BLE_LL_CTRL_CHANNEL_MAP_REQ; + ble_ll_ctrl_chanmap_req_make(connsm, dptr + 1); + break; case BLE_LL_CTRL_PROC_FEATURE_XCHG: if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { opcode = BLE_LL_CTRL_FEATURE_REQ; @@ -910,14 +973,16 @@ ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc) connsm->cur_ctrl_proc = ctrl_proc; /* Initialize the procedure response timeout */ - os_callout_func_init(&connsm->ctrl_proc_rsp_timer, - &g_ble_ll_data.ll_evq, - ble_ll_ctrl_proc_rsp_timer_cb, - connsm); - - /* Re-start the timer. Control procedure timeout is 40 seconds */ - os_callout_reset(&connsm->ctrl_proc_rsp_timer.cf_c, - OS_TICKS_PER_SEC * BLE_LL_CTRL_PROC_TIMEOUT); + if (ctrl_proc != BLE_LL_CTRL_PROC_CHAN_MAP_UPD) { + os_callout_func_init(&connsm->ctrl_proc_rsp_timer, + &g_ble_ll_data.ll_evq, + ble_ll_ctrl_proc_rsp_timer_cb, + connsm); + + /* Re-start timer. Control procedure timeout is 40 seconds */ + os_callout_reset(&connsm->ctrl_proc_rsp_timer.cf_c, + OS_TICKS_PER_SEC * BLE_LL_CTRL_PROC_TIMEOUT); + } } } @@ -1055,6 +1120,11 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) case BLE_LL_CTRL_CONN_UPDATE_REQ: rsp_opcode = ble_ll_ctrl_rx_conn_update(connsm, dptr, rspbuf); break; + case BLE_LL_CTRL_CHANNEL_MAP_REQ: + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + ble_ll_ctrl_rx_chanmap_req(connsm, dptr); + } + break; case BLE_LL_CTRL_LENGTH_REQ: /* Extract parameters and check if valid */ if (ble_ll_ctrl_len_proc(connsm, dptr)) { @@ -1092,7 +1162,6 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) case BLE_LL_CTRL_UNKNOWN_RSP: ble_ll_ctrl_proc_unk_rsp(connsm, dptr); break; - case BLE_LL_CTRL_FEATURE_REQ: if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { rsp_opcode = ble_ll_ctrl_rx_feature_req(connsm, dptr, rspbuf); @@ -1103,7 +1172,6 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; } break; - /* XXX: check to see if ctrl procedure was running? Do we care? */ case BLE_LL_CTRL_FEATURE_RSP: /* Stop the control procedure */ @@ -1113,11 +1181,9 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_FEATURE_XCHG); } break; - case BLE_LL_CTRL_VERSION_IND: rsp_opcode = ble_ll_ctrl_rx_version_ind(connsm, dptr, rspbuf + 1); break; - case BLE_LL_CTRL_SLAVE_FEATURE_REQ: if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { rsp_opcode = ble_ll_ctrl_rx_feature_req(connsm, dptr, rspbuf); @@ -1127,9 +1193,7 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; } break; - /* XXX: remember to check if feature supported. Implement! */ - case BLE_LL_CTRL_CHANNEL_MAP_REQ: case BLE_LL_CTRL_ENC_REQ: case BLE_LL_CTRL_START_ENC_REQ: case BLE_LL_CTRL_PAUSE_ENC_REQ: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/controller/src/ble_ll_hci.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_hci.c b/net/nimble/controller/src/ble_ll_hci.c index 5412698..a508871 100644 --- a/net/nimble/controller/src/ble_ll_hci.c +++ b/net/nimble/controller/src/ble_ll_hci.c @@ -229,6 +229,32 @@ ble_ll_hci_is_event_enabled(int bitpos) } /** + * Called to determine if the reply to the command should be a command complete + * event or a command status event. + * + * @param ocf + * + * @return int 0: return command complete; 1: return command status event + */ +static int +ble_ll_hci_le_cmd_send_cmd_status(uint16_t ocf) +{ + int rc; + + switch (ocf) { + case BLE_HCI_OCF_LE_RD_REM_FEAT: + case BLE_HCI_OCF_LE_CREATE_CONN: + case BLE_HCI_OCF_LE_CONN_UPDATE: + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +/** * Process a LE command sent from the host to the controller. The HCI command * has a 3 byte command header followed by data. The header is: * -> opcode (2 bytes) @@ -246,6 +272,7 @@ static int ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) { int rc; + uint8_t cmdlen; uint8_t len; uint8_t *rspbuf; @@ -255,6 +282,12 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) /* Get length from command */ len = cmdbuf[sizeof(uint16_t)]; + /* Check the length to make sure it is valid */ + cmdlen = g_ble_hci_le_cmd_len[ocf]; + if ((cmdlen != 0xFF) && (len != cmdlen)) { + goto ll_hci_le_cmd_exit; + } + /* * The command response pointer points into the same buffer as the * command data itself. That is fine, as each command reads all the data @@ -267,35 +300,23 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) switch (ocf) { case BLE_HCI_OCF_LE_SET_EVENT_MASK: - if (len == BLE_HCI_SET_LE_EVENT_MASK_LEN) { - rc = ble_ll_hci_set_le_event_mask(cmdbuf); - } + rc = ble_ll_hci_set_le_event_mask(cmdbuf); break; case BLE_HCI_OCF_LE_RD_BUF_SIZE: - if (len == BLE_HCI_RD_BUF_SIZE_LEN) { - rc = ble_ll_hci_le_read_bufsize(rspbuf, rsplen); - } + rc = ble_ll_hci_le_read_bufsize(rspbuf, rsplen); break; case BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT: - if (len == 0) { - rc = ble_ll_hci_le_read_local_features(rspbuf, rsplen); - } + rc = ble_ll_hci_le_read_local_features(rspbuf, rsplen); break; case BLE_HCI_OCF_LE_SET_RAND_ADDR: - if (len == BLE_DEV_ADDR_LEN) { - rc = ble_ll_set_random_addr(cmdbuf); - } + rc = ble_ll_set_random_addr(cmdbuf); break; case BLE_HCI_OCF_LE_SET_ADV_PARAMS: /* Length should be one byte */ - if (len == BLE_HCI_SET_ADV_PARAM_LEN) { - rc = ble_ll_adv_set_adv_params(cmdbuf); - } + rc = ble_ll_adv_set_adv_params(cmdbuf); break; case BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR: - if (len == 0) { - rc = ble_ll_adv_read_txpwr(rspbuf, rsplen); - } + rc = ble_ll_adv_read_txpwr(rspbuf, rsplen); break; case BLE_HCI_OCF_LE_SET_ADV_DATA: if (len > 0) { @@ -311,87 +332,65 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) break; case BLE_HCI_OCF_LE_SET_ADV_ENABLE: /* Length should be one byte */ - if (len == BLE_HCI_SET_ADV_ENABLE_LEN) { - rc = ble_ll_adv_set_enable(cmdbuf); - } + rc = ble_ll_adv_set_enable(cmdbuf); break; case BLE_HCI_OCF_LE_SET_SCAN_ENABLE: - if (len == BLE_HCI_SET_SCAN_ENABLE_LEN) { - rc = ble_ll_scan_set_enable(cmdbuf); - } + rc = ble_ll_scan_set_enable(cmdbuf); break; case BLE_HCI_OCF_LE_SET_SCAN_PARAMS: - /* Length should be one byte */ - if (len == BLE_HCI_SET_SCAN_PARAM_LEN) { - rc = ble_ll_scan_set_scan_params(cmdbuf); - } + rc = ble_ll_scan_set_scan_params(cmdbuf); break; case BLE_HCI_OCF_LE_CREATE_CONN: - if (len == BLE_HCI_CREATE_CONN_LEN) { - rc = ble_ll_conn_create(cmdbuf); - } - /* This is a hack; command status gets sent instead of cmd complete */ - rc += (BLE_ERR_MAX + 1); + rc = ble_ll_conn_create(cmdbuf); break; case BLE_HCI_OCF_LE_CREATE_CONN_CANCEL: - if (len == 0) { - rc = ble_ll_conn_create_cancel(); - } + rc = ble_ll_conn_create_cancel(); break; case BLE_HCI_OCF_LE_CLEAR_WHITE_LIST: - if (len == 0) { - rc = ble_ll_whitelist_clear(); - } + rc = ble_ll_whitelist_clear(); break; case BLE_HCI_OCF_LE_RD_WHITE_LIST_SIZE: - if (len == 0) { - rc = ble_ll_whitelist_read_size(rspbuf, rsplen); - } + rc = ble_ll_whitelist_read_size(rspbuf, rsplen); break; case BLE_HCI_OCF_LE_ADD_WHITE_LIST: - if (len == BLE_HCI_CHG_WHITE_LIST_LEN) { - rc = ble_ll_whitelist_add(cmdbuf + 1, cmdbuf[0]); - } + rc = ble_ll_whitelist_add(cmdbuf + 1, cmdbuf[0]); break; case BLE_HCI_OCF_LE_RMV_WHITE_LIST: - if (len == BLE_HCI_CHG_WHITE_LIST_LEN) { - rc = ble_ll_whitelist_rmv(cmdbuf + 1, cmdbuf[0]); - } + rc = ble_ll_whitelist_rmv(cmdbuf + 1, cmdbuf[0]); break; case BLE_HCI_OCF_LE_CONN_UPDATE: - if (len == BLE_HCI_CONN_UPDATE_LEN) { - rc = ble_ll_conn_hci_update(cmdbuf); - } - /* This is a hack; command status gets sent instead of cmd complete */ - rc += (BLE_ERR_MAX + 1); + rc = ble_ll_conn_hci_update(cmdbuf); + break; + case BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS: + rc = ble_ll_conn_hci_set_chan_class(cmdbuf); + break; + case BLE_HCI_OCF_LE_RD_CHAN_MAP: + rc = ble_ll_conn_hci_rd_chan_map(cmdbuf, rspbuf, rsplen); break; - - /* XXX: implement */ case BLE_HCI_OCF_LE_RD_REM_FEAT: - if (len == BLE_HCI_CONN_RD_REM_FEAT_LEN) { - rc = ble_ll_conn_read_rem_features(cmdbuf); - } - /* This is a hack; command status gets sent instead of cmd complete */ - rc += (BLE_ERR_MAX + 1); + rc = ble_ll_conn_hci_read_rem_features(cmdbuf); break; - case BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR: - if (len == BLE_HCI_CONN_PARAM_NEG_REPLY_LEN) { - rc = ble_ll_conn_hci_param_reply(cmdbuf, 0); - } + rc = ble_ll_conn_hci_param_reply(cmdbuf, 0); break; - case BLE_HCI_OCF_LE_REM_CONN_PARAM_RR: - if (len == BLE_HCI_CONN_PARAM_REPLY_LEN) { - rc = ble_ll_conn_hci_param_reply(cmdbuf, 1); - } + rc = ble_ll_conn_hci_param_reply(cmdbuf, 1); break; - default: rc = BLE_ERR_UNKNOWN_HCI_CMD; break; } + /* + * This code is here because we add 256 to the return code to denote + * that the reply to this command should be command status (as opposed to + * command complete). + */ +ll_hci_le_cmd_exit: + if (ble_ll_hci_le_cmd_send_cmd_status(ocf)) { + rc += (BLE_ERR_MAX + 1); + } + return rc; } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/host/include/host/host_hci.h ---------------------------------------------------------------------- diff --git a/net/nimble/host/include/host/host_hci.h b/net/nimble/host/include/host/host_hci.h index 4dc16a9..f141a22 100644 --- a/net/nimble/host/include/host/host_hci.h +++ b/net/nimble/host/include/host/host_hci.h @@ -32,6 +32,8 @@ int host_hci_cmd_disconnect(uint16_t handle, uint8_t reason); int host_hci_cmd_rd_rem_version(uint16_t handle); int host_hci_cmd_rd_local_version(void); int host_hci_cmd_read_rssi(uint16_t handle); +int host_hci_cmd_le_set_host_chan_class(uint8_t *new_chan_map); +int host_hci_cmd_le_rd_chanmap(uint16_t handle); int host_hci_cmd_le_set_scan_rsp_data(uint8_t *data, uint8_t len); int host_hci_cmd_le_set_adv_data(uint8_t *data, uint8_t len); int host_hci_cmd_le_set_adv_params(struct hci_adv_params *adv); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/host/src/host_dbg.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/host_dbg.c b/net/nimble/host/src/host_dbg.c index e1c99a8..a009a15 100644 --- a/net/nimble/host/src/host_dbg.c +++ b/net/nimble/host/src/host_dbg.c @@ -251,6 +251,17 @@ host_hci_dbg_cmd_complete_disp(uint8_t *evdata, uint8_t len) break; } break; + case BLE_HCI_OGF_LE: + switch (ocf) { + case BLE_HCI_OCF_LE_RD_CHAN_MAP: + BLE_HS_LOG(DEBUG, " handle=%u chanmap=%x.%x.%x.%x.%x", + le16toh(evdata + 4), evdata[6], evdata[7], evdata[8], + evdata[9], evdata[10]); + break; + default: + break; + } + break; default: break; } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/host/src/host_hci_cmd.c ---------------------------------------------------------------------- diff --git a/net/nimble/host/src/host_hci_cmd.c b/net/nimble/host/src/host_hci_cmd.c index 500f3cc..ac8c67e 100644 --- a/net/nimble/host/src/host_hci_cmd.c +++ b/net/nimble/host/src/host_hci_cmd.c @@ -641,6 +641,42 @@ host_hci_cmd_le_conn_param_neg_reply(struct hci_conn_param_neg_reply *hcn) } /** + * Read the channel map for a given connection. + * + * @param handle + * + * @return int + */ +int +host_hci_cmd_le_rd_chanmap(uint16_t handle) +{ + int rc; + uint8_t cmd[BLE_HCI_RD_CHANMAP_LEN]; + + htole16(cmd, handle); + rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_RD_CHAN_MAP, + BLE_HCI_RD_CHANMAP_LEN, cmd); + return rc; +} + +/** + * Set the channel map in the controller + * + * @param chanmap + * + * @return int + */ +int +host_hci_cmd_le_set_host_chan_class(uint8_t *chanmap) +{ + int rc; + + rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_SET_HOST_CHAN_CLASS, + BLE_HCI_SET_HOST_CHAN_CLASS_LEN, chanmap); + return rc; +} + +/** * Read the RSSI for a given connection handle * * NOTE: OGF=0x05 OCF=0x0005 http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/include/nimble/hci_common.h ---------------------------------------------------------------------- diff --git a/net/nimble/include/nimble/hci_common.h b/net/nimble/include/nimble/hci_common.h index 77834ec..2978eb2 100644 --- a/net/nimble/include/nimble/hci_common.h +++ b/net/nimble/include/nimble/hci_common.h @@ -49,6 +49,12 @@ /* NOTE: 0x07 not defined in specification */ #define BLE_HCI_OGF_LE (0x08) +/* + * Number of LE commands. NOTE: this is really just used to size the array + * containing the lengths of the LE commands. + */ +#define BLE_HCI_NUM_LE_CMDS (48) + /* List of OCF for Link Control commands (OGF=0x01) */ #define BLE_HCI_OCF_DISCONNECT_CMD (0x0006) #define BLE_HCI_OCF_RD_REM_VER_INFO (0x001D) @@ -299,6 +305,12 @@ /* --- LE connection update (OCF 0x0013) */ #define BLE_HCI_CONN_UPDATE_LEN (14) +/* --- LE set host channel classification command (OCF 0x0014) */ +#define BLE_HCI_SET_HOST_CHAN_CLASS_LEN (5) + +/* --- LE read channel map command (OCF 0x0015) */ +#define BLE_HCI_RD_CHANMAP_LEN (2) + /* --- LE read remote features (OCF 0x0016) */ #define BLE_HCI_CONN_RD_REM_FEAT_LEN (2) @@ -595,4 +607,7 @@ struct hci_data_hdr #define BLE_HCI_PB_FIRST_FLUSH 2 #define BLE_HCI_PB_FULL 3 +/* External data structures */ +extern const uint8_t g_ble_hci_le_cmd_len[BLE_HCI_NUM_LE_CMDS]; + #endif /* H_BLE_HCI_COMMON_ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/net/nimble/src/hci_common.c ---------------------------------------------------------------------- diff --git a/net/nimble/src/hci_common.c b/net/nimble/src/hci_common.c new file mode 100644 index 0000000..e30e071 --- /dev/null +++ b/net/nimble/src/hci_common.c @@ -0,0 +1,80 @@ +/** + * 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 <stdint.h> +#include "os/os.h" +#include "nimble/ble.h" +#include "nimble/hci_common.h" + +/* + * Lengths for commands. NOTE: 0xFF is a special case and means that there + * is no fixed length for the command or is a command that we have not + * implemented. + */ +const uint8_t g_ble_hci_le_cmd_len[BLE_HCI_NUM_LE_CMDS] = +{ + 0, /* 0x0000: reserved */ + BLE_HCI_SET_LE_EVENT_MASK_LEN, /* 0x0001: set event mask */ + BLE_HCI_RD_BUF_SIZE_LEN, /* 0x0002: read buffer size */ + 0, /* 0x0003: read local supp features */ + 0, /* 0x0004: not defined */ + BLE_DEV_ADDR_LEN, /* 0x0005: set random address */ + BLE_HCI_SET_ADV_PARAM_LEN, /* 0x0006: set advertising parameters */ + 0, /* 0x0007: read adv chan tx power */ + 0xFF, /* 0x0008: set advertising data */ + 0xFF, /* 0x0009: set scan rsp data */ + BLE_HCI_SET_ADV_ENABLE_LEN, /* 0x000A: set advertising enable */ + BLE_HCI_SET_SCAN_PARAM_LEN, /* 0x000B: set scan parameters */ + BLE_HCI_SET_SCAN_ENABLE_LEN, /* 0x000C: set scan enable */ + BLE_HCI_CREATE_CONN_LEN, /* 0x000D: create connection */ + 0, /* 0x000E: create connection cancel*/ + 0, /* 0x000F: read whitelist size */ + 0, /* 0x0010: clear white list */ + BLE_HCI_CHG_WHITE_LIST_LEN, /* 0x0011: add to white list */ + BLE_HCI_CHG_WHITE_LIST_LEN, /* 0x0012: remove from white list */ + BLE_HCI_CONN_UPDATE_LEN, /* 0x0013: connection update */ + BLE_HCI_SET_HOST_CHAN_CLASS_LEN, /* 0x0014: set host chan class */ + sizeof(uint16_t), /* 0x0015: read channel map */ + BLE_HCI_CONN_RD_REM_FEAT_LEN, /* 0x0016: read remote features */ + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + BLE_HCI_CONN_PARAM_REPLY_LEN, /* 0x0020: conn param reply */ + BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, /* 0x0021: conn param neg reply */ + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, /* 0x002F: Read max data length */ +}; + http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/9e54d5f5/project/bletest/src/main.c ---------------------------------------------------------------------- diff --git a/project/bletest/src/main.c b/project/bletest/src/main.c index 6c24e64..0000ffa 100755 --- a/project/bletest/src/main.c +++ b/project/bletest/src/main.c @@ -110,7 +110,7 @@ os_membuf_t g_mbuf_buffer[MBUF_MEMPOOL_SIZE]; #define BLETEST_CFG_CONN_SPVN_TMO (1000) /* 20 seconds */ #define BLETEST_CFG_MIN_CE_LEN (6) #define BLETEST_CFG_MAX_CE_LEN (BLETEST_CFG_CONN_ITVL) -#define BLETEST_CFG_CONCURRENT_CONNS (16) +#define BLETEST_CFG_CONCURRENT_CONNS (1) /* BLETEST variables */ #undef BLETEST_ADV_PKT_NUM @@ -378,9 +378,10 @@ bletest_init_initiator(void) void bletest_execute_initiator(void) { -// int i; + int i; int rc; uint16_t handle; + uint8_t new_chan_map[5]; /* * Determine if there is an active connection for the current handle @@ -413,22 +414,33 @@ bletest_execute_initiator(void) } } } else { -#if 0 if ((int32_t)(os_time_get() - g_next_os_time) >= 0) { - for (i = 0; i < g_bletest_current_conns; ++i) { - if (ble_ll_conn_find_active_conn(i + 1)) { - /* Ask for version information */ - host_hci_cmd_read_rssi(i+1); - host_hci_outstanding_opcode = 0; - - #if 0 - bletest_send_conn_update(1); - #endif - } + if ((g_bletest_state == 1) || (g_bletest_state == 3)) { + for (i = 0; i < g_bletest_current_conns; ++i) { + if (ble_ll_conn_find_active_conn(i + 1)) { + host_hci_cmd_le_rd_chanmap(i+1); + host_hci_outstanding_opcode = 0; + } + } + } else if (g_bletest_state == 2) { + new_chan_map[0] = 0; + new_chan_map[1] = 0x3; + new_chan_map[2] = 0; + new_chan_map[3] = 0; + new_chan_map[4] = 0; + host_hci_cmd_le_set_host_chan_class(new_chan_map); + host_hci_outstanding_opcode = 0; + } else { + for (i = 0; i < g_bletest_current_conns; ++i) { + if (ble_ll_conn_find_active_conn(i + 1)) { + host_hci_cmd_read_rssi(i+1); + host_hci_outstanding_opcode = 0; + } + } } + ++g_bletest_state; g_next_os_time = os_time_get() + OS_TICKS_PER_SEC * 5; } -#endif } } #endif