diff --git a/firmware/btbr/include/ubtbr/btphy.h b/firmware/btbr/include/ubtbr/btphy.h index 4457b001..350c63cb 100644 --- a/firmware/btbr/include/ubtbr/btphy.h +++ b/firmware/btbr/include/ubtbr/btphy.h @@ -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, @@ -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 diff --git a/firmware/btbr/include/ubtbr/hop.h b/firmware/btbr/include/ubtbr/hop.h index 6d6f0045..4f722ba3 100644 --- a/firmware/btbr/include/ubtbr/hop.h +++ b/firmware/btbr/include/ubtbr/hop.h @@ -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) diff --git a/firmware/btbr/src/btphy/btphy.c b/firmware/btbr/src/btphy/btphy.c index ee55a89f..f8fd3a70 100644 --- a/firmware/btbr/src/btphy/btphy.c +++ b/firmware/btbr/src/btphy/btphy.c @@ -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 : @@ -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) diff --git a/firmware/btbr/src/btphy/hop.c b/firmware/btbr/src/btphy/hop.c index 93136ce4..f7117a15 100644 --- a/firmware/btbr/src/btphy/hop.c +++ b/firmware/btbr/src/btphy/hop.c @@ -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 @@ -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; @@ -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); } diff --git a/firmware/btbr/src/btphy/paging_state.c b/firmware/btbr/src/btphy/paging_state.c index 7d0a5d0a..5b8ac3e8 100644 --- a/firmware/btbr/src/btphy/paging_state.c +++ b/firmware/btbr/src/btphy/paging_state.c @@ -29,13 +29,20 @@ #include #include #include +#include /* 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; @@ -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 */ @@ -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; @@ -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); @@ -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); @@ -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 */ diff --git a/firmware/btbr/src/btphy/tx_task.c b/firmware/btbr/src/btphy/tx_task.c index 0bdd27d0..946d03bb 100644 --- a/firmware/btbr/src/btphy/tx_task.c +++ b/firmware/btbr/src/btphy/tx_task.c @@ -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) */ @@ -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; } @@ -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; } @@ -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();