Repository: incubator-mynewt-larva Updated Branches: refs/heads/master df59850fe -> 7a82be0e5
Connection update procedure implemented. Most of connection parameter request implemented but not final so connection parameter 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/262b1be2 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/262b1be2 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/262b1be2 Branch: refs/heads/master Commit: 262b1be2f72c5e42d254dcc92e62598590988332 Parents: df59850 Author: wes3 <w...@micosa.io> Authored: Fri Jan 22 15:19:39 2016 -0800 Committer: wes3 <w...@micosa.io> Committed: Fri Jan 22 15:19:39 2016 -0800 ---------------------------------------------------------------------- hw/bsp/nrf52pdk/include/bsp/bsp.h | 3 + .../controller/include/controller/ble_ll_conn.h | 46 +- .../controller/include/controller/ble_ll_ctrl.h | 24 +- .../controller/include/controller/ble_ll_hci.h | 2 +- net/nimble/controller/src/ble_ll.c | 14 +- net/nimble/controller/src/ble_ll_adv.c | 2 +- net/nimble/controller/src/ble_ll_conn.c | 127 +++- net/nimble/controller/src/ble_ll_conn_hci.c | 256 ++++++- net/nimble/controller/src/ble_ll_conn_priv.h | 4 + net/nimble/controller/src/ble_ll_ctrl.c | 696 ++++++++++++++++--- net/nimble/controller/src/ble_ll_hci.c | 26 +- net/nimble/controller/src/ble_ll_scan.c | 134 ++-- project/bletest/src/main.c | 59 +- 13 files changed, 1174 insertions(+), 219 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/hw/bsp/nrf52pdk/include/bsp/bsp.h ---------------------------------------------------------------------- diff --git a/hw/bsp/nrf52pdk/include/bsp/bsp.h b/hw/bsp/nrf52pdk/include/bsp/bsp.h index 2451c31..7ec6b58 100644 --- a/hw/bsp/nrf52pdk/include/bsp/bsp.h +++ b/hw/bsp/nrf52pdk/include/bsp/bsp.h @@ -27,6 +27,9 @@ extern "C" { /* UART info */ #define CONSOLE_UART 0 +/* Declaration of "non-zeroed" bss */ +#define nzbss_t __attribute__((section(".nzbss"))) + #ifdef __cplusplus } #endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 444ee72..4df87b7 100644 --- a/net/nimble/controller/include/controller/ble_ll_conn.h +++ b/net/nimble/controller/include/controller/ble_ll_conn.h @@ -19,7 +19,9 @@ #include "os/os.h" #include "nimble/ble.h" +#include "nimble/hci_common.h" #include "controller/ble_ll_sched.h" +#include "controller/ble_ll_ctrl.h" #include "hal/hal_cputime.h" /* Roles */ @@ -58,7 +60,7 @@ struct ble_ll_conn_sm { /* Current connection state and role */ uint8_t conn_state; - uint8_t conn_role; + uint8_t conn_role; /* Can possibly be 1 bit */ /* Connection data length management */ uint8_t max_tx_octets; @@ -91,37 +93,47 @@ struct ble_ll_conn_sm uint8_t last_rxd_hdr_byte; /* note: possibly can make 1 bit since we only use the MD bit now */ - /* connection event timing/mgmt */ + /* connection event mgmt */ uint8_t pdu_txd; /* note: can be 1 bit */ uint8_t pkt_rxd; /* note: can be 1 bit */ uint8_t terminate_ind_txd; /* note: can be 1 bit */ uint8_t terminate_ind_rxd; /* note: can be 1 bit */ uint8_t allow_slave_latency; /* note: can be 1 bit */ uint8_t slave_set_last_anchor; /* note: can be 1 bit */ + uint8_t awaiting_host_reply; /* note: can be 1 bit */ + uint8_t send_conn_upd_event; /* note: can be 1 bit */ + uint8_t conn_update_scheduled; /* note: can be 1 bit */ + uint8_t host_expects_upd_event; /* note: can be 1 bit */ + uint8_t reject_reason; + uint8_t host_reply_opcode; uint8_t master_sca; uint8_t tx_win_size; uint8_t cur_ctrl_proc; uint8_t disconnect_reason; uint8_t rxd_disconnect_reason; uint16_t pending_ctrl_procs; - uint16_t conn_itvl; - uint16_t slave_latency; - uint16_t tx_win_off; - uint16_t min_ce_len; - uint16_t max_ce_len; uint16_t event_cntr; - uint16_t supervision_tmo; uint16_t conn_handle; uint16_t completed_pkts; uint32_t access_addr; - uint32_t crcinit; /* only low 24 bits used */ - uint32_t anchor_point; - uint32_t last_anchor_point; /* slave only */ + uint32_t crcinit; /* only low 24 bits used */ uint32_t ce_end_time; /* cputime at which connection event should end */ uint32_t terminate_timeout; + uint32_t last_scheduled; + + /* Connection timing */ + uint16_t conn_itvl_min; + uint16_t conn_itvl_max; + uint16_t conn_itvl; + uint16_t slave_latency; + uint16_t supervision_tmo; + uint16_t min_ce_len; + uint16_t max_ce_len; + uint16_t tx_win_off; + uint32_t anchor_point; + uint32_t last_anchor_point; uint32_t slave_cur_tx_win_usecs; uint32_t slave_cur_window_widening; - uint32_t last_scheduled; /* address information */ uint8_t own_addr_type; @@ -154,6 +166,16 @@ struct ble_ll_conn_sm /* For scheduling connections */ struct ble_ll_sched_item conn_sch; + + /* XXX: ifdef this by feature? */ + /* + * For connection update procedure. XXX: can make this a pointer and + * malloc it if we want to save space. + */ + struct hci_conn_update conn_param_req; + + /* For connection update procedure */ + struct ble_ll_conn_upd_req conn_update_req; }; /* http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/net/nimble/controller/include/controller/ble_ll_ctrl.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_ctrl.h b/net/nimble/controller/include/controller/ble_ll_ctrl.h index 8920f08..1f3de56 100644 --- a/net/nimble/controller/include/controller/ble_ll_ctrl.h +++ b/net/nimble/controller/include/controller/ble_ll_ctrl.h @@ -22,7 +22,7 @@ * It is used to determine which LL control procedure is currently running * in a connection and which ones may be pending. */ -#define BLE_LL_CTRL_PROC_CONN_UPD (0) +#define BLE_LL_CTRL_PROC_CONN_UPDATE (0) #define BLE_LL_CTRL_PROC_CHAN_MAP_UPD (1) #define BLE_LL_CTRL_PROC_ENCRYPT (2) #define BLE_LL_CTRL_PROC_FEATURE_XCHG (3) @@ -37,6 +37,9 @@ /* Checks if a particular control procedure is running */ #define IS_PENDING_CTRL_PROC_M(sm, proc) (sm->pending_ctrl_procs & (1 << proc)) +/* LL control procedure timeout */ +#define BLE_LL_CTRL_PROC_TIMEOUT (40) /* in secs */ + /* * LL CTRL PDU format * -> Opcode (1 byte) @@ -65,6 +68,11 @@ #define BLE_LL_CTRL_LENGTH_REQ (20) #define BLE_LL_CTRL_LENGTH_RSP (21) +/* Maximum opcode value */ +#define BLE_LL_CTRL_OPCODES (BLE_LL_CTRL_LENGTH_RSP + 1) + +extern const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES]; + /* Maximum # of payload bytes in a LL control PDU */ #define BLE_LL_CTRL_MAX_PAYLOAD (26) @@ -169,7 +177,7 @@ struct ble_ll_conn_params uint16_t interval_max; uint16_t latency; uint16_t timeout; - uint16_t pref_periodicity; + uint8_t pref_periodicity; uint16_t ref_conn_event_cnt; uint16_t offset0; uint16_t offset1; @@ -215,9 +223,19 @@ struct ble_ll_conn_sm; void ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc); void ble_ll_ctrl_proc_stop(struct ble_ll_conn_sm *connsm, int ctrl_proc); void ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om); -void ble_ll_ctrl_datalen_chg_event(struct ble_ll_conn_sm *connsm); void ble_ll_ctrl_chk_proc_start(struct ble_ll_conn_sm *connsm); void ble_ll_ctrl_terminate_start(struct ble_ll_conn_sm *connsm); int ble_ll_ctrl_is_terminate_ind(uint8_t hdr, uint8_t opcode); +int ble_ll_ctrl_is_reject_ind_ext(uint8_t hdr, uint8_t opcode); +uint8_t ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, + uint8_t *rsp, + struct ble_ll_conn_params *req); +int ble_ll_ctrl_reject_ind_ext_send(struct ble_ll_conn_sm *connsm, + uint8_t rej_opcode, uint8_t err); + +void ble_ll_hci_ev_datalen_chg(struct ble_ll_conn_sm *connsm); +void ble_ll_hci_ev_rem_conn_parm_req(struct ble_ll_conn_sm *connsm, + struct ble_ll_conn_params *cp); +void ble_ll_hci_ev_conn_update(struct ble_ll_conn_sm *connsm, uint8_t status); #endif /* H_BLE_LL_CTRL_ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/net/nimble/controller/include/controller/ble_ll_hci.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_hci.h b/net/nimble/controller/include/controller/ble_ll_hci.h index 32a093a..862fbad 100644 --- a/net/nimble/controller/include/controller/ble_ll_hci.h +++ b/net/nimble/controller/include/controller/ble_ll_hci.h @@ -41,7 +41,7 @@ void ble_ll_hci_init(void); void ble_ll_hci_cmd_proc(struct os_event *ev); /* Used to determine if the LE event is enabled/disabled */ -uint8_t ble_ll_hci_is_le_event_enabled(int bitpos); +uint8_t ble_ll_hci_is_le_event_enabled(int subev); /* Used to determine if event is enabled/disabled */ uint8_t ble_ll_hci_is_event_enabled(int bitpos); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 eb44261..4eb4a8e 100644 --- a/net/nimble/controller/src/ble_ll.c +++ b/net/nimble/controller/src/ble_ll.c @@ -18,6 +18,7 @@ #include <assert.h> #include <string.h> #include "os/os.h" +#include "bsp/bsp.h" #include "nimble/ble.h" #include "nimble/hci_common.h" #include "controller/ble_phy.h" @@ -49,6 +50,7 @@ /* Configuration for supported features */ #define BLE_LL_CFG_FEAT_DATA_LEN_EXT +#undef BLE_LL_CFG_FEAT_CONN_PARAM_REQ #undef BLE_LL_CFG_FEAT_LE_ENCRYPTION #undef BLE_LL_CFG_FEAT_EXT_REJECT_IND @@ -78,7 +80,10 @@ struct ble_ll_log #define BLE_LL_LOG_LEN (256) -static struct ble_ll_log g_ble_ll_log[BLE_LL_LOG_LEN]; +#if !defined(nzbss_t) +#define nzbss_t +#endif +static nzbss_t struct ble_ll_log g_ble_ll_log[BLE_LL_LOG_LEN]; static uint8_t g_ble_ll_log_index; void @@ -939,6 +944,13 @@ ble_ll_init(void) #ifdef BLE_LL_CFG_FEAT_DATA_LEN_EXT features |= BLE_LL_FEAT_DATA_LEN_EXT; #endif +#ifdef BLE_LL_CFG_FEAT_CONN_PARAM_REQ + features |= BLE_LL_FEAT_CONN_PARM_REQ; +#endif +#ifdef BLE_LL_CFG_FEAT_EXT_REJECT_IND + features |= BLE_LL_FEAT_EXTENDED_REJ; +#endif + lldata->ll_supp_features = features; /* Initialize the LL task */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/net/nimble/controller/src/ble_ll_adv.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_adv.c b/net/nimble/controller/src/ble_ll_adv.c index d74b61a..116da9a 100644 --- a/net/nimble/controller/src/ble_ll_adv.c +++ b/net/nimble/controller/src/ble_ll_adv.c @@ -1136,7 +1136,7 @@ ble_ll_adv_event_done(void *arg) os_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); /* For debug purposes */ -#ifdef BLETEST +#ifdef BLETEST_ADV_PKT_NUM bletest_inc_adv_pkt_num(); #endif http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 373909b..a4b3b44 100644 --- a/net/nimble/controller/src/ble_ll_conn.c +++ b/net/nimble/controller/src/ble_ll_conn.c @@ -103,9 +103,9 @@ extern int ble_hs_rx_data(struct os_mbuf *om); #define BLE_LL_CFG_CONN_TX_WIN_SIZE (1) #define BLE_LL_CFG_CONN_TX_WIN_OFF (0) #define BLE_LL_CFG_CONN_MASTER_SCA (BLE_MASTER_SCA_251_500_PPM << 5) -#define BLE_LL_CFG_CONN_MAX_CONNS (8) +#define BLE_LL_CFG_CONN_MAX_CONNS (32) #define BLE_LL_CFG_CONN_OUR_SCA (60) /* in ppm */ -#define BLE_LL_CFG_CONN_INIT_SLOTS (8) +#define BLE_LL_CFG_CONN_INIT_SLOTS (2) /* We cannot have more than 254 connections given our current implementation */ #if (BLE_LL_CFG_CONN_MAX_CONNS >= 255) @@ -142,7 +142,10 @@ struct ble_ll_conn_sm *g_ble_ll_conn_create_sm; struct ble_ll_conn_sm *g_ble_ll_conn_cur_sm; /* Connection state machine array */ -struct ble_ll_conn_sm g_ble_ll_conn_sm[BLE_LL_CFG_CONN_MAX_CONNS]; +#if !defined(nzbss_t) +#define nzbss_t +#endif +nzbss_t struct ble_ll_conn_sm g_ble_ll_conn_sm[BLE_LL_CFG_CONN_MAX_CONNS]; /* List of active connections */ struct ble_ll_conn_active_list g_ble_ll_conn_active_list; @@ -928,6 +931,8 @@ ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm, } /* XXX: for now, just make connection interval equal to max */ + connsm->conn_itvl_min = hcc->conn_itvl_min; + connsm->conn_itvl_max = hcc->conn_itvl_max; connsm->conn_itvl = hcc->conn_itvl_max; /* Check the min/max CE lengths are less than connection interval */ @@ -973,24 +978,35 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm) { struct ble_ll_conn_global_params *conn_params; - /* Reset event counter and last unmapped channel */ - connsm->last_unmapped_chan = 0; + /* Reset following elements */ connsm->event_cntr = 0; connsm->conn_state = BLE_LL_CONN_STATE_IDLE; connsm->allow_slave_latency = 0; connsm->disconnect_reason = 0; connsm->terminate_ind_txd = 0; connsm->terminate_ind_rxd = 0; + connsm->awaiting_host_reply = 0; + connsm->send_conn_upd_event = 0; + connsm->conn_update_scheduled = 0; + connsm->host_expects_upd_event = 0; /* Reset current control procedure */ connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; connsm->pending_ctrl_procs = 0; + /* + * Set handle in connection update procedure to 0. If the handle + * is non-zero it means that the host initiated the connection + * parameter update request and the rest of the parameters are valid. + */ + connsm->conn_param_req.handle = 0; + /* Initialize connection supervision timer */ cputime_timer_init(&connsm->conn_spvn_timer, ble_ll_conn_spvn_timer_cb, connsm); /* Calculate the next data channel */ + connsm->last_unmapped_chan = 0; connsm->data_chan_index = ble_ll_conn_calc_dci(connsm); /* Initialize event */ @@ -1079,7 +1095,7 @@ ble_ll_conn_datalen_update(struct ble_ll_conn_sm *connsm, } if (send_event) { - ble_ll_ctrl_datalen_chg_event(connsm); + ble_ll_hci_ev_datalen_chg(connsm); } } @@ -1151,13 +1167,52 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err) ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle,0,connsm->event_cntr); } +/** + * Called to move to the next connection event. + * + * @param connsm + * + * @return int + */ static int ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) { + uint8_t update_status; uint16_t latency; uint32_t itvl; + uint32_t tmo; uint32_t cur_ww; uint32_t max_ww; + struct ble_ll_conn_upd_req *upd; + + /* + * XXX: we could send the connection update event a bit earlier. The + * way this is coded is that we will generally send it when the + * connection event corresponding to the instant ends. We can send + * it when it begins if we want. + */ + + /* XXX: deal with connection request procedure here as well */ + + /* + * There are two cases where this flag gets set: + * 1) A connection update procedure was started and the event counter + * has passed the instant. + * 2) We successfully sent the reject reason. + */ + if (connsm->host_expects_upd_event) { + update_status = BLE_ERR_SUCCESS; + if (IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE)) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE); + } else { + if (IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + update_status = connsm->reject_reason; + } + } + ble_ll_hci_ev_conn_update(connsm, update_status); + connsm->host_expects_upd_event = 0; + } /* * XXX: not quite sure I am interpreting slave latency correctly here. @@ -1169,7 +1224,7 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) /* 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->allow_slave_latency) { + if (connsm->allow_slave_latency && !connsm->conn_update_scheduled) { if (connsm->pkt_rxd) { latency += connsm->slave_latency; itvl = itvl * latency; @@ -1180,6 +1235,43 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) /* Set next connection event start time */ connsm->anchor_point += cputime_usecs_to_ticks(itvl); + /* + * If a connection update has been scheduled and the event counter + * is now equal to the instant, we need to adjust the start of the + * connection by the the transmit window offset. We also copy in the + * update parameters as they now should take effect. + */ + if (connsm->conn_update_scheduled && + (connsm->event_cntr == connsm->conn_update_req.instant)) { + + /* Set flag so we send connection update event */ + upd = &connsm->conn_update_req; + if ((connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) || + (connsm->conn_itvl != upd->interval) || + (connsm->slave_latency != upd->latency) || + (connsm->supervision_tmo != upd->timeout)) { + connsm->host_expects_upd_event = 1; + } + + connsm->conn_itvl = upd->interval; + connsm->supervision_tmo = upd->timeout; + connsm->slave_latency = upd->latency; + connsm->tx_win_size = upd->winsize; + connsm->slave_cur_tx_win_usecs = + connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; + connsm->tx_win_off = upd->winoffset; + connsm->anchor_point += + cputime_usecs_to_ticks(upd->winoffset * BLE_LL_CONN_ITVL_USECS); + + /* Reset the connection supervision timeout */ + cputime_timer_stop(&connsm->conn_spvn_timer); + tmo = connsm->supervision_tmo * BLE_HCI_CONN_SPVN_TMO_UNITS * 1000; + cputime_timer_start(&connsm->conn_spvn_timer, connsm->anchor_point+tmo); + + /* Reset update scheduled flag */ + connsm->conn_update_scheduled = 0; + } + /* Calculate data channel index of next connection event */ connsm->last_unmapped_chan = connsm->unmapped_chan; while (latency > 0) { @@ -1370,6 +1462,8 @@ ble_ll_conn_event_end(void *arg) /* Set initial schedule callback */ connsm->conn_sch.sched_cb = ble_ll_conn_event_start_cb; + /* XXX: I think all this fine for when we do connection updates, but + we may want to force the first event to be scheduled. Not sure */ /* Schedule the next connection event */ while (ble_ll_sched_conn_reschedule(connsm)) { if (ble_ll_conn_next_event(connsm)) { @@ -1514,7 +1608,7 @@ ble_ll_init_rx_pkt_in(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr) if (connsm && BLE_MBUF_HDR_CRC_OK(ble_hdr) && (ble_hdr->rxinfo.flags & BLE_MBUF_HDR_F_CONN_REQ_TXD)) { /* Set address of advertiser to which we are connecting. */ - if (!ble_ll_scan_whitelist_enabled()) { + if (ble_ll_scan_whitelist_enabled()) { /* * XXX: need to see if the whitelist tells us exactly what peer * addr type we should use? Not sure it matters. If whitelisting @@ -1960,6 +2054,19 @@ ble_ll_conn_rx_isr_end(struct os_mbuf *rxpdu, uint32_t aa) goto conn_rx_pdu_end; } + /* + * Did we transmit a REJECT_IND_EXT? If so we need + * to make sure we send the connection update event + */ + if (ble_ll_ctrl_is_reject_ind_ext(txhdr->txinfo.hdr_byte, + txpdu->om_data[0])) { + if (connsm->cur_ctrl_proc == + BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + connsm->reject_reason = txpdu->om_data[2]; + connsm->host_expects_upd_event = 1; + } + } + /* Increment offset based on number of bytes sent */ txhdr->txinfo.offset += txhdr->txinfo.pyld_len; if (txhdr->txinfo.offset >= pkthdr->omp_len) { @@ -2126,7 +2233,7 @@ ble_ll_conn_tx_pkt_in(struct os_mbuf *om, uint16_t handle, uint16_t length) * * Context: Link Layer * - * @param rxbuf Pointer to received PDU + * @param rxbuf Pointer to received Connect Request PDU * @param conn_req_end receive end time of connect request * * @return 0: connection not started; 1 connecton started @@ -2294,6 +2401,8 @@ ble_ll_conn_module_init(void) */ connsm = &g_ble_ll_conn_sm[0]; for (i = 0; i < BLE_LL_CFG_CONN_MAX_CONNS; ++i) { + memset(connsm, 0, sizeof(struct ble_ll_conn_sm)); + connsm->conn_handle = i + 1; STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 a051318..4a26a9d 100644 --- a/net/nimble/controller/src/ble_ll_conn_hci.c +++ b/net/nimble/controller/src/ble_ll_conn_hci.c @@ -38,6 +38,47 @@ static uint32_t g_ble_ll_next_num_comp_pkt_evt; ((BLE_LL_CFG_NUM_COMP_PKT_RATE * OS_TICKS_PER_SEC) / 1000) /** + * Called to check that the connection parameters are within range + * + * @param itvl_min + * @param itvl_max + * @param latency + * @param spvn_tmo + * + * @return int BLE_ERR_INV_HCI_CMD_PARMS if invalid parameters, 0 otherwise + */ +int +ble_ll_conn_hci_chk_conn_params(uint16_t itvl_min, uint16_t itvl_max, + uint16_t latency, uint16_t spvn_tmo) +{ + uint32_t spvn_tmo_usecs; + uint32_t min_spvn_tmo_usecs; + + if ((itvl_min > itvl_max) || + (itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (itvl_min > BLE_HCI_CONN_ITVL_MAX) || + (latency > BLE_HCI_CONN_LATENCY_MAX) || + (spvn_tmo < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (spvn_tmo > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* + * Supervision timeout (in msecs) must be more than: + * (1 + connLatency) * connIntervalMax * 1.25 msecs * 2. + */ + spvn_tmo_usecs = spvn_tmo; + spvn_tmo_usecs *= (BLE_HCI_CONN_SPVN_TMO_UNITS * 1000); + min_spvn_tmo_usecs = (uint32_t)itvl_max * 2 * BLE_LL_CONN_ITVL_USECS; + min_spvn_tmo_usecs *= (1 + latency); + if (spvn_tmo_usecs <= min_spvn_tmo_usecs) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return BLE_ERR_SUCCESS; +} + +/** * Make a connect request PDU * * @param connsm @@ -102,7 +143,7 @@ ble_ll_conn_comp_event_send(struct ble_ll_conn_sm *connsm, uint8_t status) { uint8_t *evbuf; - if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_COMPLETE - 1)) { + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_COMPLETE)) { evbuf = os_memblock_get(&g_hci_cmd_pool); if (evbuf) { evbuf[0] = BLE_HCI_EVCODE_LE_META; @@ -260,8 +301,6 @@ int ble_ll_conn_create(uint8_t *cmdbuf) { int rc; - uint32_t spvn_tmo_usecs; - uint32_t min_spvn_tmo_usecs; struct hci_create_conn ccdata; struct hci_create_conn *hcc; struct ble_ll_conn_sm *connsm; @@ -311,36 +350,17 @@ ble_ll_conn_create(uint8_t *cmdbuf) return BLE_ERR_INV_HCI_CMD_PARMS; } - /* Check connection interval */ + /* Check connection interval, latency and supervision timeoout */ hcc->conn_itvl_min = le16toh(cmdbuf + 13); hcc->conn_itvl_max = le16toh(cmdbuf + 15); hcc->conn_latency = le16toh(cmdbuf + 17); - if ((hcc->conn_itvl_min > hcc->conn_itvl_max) || - (hcc->conn_itvl_min < BLE_HCI_CONN_ITVL_MIN) || - (hcc->conn_itvl_min > BLE_HCI_CONN_ITVL_MAX) || - (hcc->conn_latency > BLE_HCI_CONN_LATENCY_MAX)) { - return BLE_ERR_INV_HCI_CMD_PARMS; - } - - /* Check supervision timeout */ hcc->supervision_timeout = le16toh(cmdbuf + 19); - 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; - } - - /* - * Supervision timeout (in msecs) must be more than: - * (1 + connLatency) * connIntervalMax * 1.25 msecs * 2. - */ - spvn_tmo_usecs = hcc->supervision_timeout; - spvn_tmo_usecs *= (BLE_HCI_CONN_SPVN_TMO_UNITS * 1000); - min_spvn_tmo_usecs = (uint32_t)hcc->conn_itvl_max * 2 * - BLE_LL_CONN_ITVL_USECS; - min_spvn_tmo_usecs *= (1 + hcc->conn_latency); - if (spvn_tmo_usecs <= min_spvn_tmo_usecs) { - return BLE_ERR_INV_HCI_CMD_PARMS; + rc = ble_ll_conn_hci_chk_conn_params(hcc->conn_itvl_min, + hcc->conn_itvl_max, + hcc->conn_latency, + hcc->supervision_timeout); + if (rc) { + return rc; } /* Min/max connection event lengths */ @@ -376,6 +396,184 @@ ble_ll_conn_create(uint8_t *cmdbuf) return rc; } +static int +ble_ll_conn_process_conn_params(uint8_t *cmdbuf, struct ble_ll_conn_sm *connsm) +{ + int rc; + struct hci_conn_update *hcu; + + /* Retrieve command data */ + hcu = &connsm->conn_param_req; + hcu->handle = connsm->conn_handle; + hcu->conn_itvl_min = le16toh(cmdbuf + 2); + hcu->conn_itvl_max = le16toh(cmdbuf + 4); + hcu->conn_latency = le16toh(cmdbuf + 6); + hcu->supervision_timeout = le16toh(cmdbuf + 8); + hcu->min_ce_len = le16toh(cmdbuf + 10); + hcu->max_ce_len = le16toh(cmdbuf + 12); + + /* Check that parameter values are in range */ + rc = ble_ll_conn_hci_chk_conn_params(hcu->conn_itvl_min, + hcu->conn_itvl_max, + hcu->conn_latency, + hcu->supervision_timeout); + + /* Check valid min/max ce length */ + if (rc || (hcu->min_ce_len > hcu->max_ce_len)) { + hcu->handle = 0; + return BLE_ERR_INV_HCI_CMD_PARMS; + } + return rc; +} + +/** + * Called to process a connection update command. + * + * @param cmdbuf + * + * @return int + */ +int +ble_ll_conn_update(uint8_t *cmdbuf) +{ + int rc; + uint8_t ctrl_proc; + uint16_t handle; + struct ble_ll_conn_sm *connsm; + struct hci_conn_update *hcu; + + /* XXX: must deal with slave not supporting this feature and using + conn update */ + + /* If no connection handle exit with error */ + handle = le16toh(cmdbuf); + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* Better not have this procedure ongoing! */ + if (IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ) || + IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_UPDATE)) { + return BLE_ERR_CMD_DISALLOWED; + } + + /* See if we support this feature */ + if ((ble_ll_read_supp_features() & BLE_LL_FEAT_CONN_PARM_REQ) == 0) { + ctrl_proc = BLE_LL_CTRL_PROC_CONN_UPDATE; + } else { + ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ; + } + + /* + * If we are a slave and the master has initiated the procedure already + * we should deny the slave request for now. If we are a master and the + * slave has initiated the procedure, we need to send a reject to the + * slave. + */ + if (connsm->awaiting_host_reply) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_LMP_COLLISION; + } else { + connsm->awaiting_host_reply = 0; + + /* XXX: If this fails no reject ind will be sent! */ + ble_ll_ctrl_reject_ind_ext_send(connsm, + connsm->host_reply_opcode, + BLE_ERR_LMP_COLLISION); + } + } + + /* Retrieve command data */ + hcu = &connsm->conn_param_req; + hcu->handle = handle; + hcu->conn_itvl_min = le16toh(cmdbuf + 2); + hcu->conn_itvl_max = le16toh(cmdbuf + 4); + hcu->conn_latency = le16toh(cmdbuf + 6); + hcu->supervision_timeout = le16toh(cmdbuf + 8); + hcu->min_ce_len = le16toh(cmdbuf + 10); + hcu->max_ce_len = le16toh(cmdbuf + 12); + if (hcu->min_ce_len > hcu->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check that parameter values are in range */ + rc = ble_ll_conn_hci_chk_conn_params(hcu->conn_itvl_min, + hcu->conn_itvl_max, + hcu->conn_latency, + hcu->supervision_timeout); + if (!rc) { + /* Start the control procedure */ + ble_ll_ctrl_proc_start(connsm, ctrl_proc); + } + + return rc; +} + +int +ble_ll_conn_param_reply(uint8_t *cmdbuf, int positive_reply) +{ + int rc; + uint8_t ble_err; + uint8_t *dptr; + uint8_t rsp_opcode; + uint8_t len; + uint16_t handle; + struct os_mbuf *om; + struct ble_ll_conn_sm *connsm; + + /* See if we support this feature */ + if ((ble_ll_read_supp_features() & BLE_LL_FEAT_CONN_PARM_REQ) == 0) { + return BLE_ERR_UNSUPP_FEATURE; + } + + /* If no connection handle exit with error */ + handle = le16toh(cmdbuf); + + /* If we dont have a handle we cant do anything */ + connsm = ble_ll_conn_find_active_conn(handle); + if (!connsm) { + return BLE_ERR_UNK_CONN_ID; + } + + /* Make sure connection parameters are valid if this is a positive reply */ + rc = BLE_ERR_SUCCESS; + ble_err = cmdbuf[2]; + if (positive_reply) { + rc = ble_ll_conn_process_conn_params(cmdbuf, connsm); + if (rc) { + ble_err = BLE_ERR_CONN_PARMS; + } + } + + /* The connection should be awaiting a reply. If not, just discard */ + if (connsm->awaiting_host_reply) { + /* Get a control packet buffer */ + if (positive_reply && (rc == BLE_ERR_SUCCESS)) { + om = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + if (om) { + dptr = om->om_data; + rsp_opcode = ble_ll_ctrl_conn_param_reply(connsm, dptr, NULL); + dptr[0] = rsp_opcode; + len = g_ble_ll_ctrl_pkt_lengths[rsp_opcode] + 1; + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); + } + } else { + /* XXX: check return code and deal */ + ble_ll_ctrl_reject_ind_ext_send(connsm, + connsm->host_reply_opcode, + ble_err); + } + connsm->awaiting_host_reply = 0; + + /* XXX: if we cant get a buffer, what do we do? We need to remember + * reason if it was a negative reply. We also would need to have + some state to tell us this happened */ + } + + return rc; +} + /** * Called when HCI command to cancel a create connection command has been * received. http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 47015a3..5c3a700 100644 --- a/net/nimble/controller/src/ble_ll_conn_priv.h +++ b/net/nimble/controller/src/ble_ll_conn_priv.h @@ -97,10 +97,14 @@ void ble_ll_disconn_comp_event_send(struct ble_ll_conn_sm *connsm, int ble_ll_conn_hci_disconnect_cmd(uint8_t *cmdbuf); int ble_ll_conn_create(uint8_t *cmdbuf); +int ble_ll_conn_update(uint8_t *cmdbuf); +int ble_ll_conn_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); 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); #endif /* H_BLE_LL_CONN_PRIV_ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 0e5df2d..04b965e 100644 --- a/net/nimble/controller/src/ble_ll_ctrl.c +++ b/net/nimble/controller/src/ble_ll_ctrl.c @@ -49,6 +49,38 @@ * sent (actually attempted to tx). Do we count failures? How? */ + /* XXX: NOTE: we are not supposed to send a REJECT_IND_EXT unless we know the + slave supports that feature */ + +/* XXX: + * 1) One thing I need to make sure I do: if we initiated this procedure and + * we stop it, we have to make sure we send the update complete event! I + * am referring to the connection parameter request procedure. The code is + * already there to send the event when the connection update procedure + * is over. + */ + +/* + * XXX: I definitely have an issue with control procedures and connection + * param request procedure and connection update procedure. This was + * noted when receiving an unknown response. Right now, I am getting confused + * with connection parameter request and updates regarding which procedures + * are running. So I need to go look through all the code and see where I + * used the request procedure and the update procedure and make sure I am doing + * the correct thing. + */ + +/* + * This array contains the length of the CtrData field in LL control PDU's. + * Note that there is a one byte opcode which precedes this field in the LL + * control PDU, so total data channel payload length for the control pdu is + * one greater. + */ +const uint8_t g_ble_ll_ctrl_pkt_lengths[BLE_LL_CTRL_OPCODES] = +{ + 11, 7, 1, 22, 12, 0, 0, 1, 8, 8, 0, 0, 5, 1, 8, 23, 23, 2, 0, 0, 8, 8 +}; + /* XXX: Improvements * 1) We can inititalize the procedure timer once per connection state machine */ @@ -88,10 +120,10 @@ ble_ll_ctrl_len_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr) struct ble_ll_len_req ctrl_req; /* Extract parameters and check if valid */ - ctrl_req.max_rx_bytes = le16toh(dptr + 3); - ctrl_req.max_rx_time = le16toh(dptr + 5); - ctrl_req.max_tx_bytes = le16toh(dptr + 7); - ctrl_req.max_tx_time = le16toh(dptr + 9); + ctrl_req.max_rx_bytes = le16toh(dptr); + ctrl_req.max_rx_time = le16toh(dptr + 2); + ctrl_req.max_tx_bytes = le16toh(dptr + 4); + ctrl_req.max_tx_time = le16toh(dptr + 6); if (!ble_ll_ctrl_chk_supp_bytes(ctrl_req.max_rx_bytes) || !ble_ll_ctrl_chk_supp_bytes(ctrl_req.max_tx_bytes) || @@ -107,6 +139,122 @@ ble_ll_ctrl_len_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr) return rc; } +static int +ble_ll_ctrl_conn_param_pdu_proc(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf, uint8_t opcode) +{ + int rc; + int indicate; + uint8_t rsp_opcode; + uint8_t ble_err; + struct ble_ll_conn_params cp; + struct ble_ll_conn_params *req; + struct hci_conn_update *hcu; + + /* Extract parameters and check if valid */ + cp.interval_min = le16toh(dptr); + cp.interval_max = le16toh(dptr + 2); + cp.latency = le16toh(dptr + 4); + cp.timeout = le16toh(dptr + 6); + cp.pref_periodicity = dptr[8]; + cp.ref_conn_event_cnt = le16toh(dptr + 9); + cp.offset0 = le16toh(dptr + 11); + cp.offset1 = le16toh(dptr + 13); + cp.offset2 = le16toh(dptr + 15); + cp.offset3 = le16toh(dptr + 17); + cp.offset4 = le16toh(dptr + 19); + cp.offset5 = le16toh(dptr + 21); + + /* Check if parameters are valid */ + ble_err = BLE_ERR_SUCCESS; + rc = ble_ll_conn_hci_chk_conn_params(cp.interval_min, + cp.interval_max, + cp.latency, + cp.timeout); + if (rc) { + ble_err = BLE_ERR_INV_LMP_LL_PARM; + goto conn_param_pdu_exit; + } + + /* + * Check if there is a requested change to either the interval, timeout + * or latency. If not, this may just be an anchor point change and we do + * not have to notify the host. + * XXX: what if we dont like the parameters? When do we check that out? + */ + req = NULL; + indicate = 1; + if ((connsm->conn_itvl >= cp.interval_min) && + (connsm->conn_itvl <= cp.interval_max) && + (connsm->supervision_tmo == cp.timeout) && + (connsm->slave_latency == cp.latency)) { + indicate = 0; + /* XXX: For now, if we are a master, we wont send a response that + * needs to remember the request. That might change with connection + update pdu. Not sure. */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + req = &cp; + } + goto conn_parm_req_do_indicate; + } + + /* + * A change has been requested. Is it within the values specified by + * the host? Note that for a master we will not be processing a + * connect param request from a slave if we are currently trying to + * update the connection parameters. This means that the previous + * check is all we need for a master (when receiving a request). + */ + if ((connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) || + (opcode == BLE_LL_CTRL_CONN_PARM_RSP)) { + /* + * Not sure what to do about the slave. It is possible that the + * current connection parameters are not the same ones as the local host + * has provided? Not sure what to do here. Do we need to remember what + * host sent us? For now, I will assume that we need to remember what + * the host sent us and check it out. + */ + hcu = &connsm->conn_param_req; + if (hcu->handle != 0) { + if (!((cp.interval_min < hcu->conn_itvl_min) || + (cp.interval_min > hcu->conn_itvl_max) || + (cp.interval_max < hcu->conn_itvl_min) || + (cp.interval_max > hcu->conn_itvl_max) || + (cp.latency != hcu->conn_latency) || + (cp.timeout != hcu->supervision_timeout))) { + indicate = 0; + } + } + } + +conn_parm_req_do_indicate: + /* + * XXX: are the connection update parameters acceptable? If not, we will + * need to know before we indicate to the host that they are acceptable. + */ + if (indicate) { + /* + * Send event to host. At this point we leave and wait to get + * an answer. + */ + ble_ll_hci_ev_rem_conn_parm_req(connsm, &cp); + connsm->host_reply_opcode = opcode; + connsm->awaiting_host_reply = 1; + rsp_opcode = 255; + } else { + /* Create reply to connection request */ + rsp_opcode = ble_ll_ctrl_conn_param_reply(connsm, rspbuf, req); + } + +conn_param_pdu_exit: + if (ble_err) { + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = opcode; + rspbuf[2] = ble_err; + } + return rsp_opcode; +} + /** * Called to process and UNKNOWN_RSP LL control packet. * @@ -121,47 +269,34 @@ ble_ll_ctrl_proc_unk_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr) uint8_t opcode; /* Get opcode of unknown LL control frame */ - opcode = dptr[3]; + opcode = dptr[0]; + /* XXX: add others here */ /* Convert opcode to control procedure id */ switch (opcode) { case BLE_LL_CTRL_LENGTH_REQ: ctrl_proc = BLE_LL_CTRL_PROC_DATA_LEN_UPD; break; + case BLE_LL_CTRL_CONN_UPDATE_REQ: + ctrl_proc = BLE_LL_CTRL_PROC_CONN_UPDATE; + break; + case BLE_LL_CTRL_CONN_PARM_RSP: + case BLE_LL_CTRL_CONN_PARM_REQ: + ctrl_proc = BLE_LL_CTRL_PROC_CONN_PARAM_REQ; + break; default: ctrl_proc = BLE_LL_CTRL_PROC_NUM; break; } + /* XXX: are there any other events that we need to send when we get + the unknown response? */ /* If we are running this one currently, stop it */ if (connsm->cur_ctrl_proc == ctrl_proc) { /* Stop the control procedure */ ble_ll_ctrl_proc_stop(connsm, ctrl_proc); - } -} - -/** - * Send a data length change event for a connection to the host. - * - * @param connsm Pointer to connection state machine - */ -void -ble_ll_ctrl_datalen_chg_event(struct ble_ll_conn_sm *connsm) -{ - uint8_t *evbuf; - - if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DATA_LEN_CHG - 1)) { - evbuf = os_memblock_get(&g_hci_cmd_pool); - if (evbuf) { - evbuf[0] = BLE_HCI_EVCODE_LE_META; - evbuf[1] = BLE_HCI_LE_DATA_LEN_CHG_LEN; - evbuf[2] = BLE_HCI_LE_SUBEV_DATA_LEN_CHG; - htole16(evbuf + 3, connsm->conn_handle); - htole16(evbuf + 5, connsm->eff_max_tx_octets); - htole16(evbuf + 7, connsm->eff_max_tx_time); - htole16(evbuf + 9, connsm->eff_max_rx_octets); - htole16(evbuf + 11, connsm->eff_max_rx_time); - ble_ll_hci_event_send(evbuf); + if (ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + ble_ll_hci_ev_conn_update(connsm, BLE_ERR_UNSUPP_FEATURE); } } } @@ -185,6 +320,285 @@ ble_ll_ctrl_datalen_upd_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr) } /** + * Called to make a connection parameter request or response control pdu. + * + * @param connsm + * @param dptr Pointer to start of data. NOTE: the opcode is not part + * of the data. + */ +static void +ble_ll_ctrl_conn_param_pdu_make(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + struct ble_ll_conn_params *req) +{ + uint16_t invalid_offset; + struct hci_conn_update *hcu; + + invalid_offset = 0xFFFF; + /* If we were passed in a request, we use the parameters from the request */ + if (req) { + htole16(dptr, req->interval_min); + htole16(dptr + 2, req->interval_max); + htole16(dptr + 4, req->latency); + htole16(dptr + 6, req->timeout); + } else { + hcu = &connsm->conn_param_req; + /* The host should have provided the parameters! */ + assert(hcu->handle != 0); + htole16(dptr, hcu->conn_itvl_min); + htole16(dptr + 2, hcu->conn_itvl_max); + htole16(dptr + 4, hcu->conn_latency); + htole16(dptr + 6, hcu->supervision_timeout); + } + + /* XXX: NOTE: if interval min and interval max are != to each + * other this value should be set to non-zero. I think this + * applies only when an offset field is set. See section 5.1.7.1 pg 103 + * Vol 6 Part B. + */ + /* XXX: for now, set periodicity to 0 */ + dptr[8] = 0; + + /* XXX: deal with reference event count. what to put here? */ + htole16(dptr + 9, connsm->event_cntr); + + /* XXX: For now, dont use offsets */ + htole16(dptr + 11, invalid_offset); + htole16(dptr + 13, invalid_offset); + htole16(dptr + 15, invalid_offset); + htole16(dptr + 17, invalid_offset); + htole16(dptr + 19, invalid_offset); + htole16(dptr + 21, invalid_offset); +} + +/** + * Called to make a connection update request LL control PDU + * + * Context: Link Layer + * + * @param connsm + * @param rsp + */ +static void +ble_ll_ctrl_conn_upd_make(struct ble_ll_conn_sm *connsm, uint8_t *pyld) +{ + uint16_t instant; + struct hci_conn_update *hcu; + struct ble_ll_conn_upd_req *req; + + /* Make sure we have the parameters! */ + assert(connsm->conn_param_req.handle != 0); + + + /* + * Set instant. We set the instant to the current event counter plus + * the amount of slave latency as the slave may not be listening + * at every connection interval and we are not sure when the connect + * request will actually get sent. We add one more event plus the + * minimum as per the spec of 6 connection events. + */ + instant = connsm->event_cntr + connsm->slave_latency + 6 + 1; + + /* + * XXX: This should change in the future, but for now we will just + * start the new instant at the same anchor using win offset 0. + */ + /* Copy parameters in connection update structure */ + hcu = &connsm->conn_param_req; + req = &connsm->conn_update_req; + req->winsize = connsm->tx_win_size; + req->winoffset = 0; + req->interval = hcu->conn_itvl_max; + req->timeout = hcu->supervision_timeout; + req->latency = hcu->conn_latency; + req->instant = instant; + + /* XXX: make sure this works for the connection parameter request proc. */ + pyld[0] = req->winsize; + htole16(pyld + 1, req->winoffset); + htole16(pyld + 3, req->interval); + htole16(pyld + 5, req->latency); + htole16(pyld + 7, req->timeout); + htole16(pyld + 9, instant); + + /* Set flag in state machine to denote we have scheduled an update */ + connsm->conn_update_scheduled = 1; +} + +/** + * Called to respond to a LL control PDU connection parameter request or + * response. + * + * @param connsm + * @param rsp + * @param req + * + * @return uint8_t + */ +uint8_t +ble_ll_ctrl_conn_param_reply(struct ble_ll_conn_sm *connsm, uint8_t *rsp, + struct ble_ll_conn_params *req) +{ + uint8_t rsp_opcode; + + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + /* Create a connection parameter response */ + ble_ll_ctrl_conn_param_pdu_make(connsm, rsp + 1, req); + rsp_opcode = BLE_LL_CTRL_CONN_PARM_RSP; + } else { + /* Create a connection update pdu */ + ble_ll_ctrl_conn_upd_make(connsm, rsp + 1); + rsp_opcode = BLE_LL_CTRL_CONN_UPDATE_REQ; + } + + return rsp_opcode; +} + +/** + * Called when we receive a connection update event + * + * @param connsm + * @param dptr + * @param rspbuf + * + * @return int + */ +static int +ble_ll_ctrl_rx_conn_update(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + uint16_t conn_events; + struct ble_ll_conn_upd_req *reqdata; + + /* Only a slave should receive this */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { + return BLE_ERR_MAX; + } + +#if 0 + /* Deal with receiving this when in this state. I think we are done */ + if (IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + } +#endif + + /* Retrieve parameters */ + reqdata = &connsm->conn_update_req; + reqdata->winsize = dptr[0]; + reqdata->winoffset = le16toh(dptr + 1); + reqdata->interval = le16toh(dptr + 3); + reqdata->latency = le16toh(dptr + 5); + reqdata->timeout = le16toh(dptr + 7); + reqdata->instant = le16toh(dptr + 9); + + /* XXX: validate them at some point. If they dont check out, we + return the unknown response */ + + /* If instant is in the past, we have to end the connection */ + conn_events = (reqdata->instant - connsm->event_cntr) & 0xFFFF; + if (conn_events >= 32767) { + ble_ll_conn_timeout(connsm, BLE_ERR_INSTANT_PASSED); + rsp_opcode = BLE_ERR_MAX; + } else { + connsm->conn_update_scheduled = 1; + } + + return rsp_opcode; +} + +static int +ble_ll_ctrl_rx_conn_param_req(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + + /* + * This is not in the specification per se but it simplifies the + * implementation. If we get a connection parameter request and we + * are awaiting a reply from the host, simply ignore the request. This + * might not be a good idea if the parameters are different, but oh + * well. This is not expected to happen anyway. A return of BLE_ERR_MAX + * means that we will simply discard the connection parameter request + */ + if (connsm->awaiting_host_reply) { + return BLE_ERR_MAX; + } + + /* XXX: remember to deal with this on the master: if the slave has + * initiated a procedure we may have received its connection parameter + * update request and have signaled the host with an event. If that + * is the case, we will need to drop the host command when we get it + and also clear any applicable states. */ + + /* XXX: Read 5.3 again. There are multiple control procedures that might + * be pending (a connection update) that will cause collisions and the + behavior below. */ + /* + * Check for procedure collision (Vol 6 PartB 5.3). If we are a slave + * and we receive a request we "consider the slave initiated + * procedure as complete". This means send a connection update complete + * event (with error). + * + * If a master, we send reject with a + * transaction collision error code. + */ + if (IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + ble_ll_ctrl_proc_stop(connsm, + BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + } else { + connsm->pending_ctrl_procs &= + ~BLE_LL_CTRL_PROC_CONN_PARAM_REQ; + } + ble_ll_hci_ev_conn_update(connsm, BLE_ERR_LMP_COLLISION); + } else { + /* The master sends reject ind ext w/error code 0x23 */ + rsp_opcode = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = BLE_LL_CTRL_CONN_PARM_REQ; + rspbuf[2] = BLE_ERR_LMP_COLLISION; + 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); + return rsp_opcode; +} + +static int +ble_ll_ctrl_rx_conn_param_rsp(struct ble_ll_conn_sm *connsm, uint8_t *dptr, + uint8_t *rspbuf) +{ + uint8_t rsp_opcode; + + /* A slave should never receive this response */ + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + return BLE_ERR_MAX; + } + + /* + * This case should never happen! It means that the slave initiated a + * procedure and the master initiated one as well. If we do get in this + * state just clear the awaiting reply. The slave will hopefully stop its + * procedure when we reply. + */ + if (connsm->awaiting_host_reply) { + connsm->awaiting_host_reply = 0; + } + + /* If we receive a response and no procedure is pending, just leave */ + if (!IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ)) { + return BLE_ERR_MAX; + } + + /* Process the received connection parameter response */ + rsp_opcode = ble_ll_ctrl_conn_param_pdu_proc(connsm, dptr, rspbuf, + BLE_LL_CTRL_CONN_PARM_RSP); + return rsp_opcode; +} + +/** * 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. @@ -224,15 +638,21 @@ ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc) switch (ctrl_proc) { case BLE_LL_CTRL_PROC_DATA_LEN_UPD: - len = BLE_LL_CTRL_LENGTH_REQ_LEN; opcode = BLE_LL_CTRL_LENGTH_REQ; ble_ll_ctrl_datalen_upd_make(connsm, dptr); break; case BLE_LL_CTRL_PROC_TERMINATE: - len = BLE_LL_CTRL_TERMINATE_IND_LEN; opcode = BLE_LL_CTRL_TERMINATE_IND; dptr[1] = connsm->disconnect_reason; break; + case BLE_LL_CTRL_PROC_CONN_PARAM_REQ: + opcode = BLE_LL_CTRL_CONN_PARM_REQ; + ble_ll_ctrl_conn_param_pdu_make(connsm, dptr + 1, NULL); + break; + case BLE_LL_CTRL_PROC_CONN_UPDATE: + opcode = BLE_LL_CTRL_CONN_UPDATE_REQ; + ble_ll_ctrl_conn_upd_make(connsm, dptr + 1); + break; default: assert(0); break; @@ -240,7 +660,7 @@ ble_ll_ctrl_proc_init(struct ble_ll_conn_sm *connsm, int ctrl_proc) /* Set llid, length and opcode */ dptr[0] = opcode; - ++len; + len = g_ble_ll_ctrl_pkt_lengths[opcode] + 1; /* Add packet to transmit queue of connection */ ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); @@ -272,6 +692,29 @@ ble_ll_ctrl_is_terminate_ind(uint8_t hdr, uint8_t opcode) } /** + * Called to determine if the pdu is a TERMINATE_IND + * + * @param hdr + * @param opcode + * + * @return int + */ +int +ble_ll_ctrl_is_reject_ind_ext(uint8_t hdr, uint8_t opcode) +{ + int rc; + + rc = 0; + if ((hdr & BLE_LL_DATA_HDR_LLID_MASK) == BLE_LL_LLID_CTRL) { + if (opcode == BLE_LL_CTRL_REJECT_IND_EXT) { + rc = 1; + } + } + return rc; +} + + +/** * Stops the LL control procedure indicated by 'ctrl_proc'. * * Context: Link Layer task @@ -282,11 +725,11 @@ ble_ll_ctrl_is_terminate_ind(uint8_t hdr, uint8_t opcode) void ble_ll_ctrl_proc_stop(struct ble_ll_conn_sm *connsm, int ctrl_proc) { - if (connsm->cur_ctrl_proc == ctrl_proc) { - os_callout_stop(&connsm->ctrl_proc_rsp_timer.cf_c); - connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; - connsm->pending_ctrl_procs &= ~(1 << ctrl_proc); - } + assert(connsm->cur_ctrl_proc == ctrl_proc); + + os_callout_stop(&connsm->ctrl_proc_rsp_timer.cf_c); + connsm->cur_ctrl_proc = BLE_LL_CTRL_PROC_IDLE; + connsm->pending_ctrl_procs &= ~(1 << ctrl_proc); /* If there are others, start them */ ble_ll_ctrl_chk_proc_start(connsm); @@ -352,7 +795,7 @@ ble_ll_ctrl_proc_start(struct ble_ll_conn_sm *connsm, int ctrl_proc) /* Re-start the timer. Control procedure timeout is 40 seconds */ os_callout_reset(&connsm->ctrl_proc_rsp_timer.cf_c, - OS_TICKS_PER_SEC * 40); + OS_TICKS_PER_SEC * BLE_LL_CTRL_PROC_TIMEOUT); } } @@ -378,7 +821,7 @@ ble_ll_ctrl_chk_proc_start(struct ble_ll_conn_sm *connsm) /* * If the terminate procedure is not pending it means we were not * able to start it right away (no control pdu was available). - * Start it now + * Start it now. */ ble_ll_ctrl_terminate_start(connsm); return; @@ -413,70 +856,91 @@ void ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) { uint8_t features; + uint8_t feature; uint8_t len; uint8_t opcode; + uint8_t rsp_opcode; uint8_t *dptr; + uint8_t *rspbuf; /* XXX: where do we validate length received and packet header length? * do this in LL task when received. Someplace!!! What I mean * is we should validate the over the air length with the mbuf length. Should the PHY do that???? */ - /* Get length and opcode from PDU */ + /* Get length and opcode from PDU.*/ dptr = om->om_data; + rspbuf = dptr; len = dptr[1]; opcode = dptr[2]; + /* Move data pointer to start of control data (2 byte PDU hdr + opcode) */ + dptr += (BLE_LL_PDU_HDR_LEN + 1); + + /* + * Subtract the opcode from the length. Note that if the length was zero, + * which would be an error, we will fail the check against the length + * of the control packet. + */ + --len; + /* opcode must be good */ - if ((opcode > BLE_LL_CTRL_LENGTH_RSP) || (len < 1) || - (len > BLE_LL_CTRL_MAX_PAYLOAD)) { + if ((opcode >= BLE_LL_CTRL_OPCODES) || + (len != g_ble_ll_ctrl_pkt_lengths[opcode])) { goto rx_malformed_ctrl; } - /* Subtract the opcode from the length */ - --len; - + /* Check if the feature is supported. */ switch (opcode) { case BLE_LL_CTRL_LENGTH_REQ: - /* Check length */ - if (len != BLE_LL_CTRL_LENGTH_REQ_LEN) { - goto rx_malformed_ctrl; - } + feature = BLE_LL_FEAT_DATA_LEN_EXT; + break; + case BLE_LL_CTRL_CONN_PARM_REQ: + case BLE_LL_CTRL_CONN_PARM_RSP: + feature = BLE_LL_FEAT_CONN_PARM_REQ; + break; + default: + feature = 0; + break; + } - /* Check parameters for validity */ + if (feature) { features = ble_ll_read_supp_features(); - if (features & BLE_LL_FEAT_DATA_LEN_EXT) { - /* Extract parameters and check if valid */ - if (ble_ll_ctrl_len_proc(connsm, dptr)) { - goto rx_malformed_ctrl; - } - - /* - * If we have not started this procedure ourselves and it is - * pending, no need to perform it. - */ - if ((connsm->cur_ctrl_proc != BLE_LL_CTRL_PROC_DATA_LEN_UPD) && - IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD)) { - connsm->pending_ctrl_procs &= ~BLE_LL_CTRL_PROC_DATA_LEN_UPD; - } - - /* Send a response */ - opcode = BLE_LL_CTRL_LENGTH_RSP; - ble_ll_ctrl_datalen_upd_make(connsm, dptr); - } else { - /* XXX: construct unknown pdu */ - opcode = BLE_LL_CTRL_UNKNOWN_RSP; - len = BLE_LL_CTRL_UNK_RSP_LEN; - dptr[1] = BLE_LL_CTRL_LENGTH_REQ; + if ((features & feature) == 0) { + /* Construct unknown rsp pdu */ + rspbuf[1] = opcode; + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + goto ll_ctrl_send_rsp; } + } + + /* Process opcode */ + rsp_opcode = 255; + switch (opcode) { + case BLE_LL_CTRL_CONN_UPDATE_REQ: + rsp_opcode = ble_ll_ctrl_rx_conn_update(connsm, dptr, rspbuf); break; - case BLE_LL_CTRL_LENGTH_RSP: - /* Check length (response length same as request length) */ - if (len != BLE_LL_CTRL_LENGTH_REQ_LEN) { + case BLE_LL_CTRL_LENGTH_REQ: + /* Extract parameters and check if valid */ + if (ble_ll_ctrl_len_proc(connsm, dptr)) { goto rx_malformed_ctrl; } /* + * If we have not started this procedure ourselves and it is + * pending, no need to perform it. + */ + if ((connsm->cur_ctrl_proc != BLE_LL_CTRL_PROC_DATA_LEN_UPD) && + IS_PENDING_CTRL_PROC_M(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD)) { + connsm->pending_ctrl_procs &= ~BLE_LL_CTRL_PROC_DATA_LEN_UPD; + } + + /* Send a response */ + rsp_opcode = BLE_LL_CTRL_LENGTH_RSP; + ble_ll_ctrl_datalen_upd_make(connsm, rspbuf); + break; + case BLE_LL_CTRL_LENGTH_RSP: + /* * According to specification, we should only process this if we * asked for it. */ @@ -489,29 +953,49 @@ ble_ll_ctrl_rx_pdu(struct ble_ll_conn_sm *connsm, struct os_mbuf *om) /* Stop the control procedure */ ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_DATA_LEN_UPD); } - opcode = 255; break; case BLE_LL_CTRL_UNKNOWN_RSP: - /* Check length (response length same as request length) */ - if (len != BLE_LL_CTRL_UNK_RSP_LEN) { - goto rx_malformed_ctrl; - } - ble_ll_ctrl_proc_unk_rsp(connsm, dptr); - opcode = 255; + break; + + /* XXX: remember to check if feature supported */ + case BLE_LL_CTRL_CHANNEL_MAP_REQ: + case BLE_LL_CTRL_ENC_REQ: + case BLE_LL_CTRL_START_ENC_REQ: + case BLE_LL_CTRL_FEATURE_REQ: + case BLE_LL_CTRL_PAUSE_ENC_REQ: + case BLE_LL_CTRL_SLAVE_FEATURE_REQ: + /* Construct unknown pdu */ + rspbuf[1] = opcode; + rsp_opcode = BLE_LL_CTRL_UNKNOWN_RSP; + break; + case BLE_LL_CTRL_CONN_PARM_REQ: + rsp_opcode = ble_ll_ctrl_rx_conn_param_req(connsm, dptr, rspbuf); + break; + case BLE_LL_CTRL_CONN_PARM_RSP: + rsp_opcode = ble_ll_ctrl_rx_conn_param_rsp(connsm, dptr, rspbuf); + break; + case BLE_LL_CTRL_REJECT_IND_EXT: + /* XXX: not sure what other control procedures to check out, but + add them when needed */ + /* XXX: should I check to make sure that the rejected opcode is sane? */ + if (connsm->cur_ctrl_proc == BLE_LL_CTRL_PROC_CONN_PARAM_REQ) { + ble_ll_ctrl_proc_stop(connsm, BLE_LL_CTRL_PROC_CONN_PARAM_REQ); + ble_ll_hci_ev_conn_update(connsm, dptr[1]); + } break; default: - /* XXX: this is an un-implemented control procedure. What to do? */ - opcode = 255; + /* We really should never get here */ break; } /* Free mbuf or send response */ - if (opcode == 255) { +ll_ctrl_send_rsp: + if (rsp_opcode == 255) { os_mbuf_free(om); } else { - ++len; - dptr[0] = opcode; + rspbuf[0] = rsp_opcode; + len = g_ble_ll_ctrl_pkt_lengths[rsp_opcode] + 1; ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); } return; @@ -521,3 +1005,37 @@ rx_malformed_ctrl: ++g_ble_ll_stats.rx_malformed_ctrl_pdus; return; } + +/** + * Called to creeate and send a REJECT_IND_EXT control PDU + * + * + * @param connsm + * @param rej_opcode + * @param err + * + * @return int + */ +int +ble_ll_ctrl_reject_ind_ext_send(struct ble_ll_conn_sm *connsm, + uint8_t rej_opcode, uint8_t err) +{ + int rc; + uint8_t len; + uint8_t *rspbuf; + struct os_mbuf *om; + + om = os_mbuf_get_pkthdr(&g_mbuf_pool, sizeof(struct ble_mbuf_hdr)); + if (om) { + rspbuf = om->om_data; + rspbuf[0] = BLE_LL_CTRL_REJECT_IND_EXT; + rspbuf[1] = rej_opcode; + rspbuf[2] = err; + len = BLE_LL_CTRL_REJECT_IND_EXT_LEN + 1; + ble_ll_conn_enqueue_pkt(connsm, om, BLE_LL_LLID_CTRL, len); + rc = 0; + } else { + rc = 1; + } + return rc; +} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/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 3f6bd95..112e7e8 100644 --- a/net/nimble/controller/src/ble_ll_hci.c +++ b/net/nimble/controller/src/ble_ll_hci.c @@ -157,20 +157,22 @@ ble_ll_hci_le_read_local_features(uint8_t *rspbuf, uint8_t *rsplen) /** * Checks to see if a LE event has been disabled by the host. * - * @param bitpos This is the bit position of the LE event. Note that this can + * @param subev Sub-event code of the LE Meta event. Note that this can * be a value from 0 to 63, inclusive. * * @return uint8_t 0: event is not enabled; otherwise event is enabled. */ uint8_t -ble_ll_hci_is_le_event_enabled(int bitpos) +ble_ll_hci_is_le_event_enabled(int subev) { uint8_t enabled; uint8_t bytenum; uint8_t bitmask; + int bitpos; /* The LE meta event must be enabled for any LE event to be enabled */ enabled = 0; + bitpos = subev - 1; if (g_ble_ll_hci_event_mask[7] & 0x20) { bytenum = bitpos / 8; bitmask = 1 << (bitpos & 0x7); @@ -332,6 +334,26 @@ ble_ll_hci_le_cmd_proc(uint8_t *cmdbuf, uint16_t ocf, uint8_t *rsplen) 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_update(cmdbuf); + } + /* This is a hack; command status gets sent instead of cmd complete */ + rc += (BLE_ERR_MAX + 1); + break; + + case BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR: + if (len == BLE_HCI_CONN_PARAM_NEG_REPLY_LEN) { + rc = ble_ll_conn_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_param_reply(cmdbuf, 1); + } + break; + default: rc = BLE_ERR_UNKNOWN_HCI_CMD; break; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/net/nimble/controller/src/ble_ll_scan.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_scan.c b/net/nimble/controller/src/ble_ll_scan.c index 1b2ec0b..93aca0e 100644 --- a/net/nimble/controller/src/ble_ll_scan.c +++ b/net/nimble/controller/src/ble_ll_scan.c @@ -124,46 +124,6 @@ ble_ll_scan_req_backoff(struct ble_ll_scan_sm *scansm, int success) } /** - * Called to determine if we are inside or outside the scan window. If we - * are inside the scan window it means that the device should be receiving - * on the scan channel. - * - * Context: Interrupt and Link Layer - * - * @param scansm - * - * @return int 0: inside scan window 1: outside scan window - */ -int -ble_ll_scan_window_chk(struct ble_ll_scan_sm *scansm) -{ - int rc; - uint32_t now; - uint32_t itvl; - - rc = 0; - itvl = cputime_usecs_to_ticks(scansm->scan_itvl * BLE_HCI_SCAN_ITVL); - now = cputime_get32(); - while ((int32_t)(now - scansm->scan_win_start_time) >= itvl) { - scansm->scan_win_start_time += itvl; - ++scansm->scan_chan; - if (scansm->scan_chan == BLE_PHY_NUM_CHANS) { - scansm->scan_chan = BLE_PHY_ADV_CHAN_START; - } - } - - if (scansm->scan_window != scansm->scan_itvl) { - itvl = cputime_usecs_to_ticks(scansm->scan_window * - BLE_HCI_SCAN_ITVL); - if ((now - scansm->scan_win_start_time) >= itvl) { - rc = 1; - } - } - - return rc; -} - -/** * ble ll scan req pdu make * * Construct a SCAN_REQ PDU. @@ -431,7 +391,7 @@ ble_ll_hci_send_adv_report(uint8_t pdu_type, uint8_t txadd, uint8_t *rxbuf, adv_data_len -= BLE_DEV_ADDR_LEN; } - if (ble_ll_hci_is_le_event_enabled(subev - 1)) { + if (ble_ll_hci_is_le_event_enabled(subev)) { evbuf = os_memblock_get(&g_hci_cmd_pool); if (evbuf) { evbuf[0] = BLE_HCI_EVCODE_LE_META; @@ -539,12 +499,12 @@ ble_ll_scan_chk_filter_policy(uint8_t pdu_type, uint8_t *rxbuf, uint8_t flags) * @return int */ static int -ble_ll_scan_start(struct ble_ll_scan_sm *scansm) +ble_ll_scan_start(struct ble_ll_scan_sm *scansm, uint8_t chan) { int rc; /* Set channel */ - rc = ble_phy_setchan(scansm->scan_chan, 0, 0); + rc = ble_phy_setchan(chan, 0, 0); assert(rc == 0); /* @@ -568,7 +528,6 @@ ble_ll_scan_start(struct ble_ll_scan_sm *scansm) /* Set end time to end of scan window */ rc = BLE_LL_SCHED_STATE_RUNNING; } - scansm->last_sched_time = cputime_get32(); /* If there is a still a scan response pending, we have failed! */ if (scansm->scan_rsp_pending) { @@ -578,6 +537,75 @@ ble_ll_scan_start(struct ble_ll_scan_sm *scansm) return rc; } +static void +ble_ll_scan_update_window(struct ble_ll_scan_sm *scansm, uint32_t cputime) +{ + uint8_t chan; + uint32_t itvl; + uint32_t win_start; + + itvl = cputime_usecs_to_ticks(scansm->scan_itvl * BLE_HCI_SCAN_ITVL); + chan = scansm->scan_chan; + win_start = scansm->scan_win_start_time; + while ((int32_t)(cputime - win_start) >= itvl) { + win_start += itvl; + ++chan; + if (chan == BLE_PHY_NUM_CHANS) { + chan = BLE_PHY_ADV_CHAN_START; + } + } + + scansm->scan_chan = chan; + scansm->scan_win_start_time = win_start; + scansm->last_sched_time = cputime; +} + +/** + * Called to determine if we are inside or outside the scan window. If we + * are inside the scan window it means that the device should be receiving + * on the scan channel. + * + * Context: Interrupt and Link Layer + * + * @param scansm + * + * @return int 0: inside scan window 1: outside scan window + */ +static int +ble_ll_scan_window_chk(struct ble_ll_scan_sm *scansm, uint32_t cputime) +{ + int rc; + uint8_t chan; + uint32_t itvl; + uint32_t win_start; + + itvl = cputime_usecs_to_ticks(scansm->scan_itvl * BLE_HCI_SCAN_ITVL); + chan = scansm->scan_chan; + win_start = scansm->scan_win_start_time; + while ((int32_t)(cputime - win_start) >= itvl) { + win_start += itvl; + ++chan; + if (chan == BLE_PHY_NUM_CHANS) { + chan = BLE_PHY_ADV_CHAN_START; + } + } + + rc = 0; + if (scansm->scan_window != scansm->scan_itvl) { + itvl = cputime_usecs_to_ticks(scansm->scan_window * BLE_HCI_SCAN_ITVL); + if ((cputime - win_start) >= itvl) { + rc = 1; + } + } + + if (!rc) { + /* Turn on the receiver and set state */ + ble_ll_scan_start(scansm, chan); + } + + return rc; +} + /** * Called when the scanning schedule item executes * @@ -591,6 +619,7 @@ int ble_ll_scan_sched_cb(struct ble_ll_sched_item *sch) { int rc; + uint32_t now; struct ble_ll_scan_sm *scansm; /* @@ -618,23 +647,22 @@ ble_ll_scan_sched_cb(struct ble_ll_sched_item *sch) break; } + now = cputime_get32(); scansm = (struct ble_ll_scan_sm *)sch->cb_arg; if (rc) { rc = BLE_LL_SCHED_STATE_DONE; } else { /* Determine if we should be off or receiving */ - rc = ble_ll_scan_window_chk(scansm); + rc = ble_ll_scan_window_chk(scansm, now); if (!rc) { - ble_ll_scan_start(scansm); rc = BLE_LL_SCHED_STATE_RUNNING; } else { rc = BLE_LL_SCHED_STATE_DONE; } } - if (rc == BLE_LL_SCHED_STATE_DONE) { - scansm->last_sched_time = cputime_get32(); - } + /* Update scan window start time and channel */ + ble_ll_scan_update_window(scansm, now); /* Post scanning scheduled done event */ ble_ll_event_send(&scansm->scan_sched_ev); @@ -733,6 +761,7 @@ ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm) /* XXX: align to current or next slot???. */ /* Schedule start time now */ scansm->scan_win_start_time = cputime_get32(); + scansm->last_sched_time = scansm->scan_win_start_time; /* * If we are in standby, start scanning. Otherwise, scanning will resume @@ -740,7 +769,7 @@ ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm) */ OS_ENTER_CRITICAL(sr); if (ble_ll_state_get() == BLE_LL_STATE_STANDBY) { - ble_ll_scan_start(scansm); + ble_ll_scan_start(scansm, BLE_PHY_ADV_CHAN_START); } OS_EXIT_CRITICAL(sr); @@ -986,10 +1015,7 @@ ble_ll_scan_chk_resume(void) if (scansm->scan_enabled) { OS_ENTER_CRITICAL(sr); if (ble_ll_state_get() == BLE_LL_STATE_STANDBY) { - if (!ble_ll_scan_window_chk(scansm)) { - /* Turn on the receiver and set state */ - ble_ll_scan_start(scansm); - } + ble_ll_scan_window_chk(scansm, cputime_get32()); } OS_EXIT_CRITICAL(sr); } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/262b1be2/project/bletest/src/main.c ---------------------------------------------------------------------- diff --git a/project/bletest/src/main.c b/project/bletest/src/main.c index a3c1466..67795f5 100755 --- a/project/bletest/src/main.c +++ b/project/bletest/src/main.c @@ -25,6 +25,7 @@ /* BLE */ #include "nimble/ble.h" #include "nimble/hci_transport.h" +#include "nimble/hci_common.h" #include "host/host_hci.h" #include "host/ble_hs.h" #include "controller/ble_ll.h" @@ -33,9 +34,6 @@ #include "controller/ble_ll_scan.h" #include "controller/ble_ll_adv.h" -/* Init all tasks */ -volatile int tasks_initialized; - /* Task 1 */ #define HOST_TASK_PRIO (1) @@ -53,7 +51,7 @@ uint8_t g_host_adv_data[BLE_HCI_MAX_ADV_DATA_LEN]; uint8_t g_host_adv_len; /* Create a mbuf pool of BLE mbufs */ -#define MBUF_NUM_MBUFS (20) +#define MBUF_NUM_MBUFS (40) #define MBUF_BUF_SIZE \ ((BLE_LL_CFG_ACL_DATA_PKT_LEN + sizeof(struct hci_data_hdr) + 3) & 0xFFFC) #define MBUF_MEMBLOCK_SIZE (MBUF_BUF_SIZE + BLE_MBUF_PKT_OVERHEAD) @@ -77,15 +75,16 @@ os_membuf_t g_mbuf_buffer[MBUF_MEMPOOL_SIZE]; #define BLETEST_CFG_SCAN_WINDOW (700000 / BLE_HCI_SCAN_ITVL) #define BLETEST_CFG_SCAN_TYPE (BLE_HCI_SCAN_TYPE_ACTIVE) #define BLETEST_CFG_SCAN_FILT_POLICY (BLE_HCI_SCAN_FILT_NO_WL) -#define BLETEST_CFG_CONN_ITVL (1000) /* in 1.25 msec increments */ +#define BLETEST_CFG_CONN_ITVL (64) /* in 1.25 msec increments */ #define BLETEST_CFG_SLAVE_LATENCY (0) #define BLETEST_CFG_INIT_FILTER_POLICY (BLE_HCI_CONN_FILT_NO_WL) -#define BLETEST_CFG_CONN_SPVN_TMO (1000) /* 10 seconds */ +#define BLETEST_CFG_CONN_SPVN_TMO (2000) /* 20 seconds */ #define BLETEST_CFG_MIN_CE_LEN (6) #define BLETEST_CFG_MAX_CE_LEN (BLETEST_CFG_CONN_ITVL) -#define BLETEST_CFG_CONCURRENT_CONNS (8) +#define BLETEST_CFG_CONCURRENT_CONNS (1) /* BLETEST variables */ +#undef BLETEST_ADV_PKT_NUM #define BLETEST_PKT_SIZE (128) #define BLETEST_STACK_SIZE (256) #define BLETEST_TASK_PRIO (HOST_TASK_PRIO + 1) @@ -94,13 +93,16 @@ int g_bletest_state; struct os_eventq g_bletest_evq; struct os_callout_func g_bletest_timer; struct os_task bletest_task; -os_stack_t bletest_stack[BLETEST_STACK_SIZE]; +#if !defined(nzbss_t) +#define nzbss_t +#endif +nzbss_t os_stack_t bletest_stack[BLETEST_STACK_SIZE]; uint32_t g_bletest_conn_end; uint8_t g_bletest_current_conns; uint8_t g_bletest_cur_peer_addr[BLE_DEV_ADDR_LEN]; uint8_t g_last_handle_used; -#if 0 +#ifdef BLETEST_ADV_PKT_NUM void bletest_inc_adv_pkt_num(void) { @@ -128,12 +130,6 @@ bletest_inc_adv_pkt_num(void) host_hci_outstanding_opcode = 0; } } -#else -void -bletest_inc_adv_pkt_num(void) -{ - return; -} #endif /** @@ -333,7 +329,9 @@ bletest_init_initiator(void) void bletest_execute(void) { + int rc; uint16_t handle; + struct hci_conn_update hcu; /* * Determine if there is an active connection for the current handle @@ -342,6 +340,9 @@ bletest_execute(void) if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) { handle = g_bletest_current_conns + 1; if (ble_ll_conn_find_active_conn(handle)) { + /* Set next os time to start the connection update */ + g_next_os_time = 0; + /* Scanning better be stopped! */ assert(ble_ll_scan_enabled() == 0); @@ -356,6 +357,30 @@ bletest_execute(void) bletest_init_initiator(); } } + } else { + /* Issue a connection parameter update to connection handle 1 */ + if (g_next_os_time == 0) { + g_next_os_time = os_time_get(); + g_next_os_time += OS_TICKS_PER_SEC * 5; + } else { + if (g_next_os_time != 0xffffffff) { + if ((int32_t)(os_time_get() - g_next_os_time) >= 0) { + hcu.conn_latency = 0; + hcu.supervision_timeout = 1000; + hcu.conn_itvl_min = 1000; + hcu.conn_itvl_max = 1000; + hcu.handle = 1; + hcu.min_ce_len = 4; + hcu.max_ce_len = 4; + + rc = host_hci_cmd_le_conn_update(&hcu); + assert(rc == 0); + host_hci_outstanding_opcode = 0; + + g_next_os_time = 0xffffffff; + } + } + } } } #endif @@ -488,7 +513,7 @@ bletest_execute(void) if (g_bletest_current_conns) { for (i = 0; i < g_bletest_current_conns; ++i) { if ((g_last_handle_used == 0) || - (g_last_handle_used >= g_bletest_current_conns)) { + (g_last_handle_used > g_bletest_current_conns)) { g_last_handle_used = 1; } handle = g_last_handle_used; @@ -653,8 +678,6 @@ init_tasks(void) BLETEST_TASK_PRIO, OS_WAIT_FOREVER, bletest_stack, BLETEST_STACK_SIZE); - tasks_initialized = 1; - /* Initialize host HCI */ rc = ble_hs_init(HOST_TASK_PRIO); assert(rc == 0);