| /* 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 | (1 << 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 (((1 << 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; |
| } |
| |