blob: bcad7466e9611769d9eab76300c0a90a8e59d51e [file] [log] [blame]
/* 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;
}