Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Paging process, speed and reliability #540

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions firmware/btbr/include/ubtbr/btphy.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
typedef enum btphy_mode_e {
BT_MODE_INQUIRY,
BT_MODE_PAGING,
BT_MODE_PAGING_RESPONSE,
BT_MODE_INQUIRY_SCAN,
BT_MODE_PAGE_SCAN,
BT_MODE_MASTER,
Expand Down Expand Up @@ -71,4 +72,9 @@ void btphy_adj_clkn_delay(int delay);
void btphy_cancel_clkn_delay(void);
void btphy_timer_add(uint32_t instant, btphy_timer_fn_t cb, void *cb_arg, uint8_t anyway);

static inline void btphy_set_mode_no_ap(btphy_mode_t mode)
{
btphy.mode = mode;
}

#endif
43 changes: 43 additions & 0 deletions firmware/btbr/include/ubtbr/hop.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,49 @@ static inline uint8_t perm5(uint8_t z, uint8_t p_high, uint16_t p_low)
return z;
}


/* This function computes the input value X to be sent to hop_selection_kernel when the master
has clock clk, during page scan.

A slave in PAGE_SCAN mode listens for ID(1) messages for a duration of PAGE_WINDOW (>=10ms)
every PAGE_INTERVAL (either 0s, 1.28s or 2.56s), with slow frequency-hopping that only
changes every 1.28s. The used frequency is calculated by hop_selection_kernel from the slave's
BT-MAC (inputs A,B,C,D,E,F) and CLK[12:16] (input X).

To establish connection the master must send ID(1) on the correct frequency.
Knowing the slave BT-MAC (but not its CLK), the master needs to try all possible X values (2**5 = 32 options).
On every master slot, the master tries two frequencies corresponding to two guesses for X (slave CLK[12:16]),
one try on each clock of the slot. If any of them is correct, the slave will respond with ID(2)
on the corresponding clock of the following slot.

Since PAGE_WINDOW might be as short as 10ms, only 16 X values might fit a single window.
For slaves whose PAGE_INTERVAL is long (1.28s or 2.56s), CLK[12:16] will change across windows.
We want to avoid trying X values that correspond to the same guess for the slave's clock,
i.e., if X was rejected on time T, then X+1 will be rejected on T+1.28s, X+2 will be rejected on T+2.56s, etc.
Therefore we calculate a series of X values to try on each master clock (keeping in mind only
even slots are the master's), in a way that:

1. all possible X values are tried repeatedly within a 1.28s interval;
2. if the 16 values tried during the 10ms window starting at time T were (X0,...,X15), then none of the values (X0+1,...,X15+1) will be tried on time T+1.28s;
3. if the 16 values tried during the 10ms window starting at time T were (X0,...,X15), then none of the values (X0+2,...,X15+2) will be tried on time T+2.56s.

* CLK[5:2,0] is a simple incrementing counter (jumping over slave slots indicated by CLK[1]==1) to satisfy (1);
* CLK[16:12]*17 ensures adding 17 every 1.28s to satisfy (2);
* CLK[16:13]*16 ensures adding 17+17+16=50 == 18 (mod 32) every 2.56s to satisfy (3). */
inline uint8_t calc_iterate_slave_page_scan_phase(uint32_t clk)
{
// X = ( CLK[5...2,0] + CLK[16...12]*17 + CLK[16...13]*16 ) mod 32
return ((((clk>>1) & 0x1e) | (clk&1) ) + ((clk>>12)&0x1f)*17 + ((clk>>13)&0xf)*16)%32 ;
}

/* When we recive an ID(1) acknoledgment, that is, ID(2) From the slave device we transition into PAGE_RESPONSE
we freeze this value and use a frozen value of this +N (see Master Page Response Substate hopping scheme control word)
("our" N is actually hop_state.x which is also used in bbcodec) */
inline void hop_freeze_clock(uint32_t clk)
{
hop_state.x = calc_iterate_slave_page_scan_phase(clk);
}

/* This function increment the x variable of for paging/inquiry hopping.
* It must be called before each master's transmission. */
static inline void hop_increment(void)
Expand Down
3 changes: 2 additions & 1 deletion firmware/btbr/src/btphy/btphy.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ uint8_t btphy_whiten_seed(uint32_t clk)
{
case BT_MODE_INQUIRY:
case BT_MODE_PAGING:
case BT_MODE_PAGING_RESPONSE:
case BT_MODE_INQUIRY_SCAN:
case BT_MODE_PAGE_SCAN:
/* BT 5.1|Vol 2, Part B, 7.2 :
Expand Down Expand Up @@ -260,7 +261,7 @@ void btphy_set_mode(btphy_mode_t mode, uint32_t lap, uint8_t uap)
/* BT Core Spec v5.2|Vol 2, Part B, table 2.3:
* A23_0=lap, and A27_24=uap3_0 */
hop_init(lap|(uap<<24));
btphy.mode = mode;
btphy_set_mode_no_ap(mode);
}

void btphy_set_bdaddr(uint64_t bdaddr)
Expand Down
18 changes: 15 additions & 3 deletions firmware/btbr/src/btphy/hop.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ static uint8_t hop_selection_kernel(uint8_t x, uint8_t y1, uint8_t y2,
return (perm5((a+x)^b, y1^c, d) + e + f + y2);
}

uint8_t hop_inquiry(uint32_t clk)
static uint8_t hop_inquiry_with_phase(uint32_t clk, uint8_t x)
{
uint8_t clk1, sel;

clk1 = 1&(clk>>1);

sel = hop_selection_kernel(
hop_state.x, // X = the x
x, // X = the x
clk1, // Y1 = clk1
clk1<<5, // Y2 = 32*clk1
hop_state.a27_23, // A = A27_23
Expand All @@ -91,6 +91,16 @@ uint8_t hop_inquiry(uint32_t clk)
return hop_state.basic_bank[sel];
}

uint8_t hop_inquiry(uint32_t clk)
{
return hop_inquiry_with_phase(clk, hop_state.x);
}

static uint8_t hop_paging(uint32_t clk)
{
return hop_inquiry_with_phase(clk, calc_iterate_slave_page_scan_phase(clk));
}

static void hop_set_afh_map(uint8_t *afh_map)
{
uint8_t chan;
Expand Down Expand Up @@ -155,13 +165,15 @@ uint8_t hop_channel(uint32_t clk)
switch(btphy.mode)
{
case BT_MODE_INQUIRY:
case BT_MODE_PAGING:
case BT_MODE_PAGING_RESPONSE:
case BT_MODE_INQUIRY_SCAN:
case BT_MODE_PAGE_SCAN:
return hop_inquiry(clk);
case BT_MODE_MASTER:
case BT_MODE_SLAVE:
return hop_basic(clk);
case BT_MODE_PAGING:
return hop_paging(clk);
default:
DIE("Invalid hop mode %d\n", btphy.mode);
}
Expand Down
76 changes: 63 additions & 13 deletions firmware/btbr/src/btphy/paging_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@
#include <ubtbr/tx_task.h>
#include <ubtbr/master_state.h>
#include <ubtbr/btctl_intf.h>
#include <ubtbr/hop.h>

/* Max duration : 45 seconds */
#define PAGING_MAX_TICKS (CLKN_RATE*45)
#define TX_PREPARE_IDX 3 // We will transmit at clkn1_0 = 0

#define PAGING_1ST_CALLBACK ((void *)0xCAFE)
#define PAGING_2ND_CALLBACK ((void *)0xF00D)

extern btphy_t btphy;

static struct {
uint32_t clkn_start;
uint32_t clkn_got_id2;
bbhdr_t fhs_hdr;
uint8_t fhs_data[20];
} paging_state;
Expand All @@ -45,13 +52,19 @@ static int paging_canceled(void)
return btctl_get_state() != BTCTL_STATE_PAGE;
}

static int page_response_timeout_exceeded()
{
enum { PAGE_RESP_TO = 8*2 }; // Spec pagerespTO = 8
return((btphy.master_clkn-paging_state.clkn_got_id2) > PAGE_RESP_TO);
}

static void paging_schedule(unsigned delay);

/* Timeframe:
* usec 0 625 1250 1875
* clk -1 0 1 2 3 4 5 6
* master: prep tx | tx id | .. | | rx/prep tx | tx fhs | .. |
* slave: | | .. | tx id(2) | | | .. |tx id(3)
* usec 0 625 1250 1875
* clk -1 0 1 2 3 4 5 6
* master: prep tx | tx id/prep tx | tx id/prep rx | rx/prep rx | rx/prep tx* | tx fhs | .. |rx
* slave: | | .. | tx id(2) | | | .. |tx id(3)
*/

/* We received ID(3) from paged device */
Expand All @@ -73,8 +86,20 @@ static int paging_rx_ack_cb(msg_t *msg, void *arg, int time_offset)
master_state_init();
}
else{
cprintf("no ID(3)\n");
paging_schedule(3&(TX_PREPARE_IDX-CUR_MASTER_SLOT_IDX()));
uint8_t delay = 3&(TX_PREPARE_IDX-CUR_MASTER_SLOT_IDX());
/* If the setup fails before the CONNECTION state has been reached, the following procedure shall be carried out.
The slave shall listen as long as no FHS packet is received until pagerespTO is exceeded.*/
if(page_response_timeout_exceeded())
{
cprintf("no ID(3)\n");
paging_schedule(delay);
}else{
/* Retry sending FHS; no need to tune to the same x-1 channel since `Slave Page Response` Hopping sequence
advance `N` only when master's clock1 reaches 0. */
bbpkt_fhs_finalize_payload(paging_state.fhs_data, (btphy.master_clkn+delay+1)>>2);
tx_task_schedule(delay, NULL, NULL, &paging_state.fhs_hdr, paging_state.fhs_data);
rx_task_schedule(delay+2, paging_rx_ack_cb, NULL, 0);
}
}
msg_free(msg);
return 0;
Expand All @@ -94,11 +119,17 @@ static int paging_rx_cb(msg_t *msg, void *arg, int time_offset)
if (BBPKT_HAS_PKT(pkt))
{
/* We received the slave's ID(2) at t'(k).
* Freeze the `calc_iterate_slave_page_scan_phase(clk)` value
* as the X-phase we will carry.
* Send FHS in next slot and start basic hopping
* The FHS shall be sent on chan f(k+1), following the std paging hop
* */
delay = 3&(TX_PREPARE_IDX-CUR_MASTER_SLOT_IDX());
paging_state.clkn_got_id2 = btphy.master_clkn;

btphy_set_mode_no_ap(BT_MODE_PAGING_RESPONSE);
hop_freeze_clock(btphy.master_clkn);

/* Write clk27_2 in fhs payload
* Will tx at (clkn+1) */
bbpkt_fhs_finalize_payload(paging_state.fhs_data, (btphy.master_clkn+delay+1)>>2);
Expand All @@ -117,8 +148,16 @@ static int paging_rx_cb(msg_t *msg, void *arg, int time_offset)
}
else
{
// schedule tx in next slot
paging_schedule(3&(TX_PREPARE_IDX-CUR_MASTER_SLOT_IDX()));
if (arg == PAGING_1ST_CALLBACK){
/* Schedule rx ID(2): this listens for ID(2) in the second clock of the master slot */
rx_task_schedule(0,
paging_rx_cb, PAGING_2ND_CALLBACK, // ID rx callback
0 // no payload for an ID packet
);
}else{
/* schedule tx in next slot */
paging_schedule(3&(TX_PREPARE_IDX-CUR_MASTER_SLOT_IDX()));
}
}
end:
msg_free(msg);
Expand All @@ -135,16 +174,27 @@ static void paging_schedule(unsigned delay)
return;
}

/* Schedule TX ID(1) */
/* In case we are coming here from paging_rx_ack_cb where page_response_timeout_exceeded
is true, make sure we revert to mode BT_MODE_PAGING instead of BT_MODE_PAGING_RESPONSE */
btphy_set_mode_no_ap(BT_MODE_PAGING);

/* Schedule TX ID(1) in the 1st half of the master's TX slot */
tx_task_schedule(delay,
NULL, NULL, // no tx callback
NULL, NULL); // no header / no payload
NULL, NULL); // no header / no payload

/* Schedule TX ID(1) in the 2nd half of the master's TX slot */
tx_task_schedule(delay+1,
NULL, NULL, // no tx callback
NULL, NULL); // no header / no payload

/* Schedule rx ID(2): */
/* Schedule RX ID(2) in the `1st half of the master's TX slot` + 625uS.
If ID(2) fails to arrive, paging_rx_cb will schedule another consequtive
RX in the 2nd half of the same slot */
rx_task_schedule(delay+2,
paging_rx_cb, NULL, // ID rx callback
paging_rx_cb, PAGING_1ST_CALLBACK, // ID rx callback
0 // no payload for an ID packet
);
);
}

/* dummy tick handler to start TX'ing with a proper clock */
Expand Down
18 changes: 14 additions & 4 deletions firmware/btbr/src/btphy/tx_task.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static int tx_prepare(uint8_t p1, uint8_t p2, uint16_t p3)

/* In inquiry/paging, hop.x must be incremented before each tx
* of the master / RX of the slave */
if (btphy.mode == BT_MODE_INQUIRY || btphy.mode == BT_MODE_PAGING)
if (btphy.mode == BT_MODE_INQUIRY || btphy.mode == BT_MODE_PAGING_RESPONSE)
hop_increment();

/* Hop & prepare TX (SFSON) */
Expand Down Expand Up @@ -117,7 +117,14 @@ static int tx_prepare(uint8_t p1, uint8_t p2, uint16_t p3)
tx_task.data_size -= len;
}
/* Schedule end of transmission */
tdma_schedule(2*nslots, tx_finalize, 0, 0, 0, -3);
if(btphy.mode == BT_MODE_PAGING ){
/* Page messages should start and end in the same TDAM slot(clk) as we need to finalize() current one
And prepare() the next one for sending at clk+1 the 2nd page */
tdma_schedule(1*nslots, tx_finalize, 0, 0, 0, -2);
}else{
/* End of transmission in most packets CAN be in the following TDAM window post-tx. */
tdma_schedule(2*nslots, tx_finalize, 0, 0, 0, -3);
}

return 0;
}
Expand All @@ -138,8 +145,11 @@ static int tx_execute(uint8_t p1, uint8_t p2, uint16_t p3)
btphy_rf_tx();
if (tx_task.data_size)
btphy_rf_enable_int(tx_fifo_cb, NULL, 1);
if (btphy_cur_clkn() & 1)

if ( btphy.mode!=BT_MODE_PAGING && btphy_cur_clkn() & 1 )
DIE("txe: wrong clkn %x", btphy_cur_clkn());
else if ( btphy.mode==BT_MODE_PAGING && (btphy_cur_clkn() & 2) )
DIE("txe: wrong clkn %x: should be in master slot (0 or 1 mod 4) when paging");

return 0;
}
Expand All @@ -150,7 +160,7 @@ static int tx_finalize(uint8_t p1, uint8_t p2, uint16_t p3)
/* Wait end of transmission .. */
while (CLKN_OFFSET < TX_MAX_WAIT && (cc2400_get(FSMSTATE) & 0x1f) != STATE_STROBE_FS_ON);

/* Stop phy RX */
/* Stop phy TX */
btphy_rf_idle();

tx_task_reset();
Expand Down