/* * Copyright 2017 Mike Ryan * * This file is part of Project Ubertooth and is released under the * terms of the GPL. Refer to COPYING for more information. */ #include #include #include "ubertooth.h" #include "ubertooth_clock.h" #include "ubertooth_dma.h" #include "ubertooth_usb.h" #include "bluetooth_le.h" #include "queue.h" // current time, from timer1 #define NOW T1TC #define USEC(X) ((X)*10) #define MSEC(X) ((X)*10000) #define SEC(X) ((X)*10000000) #define PACKET_DURATION(X) (USEC(40 + (X)->size * 8)) #define ADVERTISING_AA (0x8e89bed6) /////////////////////// // time constants // time for the radio to warmup + some timing slack #define RX_WARMUP_TIME USEC(300) // max inter-frame space between packets in a connection event #define IFS_TIMEOUT USEC(300) // observed connection anchor must be within ANCHOR_EPSILON of // calculated anchor #define ANCHOR_EPSILON USEC(3) ////////////////////// // global state extern le_state_t le; // FIXME - refactor this struct volatile uint16_t rf_channel; uint8_t le_dma_dest[2]; extern volatile uint8_t mode; extern volatile uint8_t requested_mode; extern volatile uint16_t le_adv_channel; extern volatile int cancel_follow; //////////////////// // buffers // packet buffers live in a pool. a minimum of one buffer is always // being used, either waiting to receive or actively receiving a packet // (current_rxbuf). once a packet is received, it is placed into the // packet queue. the main loop pulls packets from this queue and // processes them, and then returns the buffers back to the pool by // calling buffer_release() #define LE_BUFFER_POOL_SIZE 4 typedef struct _le_rx_t { uint8_t data[2 + 255 + 3]; // header + PDU + CRC unsigned size; // total data length (known after header rx) unsigned pos; // current input byte offset uint32_t timestamp; // timestamp taken after first byte rx unsigned channel; // physical channel uint32_t access_address; // access address int available; // 1 if available, 0 in use int8_t rssi_min, rssi_max; // min and max RSSI observed values int rssi_sum; // running sum of all RSSI values } le_rx_t; // pool of all buffers static le_rx_t le_buffer_pool[LE_BUFFER_POOL_SIZE]; // buffer waiting for or actively receiving packet static le_rx_t *current_rxbuf = NULL; // received packets, waiting to be processed queue_t packet_queue; ///////////////////// // connections // this system is architected so that following multiple connections may // be possible in the future. all connection state lives in an le_conn_t // struct. at present only one such structure exists. refer to // connection event below for how anchors are handled. typedef struct _le_conn_t { uint32_t access_address; uint32_t crc_init; uint32_t crc_init_reversed; uint8_t channel_idx; uint8_t hop_increment; uint32_t conn_interval; // in units of 100 ns uint32_t supervision_timeout; // in units of 100 ns uint8_t win_size; uint32_t win_offset; // in units of 100 ns le_channel_remapping_t remapping; uint32_t last_anchor; int anchor_set; uint32_t last_packet_ts; // used to check supervision timeout uint16_t conn_event_counter; int conn_update_pending; uint32_t conn_update_pending_interval; uint32_t conn_update_pending_supervision_timeout; uint16_t conn_update_instant; int channel_map_update_pending; uint16_t channel_map_update_instant; le_channel_remapping_t pending_remapping; } le_conn_t; le_conn_t conn = { 0, }; // every connection event is tracked using this global le_conn_event_t // structure named conn_event. when a packet is observed, anchor is set. // the event may close due to receiving two packets, or if a timeout // occurs. in both cases, finish_conn_event() is called, which updates // the active connection's anchor. opened is set to 1 once the radio is // tuned to the data channel for the connection event. typedef struct _le_conn_event_t { uint32_t anchor; unsigned num_packets; int opened; } le_conn_event_t; le_conn_event_t conn_event; static void reset_conn(void) { memset(&conn, 0, sizeof(conn)); conn.access_address = ADVERTISING_AA; } ////////////////////// // code // pre-declarations for utility stuff static void timer1_start(void); static void timer1_stop(void); static void timer1_set_match(uint32_t match); static void timer1_clear_match(void); static void timer1_wait_fs_lock(void); static void timer1_cancel_fs_lock(void); static void blink(int tx, int rx, int usr); static void le_dma_init(void); static void le_cc2400_strobe_rx(void); static void change_channel(void); static uint8_t dewhiten_length(unsigned channel, uint8_t data); // resets the state of all available buffers static void buffers_init(void) { int i; for (i = 0; i < LE_BUFFER_POOL_SIZE; ++i) le_buffer_pool[i].available = 1; } // clear a buffer for new data static void buffer_clear(le_rx_t *buf) { buf->pos = 0; buf->size = 0; memset(buf->data, 0, sizeof(buf->data)); buf->rssi_min = INT8_MAX; buf->rssi_max = INT8_MIN; buf->rssi_sum = 0; } // get a packet buffer // returns a pointer to a buffer if available // returns NULL otherwise static le_rx_t *buffer_get(void) { int i; for (i = 0; i < LE_BUFFER_POOL_SIZE; ++i) { if (le_buffer_pool[i].available) { le_buffer_pool[i].available = 0; buffer_clear(&le_buffer_pool[i]); return &le_buffer_pool[i]; } } return NULL; } // release a buffer back to the pool static void buffer_release(le_rx_t *buffer) { buffer->available = 1; } // clear a connection event static void reset_conn_event(void) { conn_event.num_packets = 0; conn_event.opened = 0; } // finish a connection event // // 1) update the anchor point (see details below) // 2) increment connection event counter // 3) check if supervision timeout is exceeded // 4) setup radio for next packet (data or adv if timeout exceeded) // // anchor update logic can be summarized thusly: // 1) if we received two packets, set the connection anchor to the // observed value // 2) if we received one packet, see if it's within ANCHOR_EPISLON // microseconds if the expected anchor time. if so, it's the master // and we can update the anchor // 3) if the single packet is a slave or we received zero packets, // update the anchor to the estimated value // // FIXME this code does not properly handle the case where the initial // connection transmit window has no received packets static void finish_conn_event(void) { uint32_t last_anchor = 0; int last_anchor_set = 0; // two packets -- update anchor if (conn_event.num_packets == 2) { last_anchor = conn_event.anchor; last_anchor_set = 1; } // if there's one packet, we need to find out if it was the master else if (conn_event.num_packets == 1 && conn.anchor_set) { // calculate the difference between the estimated and observed anchor uint32_t estimated_anchor = conn.last_anchor + conn.conn_interval; uint32_t delta = estimated_anchor - conn_event.anchor; // see whether the observed anchor is within 3 us of the estimate delta += ANCHOR_EPSILON; if (delta < 2 * ANCHOR_EPSILON) { last_anchor = conn_event.anchor; last_anchor_set = 1; } } // if we observed a new anchor, set it if (last_anchor_set) { conn.last_anchor = last_anchor; conn.anchor_set = 1; } // without a new anchor, estimate the next anchor else if (conn.anchor_set) { conn.last_anchor += conn.conn_interval; } else { // FIXME this is totally broken if we receive the slave's packet first conn.last_anchor = conn_event.anchor; conn.last_packet_ts = NOW; // FIXME gross hack } // update last packet for supervision timeout if (conn_event.num_packets > 0) { conn.last_packet_ts = NOW; } reset_conn_event(); // increment connection event counter ++conn.conn_event_counter; // supervision timeout reached - switch back to advertising if (NOW - conn.last_packet_ts > conn.supervision_timeout) { reset_conn(); change_channel(); } // FIXME - hack to cancel following a connection else if (cancel_follow) { cancel_follow = 0; reset_conn(); change_channel(); } // supervision timeout not reached - hop to next channel else { timer1_set_match(conn.last_anchor + conn.conn_interval - RX_WARMUP_TIME); } } // DMA handler // called once per byte. handles all incoming data, but only minimally // processes received data. at the end of a packet, it enqueues the // received packet, fetches a new buffer, and restarts RX. void le_DMA_IRQHandler(void) { unsigned pos; int8_t rssi; uint32_t timestamp = NOW; // sampled early for most accurate measurement // channel 0 if (DMACIntStat & (1 << 0)) { // terminal count - byte received if (DMACIntTCStat & (1 << 0)) { DMACIntTCClear = (1 << 0); // poll RSSI rssi = (int8_t)(cc2400_get(RSSI) >> 8); current_rxbuf->rssi_sum += rssi; if (rssi < current_rxbuf->rssi_min) current_rxbuf->rssi_min = rssi; if (rssi > current_rxbuf->rssi_max) current_rxbuf->rssi_max = rssi; // grab byte from DMA buffer pos = current_rxbuf->pos; current_rxbuf->data[pos] = le_dma_dest[pos & 1]; // dirty hack pos += 1; current_rxbuf->pos = pos; if (pos == 1) { current_rxbuf->timestamp = timestamp - USEC(8 + 32); // packet starts at preamble current_rxbuf->channel = rf_channel; current_rxbuf->access_address = conn.access_address; // data packet received: cancel timeout // new timeout or hop timer will be set at end of packet RX if (btle_channel_index(rf_channel) < 37) { timer1_clear_match(); } } // get length from header if (pos == 2) { uint8_t length = dewhiten_length(current_rxbuf->channel, current_rxbuf->data[1]); current_rxbuf->size = length + 2 + 3; // two bytes for header and three for CRC } // finished packet - state transition if (pos > 2 && pos >= current_rxbuf->size) { // stop the CC2400 before flushing SSP cc2400_strobe(SFSON); // stop DMA on this channel and flush SSP DMACC0Config = 0; DMACIntTCClear = (1 << 0); // if we don't clear a second time, data is corrupt DIO_SSP_DMACR &= ~SSPDMACR_RXDMAE; while (SSP1SR & SSPSR_RNE) { uint8_t tmp = (uint8_t)DIO_SSP_DR; } // TODO error transition on queue_insert queue_insert(&packet_queue, current_rxbuf); // track connection events if (btle_channel_index(rf_channel) < 37) { ++conn_event.num_packets; // first packet: set connection anchor if (conn_event.num_packets == 1) { conn_event.anchor = current_rxbuf->timestamp; timer1_set_match(NOW + IFS_TIMEOUT); // set a timeout for next packet } // second packet: close connection event, and set hop timer else if (conn_event.num_packets == 2) { cc2400_strobe(SRFOFF); current_rxbuf = buffer_get(); finish_conn_event(); return; } } // get a new packet // TODO handle error transition current_rxbuf = buffer_get(); // restart DMA and SSP le_dma_init(); dio_ssp_start(); // wait for FS_LOCK in background timer1_wait_fs_lock(); } } // error - transition to error state if (DMACIntErrStat & (1 << 0)) { // TODO error state transition DMACIntErrClr = (1 << 0); } } } static void le_dma_init(void) { int i; // DMA linked list items typedef struct { uint32_t src; uint32_t dest; uint32_t next_lli; uint32_t control; } dma_lli; static dma_lli le_dma_lli[2]; for (i = 0; i < 2; ++i) { le_dma_lli[i].src = (uint32_t)&(DIO_SSP_DR); le_dma_lli[i].dest = (uint32_t)&le_dma_dest[i]; le_dma_lli[i].next_lli = (uint32_t)&le_dma_lli[1-i]; // two elements pointing back at each other le_dma_lli[i].control = 1 | (0 << 12) | // source burst size = 1 (0 << 15) | // destination burst size = 1 (0 << 18) | // source width 8 bits (0 << 21) | // destination width 8 bits DMACCxControl_I; // terminal count interrupt enable } // configure DMA channel 0 DMACC0SrcAddr = le_dma_lli[0].src; DMACC0DestAddr = le_dma_lli[0].dest; DMACC0LLI = le_dma_lli[0].next_lli; DMACC0Control = le_dma_lli[0].control; DMACC0Config = DIO_SSP_SRC | (0x2 << 11) | // peripheral to memory DMACCxConfig_IE | // allow error interrupts DMACCxConfig_ITC; // allow terminal count interrupts } // initalize USB, SSP, and DMA static void le_sys_init(void) { usb_queue_init(); // USB FIFO FIXME replace with safer queue dio_ssp_init(); // init SSP and raise !CS (self-routed GPIO) le_dma_init(); // prepare DMA + interrupts dio_ssp_start(); // enable SSP + DMA } // initialize RF and strobe FSON static void le_cc2400_init_rf(void) { u16 grmdm, mdmctrl; uint32_t sync = rbit(conn.access_address); mdmctrl = 0x0040; // 250 kHz frequency deviation grmdm = 0x44E1; // un-buffered mode, packet w/ sync word detection // 0 10 00 1 001 11 0 00 0 1 // | | | | | +--------> CRC off // | | | | +-----------> sync word: 32 MSB bits of SYNC_WORD // | | | +---------------> 1 preamble byte of 01010101 // | | +-----------------> packet mode // | +--------------------> un-buffered mode // +-----------------------> sync error bits: 2 cc2400_set(MANAND, 0x7ffe); cc2400_set(LMTST, 0x2b22); cc2400_set(MDMTST0, 0x124b); // 1 2 4b // 00 0 1 0 0 10 01001011 // | | | | | +---------> AFC_DELTA = ?? // | | | | +------------> AFC settling = 4 pairs (8 bit preamble) // | | | +--------------> no AFC adjust on packet // | | +----------------> do not invert data // | +------------------> TX IF freq 1 0Hz // +--------------------> PRNG off // // ref: CC2400 datasheet page 67 // AFC settling explained page 41/42 cc2400_set(GRMDM, grmdm); cc2400_set(SYNCL, sync & 0xffff); cc2400_set(SYNCH, (sync >> 16) & 0xffff); cc2400_set(FSDIV, rf_channel - 1); // 1 MHz IF cc2400_set(MDMCTRL, mdmctrl); // XOSC16M should always be stable, but leave this test anyway while (!(cc2400_status() & XOSC16M_STABLE)); // wait for FS_LOCK in background cc2400_strobe(SFSON); timer1_wait_fs_lock(); } // strobe RX and enable PA static void le_cc2400_strobe_rx(void) { cc2400_strobe(SRX); #ifdef UBERTOOTH_ONE PAEN_SET; HGM_SET; #endif } // change channel and init rx static void change_channel(void) { uint8_t channel_idx = 0; cc2400_strobe(SRFOFF); // stop DMA and flush SSP DIO_SSP_DMACR &= ~SSPDMACR_RXDMAE; while (SSP1SR & SSPSR_RNE) { uint8_t tmp = (uint8_t)DIO_SSP_DR; } buffer_clear(current_rxbuf); le_dma_init(); dio_ssp_start(); if (conn.access_address == ADVERTISING_AA) { // FIXME switch (le_adv_channel) { case 2402: channel_idx = 37; break; case 2426: channel_idx = 38; break; case 2480: channel_idx = 39; break; default: channel_idx = 37; break; } } else { conn.channel_idx = (conn.channel_idx + conn.hop_increment) % 37; channel_idx = le_map_channel(conn.channel_idx, &conn.remapping); } rf_channel = btle_channel_index_to_phys(channel_idx); le_cc2400_init_rf(); } /////// // timer stuff static void timer1_start(void) { T1TCR = TCR_Counter_Reset; T1PR = 4; // 100 ns T1TCR = TCR_Counter_Enable; // set up interrupt handler ISER0 = ISER0_ISE_TIMER1; } static void timer1_stop(void) { T1TCR = TCR_Counter_Reset; // clear interrupt handler ICER0 = ICER0_ICE_TIMER1; } static void timer1_set_match(uint32_t match) { T1MR0 = match; T1MCR |= TMCR_MR0I; } static void timer1_clear_match(void) { T1MCR &= ~TMCR_MR0I; } static void timer1_wait_fs_lock(void) { T1MR2 = NOW + USEC(3); T1MCR |= TMCR_MR2I; } static void timer1_cancel_fs_lock(void) { T1MCR &= ~TMCR_MR2I; } void TIMER1_IRQHandler(void) { // MR0: connection events if (T1IR & TIR_MR0_Interrupt) { // ack the interrupt T1IR = TIR_MR0_Interrupt; // connection update procedure if (conn.conn_update_pending && conn.conn_event_counter == conn.conn_update_instant) { // on the first past through, handle the transmit window // offset. if there's no offset, skip down to else block if (!conn_event.opened && conn.win_offset > 0) { timer1_set_match(conn.last_anchor + conn.conn_interval + conn.win_offset - RX_WARMUP_TIME); conn_event.opened = 1; } // after the transmit window offset, or if there is no // transmit window, set a packet timeout and change the // channel else { // conn_event.opened || conn.win_offset == 0 conn_event.opened = 1; // this is like a new connection, so set all values // accordingly conn.anchor_set = 0; conn.conn_interval = conn.conn_update_pending_interval; conn.supervision_timeout = conn.conn_update_pending_supervision_timeout; conn.conn_update_pending = 0; // timeout after conn window + max packet length timer1_set_match(conn.last_anchor + conn.conn_interval + conn.win_offset + conn.win_size + USEC(2120)); change_channel(); } return; } // channel map update if (conn.channel_map_update_pending && conn.conn_event_counter == conn.channel_map_update_instant) { conn.remapping = conn.pending_remapping; conn.channel_map_update_pending = 0; } // new connection event: set timeout and change channel if (!conn_event.opened) { conn_event.opened = 1; // timeout is max packet length + warmup time (slack) timer1_set_match(NOW + USEC(2120) + RX_WARMUP_TIME); change_channel(); } // regular connection event, plus timeout from connection updates // FIXME connection update timeouts and initial connection // timeouts need to be handled differently: they should have a // full window until the packets from the new connection are // captured and a new anchor is set. else { // new connection event: set timeout and change channel if (!conn_event.opened) { conn_event.opened = 1; // timeout is max packet length + warmup time (slack) timer1_set_match(NOW + USEC(2120) + RX_WARMUP_TIME); change_channel(); } // timeout: close connection event and set timer for next hop else { finish_conn_event(); } } } // LEDs if (T1IR & TIR_MR1_Interrupt) { T1IR = TIR_MR1_Interrupt; T1MCR &= ~TMCR_MR1I; TXLED_CLR; RXLED_CLR; USRLED_CLR; } // check FS_LOCK if (T1IR & TIR_MR2_Interrupt) { T1IR = TIR_MR2_Interrupt; // if FS is locked, strobe RX and clear interrupt if (cc2400_status() & FS_LOCK) { le_cc2400_strobe_rx(); T1MCR &= ~TMCR_MR2I; } // if FS is not locked, check again in 3 us else { timer1_wait_fs_lock(); } } } static void blink(int tx, int rx, int usr) { if (tx) TXLED_SET; if (rx) RXLED_SET; if (usr) USRLED_SET; // blink for 10 ms T1MR1 = NOW + MSEC(10); T1MCR |= TMCR_MR1I; } // helper function to dewhiten length from whitened data (only used // during DMA) static uint8_t dewhiten_length(unsigned channel, uint8_t data) { unsigned int i, bit; int idx = whitening_index[btle_channel_index(channel)]; uint8_t out = 0; // length is second byte of packet idx = (idx + 8) % sizeof(whitening); for (i = 0; i < 8; ++i) { bit = (data >> (7-i)) & 1; bit ^= whitening[idx]; idx = (idx + 1) % sizeof(whitening); out |= bit << i; } return out; } // enqueue a packet for USB // FIXME this is cribbed from existing code, but does not have enough // room for larger LE packets static int usb_enqueue_le(le_rx_t *packet) { usb_pkt_rx* f = usb_enqueue(); // fail if queue is full if (f == NULL) { return 0; } f->pkt_type = LE_PACKET; f->clkn_high = 0; f->clk100ns = packet->timestamp; f->channel = (uint8_t)((packet->channel - 2402) & 0xff); f->rssi_avg = packet->rssi_sum / packet->size; f->rssi_min = packet->rssi_min; f->rssi_max = packet->rssi_max; f->rssi_count = 0; memcpy(f->data, &packet->access_address, 4); memcpy(f->data+4, packet->data, DMA_SIZE-4); f->status = 0; return 1; } static unsigned extract_field(le_rx_t *buf, size_t offset, unsigned size) { unsigned i, ret = 0; // this could just be replaced by memcpy... right? for (i = 0; i < size; ++i) ret |= buf->data[offset + i] << (i*8); return ret; } static void le_connect_handler(le_rx_t *buf) { uint32_t aa, crc_init; uint32_t win_size, max_win_size; if (!le.do_follow) return; if (buf->size != 2 + 6 + 6 + 22 + 3) return; // FIXME ugly hack if (cancel_follow) cancel_follow = 0; conn.access_address = extract_field(buf, 14, 4); conn.crc_init = extract_field(buf, 18, 3); conn.crc_init_reversed = rbit(conn.crc_init); conn.win_size = extract_field(buf, 21, 1); conn.win_offset = extract_field(buf, 22, 2); conn.conn_interval = extract_field(buf, 24, 2); conn.supervision_timeout = extract_field(buf, 28, 2); conn.hop_increment = extract_field(buf, 35, 1) & 0x1f; if (conn.conn_interval < 6 || conn.conn_interval > 3200) { goto err_out; } else { conn.conn_interval *= USEC(1250); } // window offset is in range [0, conn_interval] conn.win_offset *= USEC(1250); if (conn.win_offset > conn.conn_interval) goto err_out; // win size is in range [1.25 ms, MIN(10 ms, conn_interval - 1.25 ms)] win_size = conn.win_size * USEC(1250); max_win_size = conn.conn_interval - USEC(1250); if (max_win_size > MSEC(10)) max_win_size = MSEC(10); if (win_size < USEC(1250) || win_size > max_win_size) goto err_out; // The connSupervisionTimeout shall be a multiple of 10 ms in the // range of 100 ms to 32.0 s and it shall be larger than (1 + // connSlaveLatency) * connInterval * 2 conn.supervision_timeout *= MSEC(10); if (conn.supervision_timeout < MSEC(100) || conn.supervision_timeout > SEC(32)) goto err_out; // TODO handle slave latency le_parse_channel_map(&buf->data[30], &conn.remapping); if (conn.remapping.total_channels == 0) goto err_out; // cancel RX on advertising channel timer1_cancel_fs_lock(); reset_conn_event(); timer1_set_match(buf->timestamp + PACKET_DURATION(buf) + conn.win_offset + USEC(1250) - RX_WARMUP_TIME); return; // error condition: reset conn and return err_out: reset_conn(); } static void connection_update_handler(le_rx_t *buf) { conn.win_size = extract_field(buf, 3, 1); conn.win_offset = extract_field(buf, 4, 2); conn.conn_update_pending_interval = extract_field(buf, 6, 2); conn.conn_update_pending_supervision_timeout = extract_field(buf, 10, 2); conn.conn_update_instant = extract_field(buf, 12, 2); // TODO check for invalid values. XXX what do we even do in that // case? we will probably drop the connection, but at least it's on // our own terms and not some impossibly long supervision timeout. conn.win_size *= USEC(1250); conn.win_offset *= USEC(1250); conn.conn_update_pending_interval *= USEC(1250); conn.conn_update_pending_supervision_timeout *= MSEC(10); conn.conn_update_pending = 1; } static void channel_map_update_handler(le_rx_t *buf) { conn.channel_map_update_pending = 1; conn.channel_map_update_instant = extract_field(buf, 8, 2); le_parse_channel_map(&buf->data[3], &conn.pending_remapping); } static void packet_handler(le_rx_t *buf) { // advertising packet if (btle_channel_index(buf->channel) >= 37) { switch (buf->data[0] & 0xf) { // CONNECT_REQ case 0x05: // TODO validate length le_connect_handler(buf); break; } } // data packet else { // LL control PDU if ((buf->data[0] & 0b11) == 0b11 && buf->data[1] > 0) { switch (buf->data[2]) { // LE_CONNECTION_UPDATE_REQ -- update connection parameters case 0x0: if (buf->data[1] == 12) connection_update_handler(buf); break; // LE_CHANNEL_MAP_REQ -- update channel map case 0x1: if (buf->data[1] == 8) channel_map_update_handler(buf); break; } } } } // compare a BD addr against target with mask static int bd_addr_cmp(uint8_t *bd_addr) { unsigned i; for (i = 0; i < 6; ++i) if ((bd_addr[i] & le.target_mask[i]) != le.target[i]) return 0; return 1; } static int filter_match(le_rx_t *buf) { if (!le.target_set) return 1; // allow all data channel packets if (btle_channel_index(buf->channel) < 37) return 1; switch (buf->data[0] & 0xf) { // ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND, SCAN_RSP case 0x00: case 0x02: case 0x06: case 0x04: // header + one address if (buf->size < 2 + 6) return 0; return bd_addr_cmp(&buf->data[2]); break; // ADV_DIRECT_IND, SCAN_REQ, CONNECT_REQ case 0x01: case 0x03: case 0x05: // header + two addresses if (buf->size < 2 + 6 + 6) return 0; return bd_addr_cmp(&buf->data[2]) || bd_addr_cmp(&buf->data[8]); break; default: break; } return 0; } void le_phy_main(void) { // disable USB interrupts -- we poll them below // n.b., they should not be enabled but let's be careful ICER0 = ICER0_ICE_USB; // disable clkn and timer0 clkn_disable(); buffers_init(); queue_init(&packet_queue); timer1_start(); current_rxbuf = buffer_get(); rf_channel = le_adv_channel; // FIXME conn.access_address = ADVERTISING_AA; le_sys_init(); le_cc2400_init_rf(); cancel_follow = 0; while (requested_mode == MODE_BT_FOLLOW_LE) { le_rx_t *packet = NULL; if (queue_remove(&packet_queue, (void **)&packet)) { le_dewhiten(packet->data, packet->size, packet->channel); if (filter_match(packet)) { blink(0, 1, 0); // RX LED usb_enqueue_le(packet); packet_handler(packet); } buffer_release(packet); } // polled USB handling handle_usb(0); // XXX maybe LED light show? } timer1_stop(); // reset state RXLED_CLR; TXLED_CLR; USRLED_CLR; clkn_init(); // TODO kill CC2400 }