/* Copyright 2016 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "bluetooth_le.h" /* chan2freq */ #include "btle_hci_int.h" #include "console.h" #include "radio.h" #include "radio_test.h" #include "registers.h" #include "timer.h" #include "util.h" #define BLE_TEST_TYPE_PRBS9 0 #define BLE_TEST_TYPE_F0 1 #define BLE_TEST_TYPE_AA 2 #define BLE_TEST_TYPE_PRBS15 3 #define BLE_TEST_TYPE_FF 4 #define BLE_TEST_TYPE_00 5 #define BLE_TEST_TYPE_0F 6 #define BLE_TEST_TYPE_55 7 #define BLE_TEST_TYPES_IMPLEMENTED 0xf6 /* No PRBS yet */ static struct nrf51_ble_packet_t rx_packet; static struct nrf51_ble_packet_t tx_packet; static uint32_t rx_end; static int test_in_progress; void ble_test_stop(void) { test_in_progress = 0; } static uint32_t prbs_lfsr; static uint32_t prbs_poly; /* * This is a Galois LFSR, the polynomial is the counterpart of the Fibonacci * LFSR in the doc. It requires fewer XORs to implement in software. * This also means that the initial value is different. */ static uint8_t prbs_next_byte(void) { int i; int lsb; uint8_t rv = 0; for (i = 0; i < 8; i++) { lsb = prbs_lfsr & 1; rv |= lsb << i; prbs_lfsr = prbs_lfsr >> 1; if (lsb) prbs_lfsr ^= prbs_poly; } return rv; } void ble_test_fill_tx_packet(int type, int len) { int i; tx_packet.s0 = type & 0xf; tx_packet.length = len; switch (type) { case BLE_TEST_TYPE_PRBS9: prbs_lfsr = 0xf; prbs_poly = 0x108; for (i = 0; i < len; i++) tx_packet.payload[i] = prbs_next_byte(); break; case BLE_TEST_TYPE_PRBS15: prbs_lfsr = 0xf; prbs_poly = 0x6000; for (i = 0; i < len; i++) tx_packet.payload[i] = prbs_next_byte(); break; case BLE_TEST_TYPE_F0: memset(tx_packet.payload, 0xF0, len); break; case BLE_TEST_TYPE_AA: memset(tx_packet.payload, 0xAA, len); break; case BLE_TEST_TYPE_FF: memset(tx_packet.payload, 0xFF, len); break; case BLE_TEST_TYPE_00: memset(tx_packet.payload, 0x00, len); break; case BLE_TEST_TYPE_0F: memset(tx_packet.payload, 0x0F, len); break; case BLE_TEST_TYPE_55: memset(tx_packet.payload, 0x55, len); break; default: break; } } static int ble_test_init(int chan) { int rv = radio_init(BLE_1MBIT); if (rv) return HCI_ERR_Hardware_Failure; if (chan > BLE_MAX_TEST_CHANNEL || chan < BLE_MIN_TEST_CHANNEL) return HCI_ERR_Invalid_HCI_Command_Parameters; NRF51_RADIO_CRCCNF = 3 | BIT(8); /* 3-byte, skip address */ /* x^24 + x^10 + x^9 + x^6 + x^4 + x^3 + x + 1 */ /* 0x1_0000_0000_0000_0110_0101_1011 */ NRF51_RADIO_CRCPOLY = 0x100065B; NRF51_RADIO_CRCINIT = 0x555555; NRF51_RADIO_TXPOWER = NRF51_RADIO_TXPOWER_0_DBM; /* The testing address is the inverse of the advertising address. */ NRF51_RADIO_BASE0 = (~BLE_ADV_ACCESS_ADDRESS) << 8; NRF51_RADIO_PREFIX0 = (~BLE_ADV_ACCESS_ADDRESS) >> 24; NRF51_RADIO_TXADDRESS = 0; NRF51_RADIO_RXADDRESSES = 1; NRF51_RADIO_PCNF0 = NRF51_RADIO_PCNF0_TEST; NRF51_RADIO_PCNF1 = NRF51_RADIO_PCNF1_TEST; NRF51_RADIO_FREQUENCY = NRF51_RADIO_FREQUENCY_VAL(2*chan + 2402); test_in_progress = 1; return rv; } int ble_test_rx_init(int chan) { NRF51_RADIO_PACKETPTR = (uint32_t)&rx_packet; return ble_test_init(chan); } int ble_test_tx_init(int chan, int len, int type) { if ((BIT(type) & BLE_TEST_TYPES_IMPLEMENTED) == 0 || (len < 0 || len > BLE_MAX_TEST_PAYLOAD_OCTETS)) return HCI_ERR_Invalid_HCI_Command_Parameters; ble_test_fill_tx_packet(type, len); NRF51_RADIO_PACKETPTR = (uint32_t)&tx_packet; return ble_test_init(chan); } void ble_test_tx(void) { NRF51_RADIO_END = 0; NRF51_RADIO_TXEN = 1; } int ble_test_rx(void) { int retries = 100; NRF51_RADIO_END = 0; NRF51_RADIO_RXEN = 1; do { retries--; if (retries <= 0) { radio_disable(); return EC_ERROR_TIMEOUT; } usleep(100); } while (!NRF51_RADIO_END); rx_end = get_time().le.lo; return EC_SUCCESS; }