mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-11-01 10:06:11 +00:00
Initial commit for the "body sensor network" code changes.
X-Original-Commit: 4d4c3e18284af0d2bd2e085c89c71078c5d0d01c
This commit is contained in:
@@ -213,6 +213,13 @@ struct ble_ll_conn_sm
|
||||
uint8_t last_unmapped_chan;
|
||||
uint8_t num_used_chans;
|
||||
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
uint8_t period_occ_mask; /* mask: period 0 = 0x01, period 3 = 0x08 */
|
||||
#endif
|
||||
|
||||
/* RSSI */
|
||||
int8_t conn_rssi;
|
||||
|
||||
/* Ack/Flow Control */
|
||||
uint8_t tx_seqnum; /* note: can be 1 bit */
|
||||
uint8_t next_exp_seqnum; /* note: can be 1 bit */
|
||||
|
||||
@@ -77,6 +77,40 @@ extern uint8_t g_ble_ll_sched_offset_ticks;
|
||||
struct ble_ll_sched_item;
|
||||
typedef int (*sched_cb_func)(struct ble_ll_sched_item *sch);
|
||||
|
||||
/*
|
||||
* Strict connection scheduling (for the master) is different than how
|
||||
* connections are normally scheduled. With strict connection scheduling we
|
||||
* introduce the concept of a "period". A period is a collection of slots. Each
|
||||
* slot is 1.25 msecs in length. The number of slots in a period is determined
|
||||
* by the syscfg value BLE_LL_CONN_INIT_SLOTS. A collection of periods is called
|
||||
* an epoch. The length of an epoch is determined by the number of connections
|
||||
* (BLE_MAX_CONNECTIONS plus BLE_LL_ADD_STRICT_SCHED_PERIODS). Connections
|
||||
* will be scheduled at period boundaries. Any scanning/initiating/advertising
|
||||
* will be done in unused periods, if possible.
|
||||
*/
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
#define BLE_LL_SCHED_PERIODS (MYNEWT_VAL(BLE_MAX_CONNECTIONS) + \
|
||||
MYNEWT_VAL(BLE_LL_ADD_STRICT_SCHED_PERIODS))
|
||||
|
||||
struct ble_ll_sched_obj
|
||||
{
|
||||
uint8_t sch_num_occ_periods;
|
||||
uint32_t sch_occ_period_mask;
|
||||
uint32_t sch_ticks_per_period;
|
||||
uint32_t sch_ticks_per_epoch;
|
||||
uint32_t sch_epoch_start;
|
||||
};
|
||||
|
||||
extern struct ble_ll_sched_obj g_ble_ll_sched_data;
|
||||
|
||||
/*
|
||||
* XXX: TODO:
|
||||
* -> How do we know epoch start is up to date? Not wrapped?
|
||||
* -> for now, only do this with no more than 32 connections.
|
||||
* -> Do not let initiating occur if no empty sched slots
|
||||
*/
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Schedule item
|
||||
* sched_type: This is the type of the schedule item.
|
||||
|
||||
@@ -1195,6 +1195,9 @@ ble_ll_reset(void)
|
||||
/* All this does is re-initialize the event masks so call the hci init */
|
||||
ble_ll_hci_init();
|
||||
|
||||
/* Reset scheduler */
|
||||
ble_ll_sched_init();
|
||||
|
||||
/* Set state to standby */
|
||||
ble_ll_state_set(BLE_LL_STATE_STANDBY);
|
||||
|
||||
|
||||
@@ -2021,6 +2021,9 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
|
||||
uint8_t *evbuf;
|
||||
struct os_mbuf *m;
|
||||
struct os_mbuf_pkthdr *pkthdr;
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
os_sr_t sr;
|
||||
#endif
|
||||
|
||||
/* Remove scheduler events just in case */
|
||||
ble_ll_sched_rmv_elem(&connsm->conn_sch);
|
||||
@@ -2057,6 +2060,16 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
|
||||
/* Make sure events off queue */
|
||||
os_eventq_remove(&g_ble_ll_data.ll_evq, &connsm->conn_ev_end);
|
||||
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
/* Remove from occupied periods */
|
||||
OS_ENTER_CRITICAL(sr);
|
||||
assert(g_ble_ll_sched_data.sch_num_occ_periods > 0);
|
||||
assert(g_ble_ll_sched_data.sch_occ_period_mask & connsm->period_occ_mask);
|
||||
--g_ble_ll_sched_data.sch_num_occ_periods;
|
||||
g_ble_ll_sched_data.sch_occ_period_mask &= ~connsm->period_occ_mask;
|
||||
OS_EXIT_CRITICAL(sr);
|
||||
#endif
|
||||
|
||||
/* Connection state machine is now idle */
|
||||
connsm->conn_state = BLE_LL_CONN_STATE_IDLE;
|
||||
|
||||
@@ -2101,7 +2114,8 @@ ble_ll_conn_end(struct ble_ll_conn_sm *connsm, uint8_t ble_err)
|
||||
STAILQ_INSERT_TAIL(&g_ble_ll_conn_free_list, connsm, free_stqe);
|
||||
|
||||
/* Log connection end */
|
||||
ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle,0,connsm->event_cntr);
|
||||
ble_ll_log(BLE_LL_LOG_ID_CONN_END,connsm->conn_handle, ble_err,
|
||||
connsm->event_cntr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2294,7 +2308,11 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm)
|
||||
* Calculate ce end time. For a slave, we need to add window widening and
|
||||
* the transmit window if we still have one.
|
||||
*/
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
itvl = g_ble_ll_sched_data.sch_ticks_per_period;
|
||||
#else
|
||||
itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT;
|
||||
#endif
|
||||
if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) {
|
||||
cur_ww = ble_ll_conn_calc_window_widening(connsm);
|
||||
max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS;
|
||||
@@ -2397,10 +2415,16 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr)
|
||||
|
||||
connsm->slave_cur_tx_win_usecs =
|
||||
connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS;
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
connsm->ce_end_time = connsm->anchor_point +
|
||||
g_ble_ll_sched_data.sch_ticks_per_period +
|
||||
os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1;
|
||||
|
||||
#else
|
||||
connsm->ce_end_time = connsm->anchor_point +
|
||||
(MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT)
|
||||
+ os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1;
|
||||
#endif
|
||||
connsm->slave_cur_window_widening = BLE_LL_JITTER_USECS;
|
||||
|
||||
/* Start the scheduler for the first connection event */
|
||||
|
||||
@@ -65,6 +65,10 @@ int32_t g_ble_ll_sched_max_early;
|
||||
/* Queue for timers */
|
||||
TAILQ_HEAD(ll_sched_qhead, ble_ll_sched_item) g_ble_ll_sched_q;
|
||||
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
struct ble_ll_sched_obj g_ble_ll_sched_data;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks if two events in the schedule will overlap in time. NOTE: consecutive
|
||||
* schedule items can end and start at the same time.
|
||||
@@ -295,6 +299,238 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
int
|
||||
ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend,
|
||||
uint8_t req_slots)
|
||||
{
|
||||
int rc;
|
||||
os_sr_t sr;
|
||||
uint32_t initial_start;
|
||||
uint32_t earliest_start;
|
||||
uint32_t earliest_end;
|
||||
uint32_t dur;
|
||||
uint32_t itvl_t;
|
||||
|
||||
int i;
|
||||
uint32_t tpp;
|
||||
uint32_t tse;
|
||||
uint32_t np;
|
||||
uint32_t cp;
|
||||
uint32_t tick_in_period;
|
||||
|
||||
struct ble_ll_sched_item *entry;
|
||||
struct ble_ll_sched_item *sch;
|
||||
|
||||
/* Better have a connsm */
|
||||
assert(connsm != NULL);
|
||||
|
||||
/* Get schedule element from connection */
|
||||
rc = -1;
|
||||
sch = &connsm->conn_sch;
|
||||
|
||||
/* XXX:
|
||||
* The calculations for the 32kHz crystal bear alot of explanation. The
|
||||
* earliest possible time that the master can start the connection with a
|
||||
* slave is 1.25 msecs from the end of the connection request. The
|
||||
* connection request is sent an IFS time from the end of the advertising
|
||||
* packet that was received plus the time it takes to send the connection
|
||||
* request. At 1 Mbps, this is 1752 usecs, or 57.41 ticks. Using 57 ticks
|
||||
* makes us off ~13 usecs. Since we dont want to actually calculate the
|
||||
* receive end time tick (this would take too long), we assume the end of
|
||||
* the advertising PDU is 'now' (we call os_cputime_get32). We dont know
|
||||
* how much time it will take to service the ISR but if we are more than the
|
||||
* rx to tx time of the chip we will not be successful transmitting the
|
||||
* connect request. All this means is that we presume that the slave will
|
||||
* receive the connect request later than we expect but no earlier than
|
||||
* 13 usecs before (this is important).
|
||||
*
|
||||
* The code then attempts to schedule the connection at the
|
||||
* earliest time although this may not be possible. When the actual
|
||||
* schedule start time is determined, the master has to determine if this
|
||||
* time is more than a transmit window offset interval (1.25 msecs). The
|
||||
* master has to tell the slave how many transmit window offsets there are
|
||||
* from the earliest possible time to when the actual transmit start will
|
||||
* occur. Later in this function you will see the calculation. The actual
|
||||
* transmission start has to occur within the transmit window. The transmit
|
||||
* window interval is in units of 1.25 msecs and has to be at least 1. To
|
||||
* make things a bit easier (but less power efficient for the slave), we
|
||||
* use a transmit window of 2. We do this because we dont quite know the
|
||||
* exact start of the transmission and if we are too early or too late we
|
||||
* could miss the transmit window. A final note: the actual transmission
|
||||
* start (the anchor point) is sched offset ticks from the schedule start
|
||||
* time. We dont add this to the calculation when calculating the window
|
||||
* offset. The reason we dont do this is we want to insure we transmit
|
||||
* after the window offset we tell the slave. For example, say we think
|
||||
* we are transmitting 1253 usecs from the earliest start. This would cause
|
||||
* us to send a transmit window offset of 1. Since we are actually
|
||||
* transmitting earlier than the slave thinks we could end up transmitting
|
||||
* before the window offset. Transmitting later is fine since we have the
|
||||
* transmit window to do so. Transmitting before is bad, since the slave
|
||||
* wont be listening. We could do better calculation if we wanted to use
|
||||
* a transmit window of 1 as opposed to 2, but for now we dont care.
|
||||
*/
|
||||
dur = os_cputime_usecs_to_ticks(g_ble_ll_sched_data.sch_ticks_per_period);
|
||||
adv_rxend = os_cputime_get32();
|
||||
if (ble_hdr->rxinfo.channel >= BLE_PHY_NUM_DATA_CHANS) {
|
||||
/*
|
||||
* We received packet on advertising channel which means this is a legacy
|
||||
* PDU on 1 Mbps - we do as described above.
|
||||
*/
|
||||
earliest_start = adv_rxend + 57;
|
||||
} else {
|
||||
/*
|
||||
* The calculations are similar as above.
|
||||
*
|
||||
* We received packet on data channel which means this is AUX_ADV_IND
|
||||
* received on secondary adv channel. We can schedule first packet at
|
||||
* the earliest after "T_IFS + AUX_CONNECT_REQ + transmitWindowDelay".
|
||||
* AUX_CONNECT_REQ and transmitWindowDelay times vary depending on which
|
||||
* PHY we received on.
|
||||
*
|
||||
*/
|
||||
if (ble_hdr->rxinfo.phy == BLE_PHY_1M) {
|
||||
// 150 + 352 + 2500 = 3002us = 98.37 ticks
|
||||
earliest_start = adv_rxend + 98;
|
||||
} else if (ble_hdr->rxinfo.phy == BLE_PHY_2M) {
|
||||
// 150 + 180 + 2500 = 2830us = 92.73 ticks
|
||||
earliest_start = adv_rxend + 93;
|
||||
} else if (ble_hdr->rxinfo.phy == BLE_PHY_CODED) {
|
||||
// 150 + 2896 + 3750 = 6796us = 222.69 ticks
|
||||
earliest_start = adv_rxend + 223;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
itvl_t = connsm->conn_itvl_ticks;
|
||||
|
||||
/* We have to find a place for this schedule */
|
||||
OS_ENTER_CRITICAL(sr);
|
||||
|
||||
/*
|
||||
* Are there any allocated periods? If not, set epoch start to earliest
|
||||
* time
|
||||
*/
|
||||
if (g_ble_ll_sched_data.sch_num_occ_periods == 0) {
|
||||
g_ble_ll_sched_data.sch_epoch_start = earliest_start;
|
||||
cp = 0;
|
||||
} else {
|
||||
/*
|
||||
* Earliest start must occur on period boundary.
|
||||
* (tse = ticks since epoch)
|
||||
*/
|
||||
tpp = g_ble_ll_sched_data.sch_ticks_per_period;
|
||||
tse = earliest_start - g_ble_ll_sched_data.sch_epoch_start;
|
||||
np = tse / tpp;
|
||||
cp = np % BLE_LL_SCHED_PERIODS;
|
||||
tick_in_period = tse - (np * tpp);
|
||||
if (tick_in_period != 0) {
|
||||
++cp;
|
||||
if (cp == BLE_LL_SCHED_PERIODS) {
|
||||
cp = 0;
|
||||
}
|
||||
earliest_start += (tpp - tick_in_period);
|
||||
}
|
||||
|
||||
/* Now find first un-occupied period starting from cp */
|
||||
for (i = 0; i < BLE_LL_SCHED_PERIODS; ++i) {
|
||||
if (g_ble_ll_sched_data.sch_occ_period_mask & (1 << cp)) {
|
||||
++cp;
|
||||
if (cp == BLE_LL_SCHED_PERIODS) {
|
||||
cp = 0;
|
||||
}
|
||||
earliest_start += tpp;
|
||||
} else {
|
||||
/* not occupied */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Should never happen but if it does... */
|
||||
if (i == BLE_LL_SCHED_PERIODS) {
|
||||
OS_EXIT_CRITICAL(sr);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
sch->start_time = earliest_start;
|
||||
initial_start = earliest_start;
|
||||
earliest_end = earliest_start + dur;
|
||||
|
||||
if (!ble_ll_sched_insert_if_empty(sch)) {
|
||||
/* Nothing in schedule. Schedule as soon as possible */
|
||||
rc = 0;
|
||||
connsm->tx_win_off = MYNEWT_VAL(BLE_LL_CONN_INIT_MIN_WIN_OFFSET);
|
||||
} else {
|
||||
os_cputime_timer_stop(&g_ble_ll_sched_timer);
|
||||
TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) {
|
||||
/* Set these because overlap function needs them to be set */
|
||||
sch->start_time = earliest_start;
|
||||
sch->end_time = earliest_end;
|
||||
|
||||
/* We can insert if before entry in list */
|
||||
if ((int32_t)(sch->end_time - entry->start_time) <= 0) {
|
||||
if ((earliest_start - initial_start) <= itvl_t) {
|
||||
rc = 0;
|
||||
TAILQ_INSERT_BEFORE(entry, sch, link);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for overlapping events */
|
||||
if (ble_ll_sched_is_overlap(sch, entry)) {
|
||||
/* Earliest start is end of this event since we overlap */
|
||||
earliest_start = entry->end_time;
|
||||
earliest_end = earliest_start + dur;
|
||||
}
|
||||
}
|
||||
|
||||
/* Must be able to schedule within one connection interval */
|
||||
if (!entry) {
|
||||
if ((earliest_start - initial_start) <= itvl_t) {
|
||||
rc = 0;
|
||||
TAILQ_INSERT_TAIL(&g_ble_ll_sched_q, sch, link);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
/* calculate number of window offsets. Each offset is 1.25 ms */
|
||||
sch->enqueued = 1;
|
||||
/*
|
||||
* NOTE: we dont add sched offset ticks as we want to under-estimate
|
||||
* the transmit window slightly since the window size is currently
|
||||
* 2 when using a 32768 crystal.
|
||||
*/
|
||||
dur = os_cputime_ticks_to_usecs(earliest_start - initial_start);
|
||||
connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
sch->start_time = earliest_start;
|
||||
sch->end_time = earliest_end;
|
||||
/*
|
||||
* Since we have the transmit window to transmit in, we dont need
|
||||
* to set the anchor point usecs; just transmit to the nearest tick.
|
||||
*/
|
||||
connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks;
|
||||
connsm->anchor_point_usecs = 0;
|
||||
connsm->ce_end_time = earliest_end;
|
||||
connsm->period_occ_mask = (1 << cp);
|
||||
g_ble_ll_sched_data.sch_occ_period_mask |= connsm->period_occ_mask;
|
||||
++g_ble_ll_sched_data.sch_num_occ_periods;
|
||||
}
|
||||
|
||||
|
||||
/* Get head of list to restart timer */
|
||||
sch = TAILQ_FIRST(&g_ble_ll_sched_q);
|
||||
|
||||
OS_EXIT_CRITICAL(sr);
|
||||
|
||||
os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
int
|
||||
ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
|
||||
struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len)
|
||||
@@ -474,6 +710,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm,
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Schedules a slave connection for the first time.
|
||||
@@ -1204,5 +1441,14 @@ ble_ll_sched_init(void)
|
||||
|
||||
/* Initialize cputimer for the scheduler */
|
||||
os_cputime_timer_init(&g_ble_ll_sched_timer, ble_ll_sched_run, NULL);
|
||||
|
||||
#if MYNEWT_VAL(BLE_LL_STRICT_CONN_SCHEDULING)
|
||||
memset(&g_ble_ll_sched_data, 0, sizeof(struct ble_ll_sched_obj));
|
||||
g_ble_ll_sched_data.sch_ticks_per_period =
|
||||
os_cputime_usecs_to_ticks(MYNEWT_VAL(BLE_LL_USECS_PER_PERIOD));
|
||||
g_ble_ll_sched_data.sch_ticks_per_epoch = BLE_LL_SCHED_PERIODS *
|
||||
g_ble_ll_sched_data.sch_ticks_per_period;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,27 @@ syscfg.defs:
|
||||
ensure interoperability with such devices set this value to 2 (or more).
|
||||
value: '0'
|
||||
|
||||
# Strict scheduling
|
||||
BLE_LL_STRICT_CONN_SCHEDULING:
|
||||
description: >
|
||||
Forces the scheduler on a central to schedule connections in fixed
|
||||
time intervals called periods. If set to 0, the scheduler is not forced
|
||||
to do this. If set to 1, the scheduler will only schedule connections at
|
||||
period boundaries. See comments in ble_ll_sched.h for more details.
|
||||
value: '1'
|
||||
|
||||
BLE_LL_ADD_STRICT_SCHED_PERIODS:
|
||||
description: >
|
||||
The number of additional periods that will be allocated for strict
|
||||
scheduling. The total # of periods allocated for strict scheduling
|
||||
will be equal to the number of connections plus this number.
|
||||
value: '0'
|
||||
|
||||
BLE_LL_USECS_PER_PERIOD:
|
||||
description: >
|
||||
The number of usecs per period.
|
||||
value: '3250'
|
||||
|
||||
# The number of random bytes to store
|
||||
BLE_LL_RNG_BUFSIZE:
|
||||
description: >
|
||||
|
||||
Reference in New Issue
Block a user