Bluetooth: Controller: Hardware abstraction layer for nRF5x radio

The drivers/bluetooth/controller path adds a native BLE Link
Layer (controller and HCI) to the Bluetooth subsystem. This first
implementation adds support for the nRF5x series of devices
from Nordic Semiconductor. The hal/ folder inside the controller
contains all IC-specific code to interface with the radio and
baseband.

Jira: ZEP-702

Origin: Original
Change-Id: I4ed61d5f67af6b4735d746a38a5b55f054521075
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Vinayak Chettimada 2016-08-15 12:01:06 +02:00 committed by Johan Hedberg
parent 02c11b501f
commit 87d4d27731
20 changed files with 1849 additions and 1 deletions

View file

@ -58,6 +58,12 @@ config BLUETOOTH_H5
Bluetooth three-wire (H:5) UART driver. Implementation of HCI
Three-Wire UART Transport Layer.
config BLUETOOTH_CONTROLLER
bool "Controller"
select BLUETOOTH_HOST_BUFFERS
help
Enables support for SoC native controller implementation.
config BLUETOOTH_NO_DRIVER
bool "No default HCI driver"
help
@ -80,7 +86,7 @@ config BLUETOOTH_HOST_BUFFERS
config BLUETOOTH_DEBUG_DRIVER
bool "Bluetooth driver debug"
depends on BLUETOOTH_DEBUG && BLUETOOTH_UART
depends on BLUETOOTH_DEBUG && (BLUETOOTH_UART || BLUETOOTH_CONTROLLER)
default n
help
This option enables debug support for the chosen
@ -119,6 +125,8 @@ endif # BLUETOOTH_STACK_HCI
source "drivers/bluetooth/nble/Kconfig"
source "drivers/bluetooth/controller/Kconfig"
config BLUETOOTH_NRF51_PM
bool "nRF51 Power Management [EXPERIMENTAL]"
depends on BLUETOOTH_H4 || NBLE
@ -128,6 +136,7 @@ config BLUETOOTH_NRF51_PM
config BLUETOOTH_WAIT_NOP
bool "Wait for \"NOP\" Command Complete event during init"
depends on !BLUETOOTH_CONTROLLER
help
Some controllers emit a Command Complete event for the NOP
opcode to indicate that they're ready to receive commands.

View file

@ -2,3 +2,4 @@ obj-$(CONFIG_BLUETOOTH_H4) += h4.o
obj-$(CONFIG_BLUETOOTH_H5) += h5.o
obj-$(CONFIG_NBLE) += nble/
obj-$(CONFIG_BLUETOOTH_NRF51_PM) += nrf51_pm.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += controller/

View file

@ -0,0 +1,30 @@
if BLUETOOTH_CONTROLLER
comment "BLE Controller configuration"
config BLUETOOTH_CONTROLLER_DATA_LENGTH
prompt "Maximum data length supported"
int
default 27
range 27 251
help
Set the maximum data length of PDU supported in the stack.
config BLUETOOTH_CONTROLLER_RX_BUFFERS
prompt "Number of Rx buffers"
int
default 1
range 1 10
help
Set the number of Rx PDUs to be buffered in the controller.
config BLUETOOTH_CONTROLLER_TX_BUFFERS
prompt "Number of Tx buffers"
int
default 1
range 1 10
help
Set the number of Tx PDUs to be queued for transmission
in the controller.
endif # BLUETOOTH_CONTROLLER

View file

@ -0,0 +1,7 @@
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/hal
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/irq.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/clock.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rtc.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rand.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/ecb.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/radio.o

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _CCM_H_
#define _CCM_H_
#include <toolchain.h>
struct __packed ccm {
uint8_t key[16];
uint64_t counter;
uint8_t direction:1;
uint8_t resv1:7;
uint8_t iv[8];
};
#endif /* _CCM_H_ */

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nrf.h"
#include "clock.h"
#include "debug.h"
static inline void hal_nop(void)
{
__asm__ volatile ("mov r0,r0");
}
#if defined(__GNUC__)
#define NOP() hal_nop()
#else
#define NOP __nop
#endif
static struct {
uint8_t m16src_refcount;
} clock_instance;
uint32_t clock_m16src_start(uint32_t async)
{
/* if clock is already started then just increment refcount.
* refcount can handle 255 (uint8_t) requests, if the start
* and stop dont happen in pairs, a rollover will be caught
* and system should assert.
*/
if (clock_instance.m16src_refcount++) {
goto hf_start_return;
}
DEBUG_RADIO_XTAL(1);
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
if (!async) {
uint32_t intenset;
NVIC_DisableIRQ(POWER_CLOCK_IRQn);
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
intenset = NRF_CLOCK->INTENSET;
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
NOP();
NOP();
NOP();
NOP();
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
__WFE();
}
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
if (!(intenset & CLOCK_INTENSET_HFCLKSTARTED_Msk)) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_HFCLKSTARTED_Msk;
}
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
NVIC_EnableIRQ(POWER_CLOCK_IRQn);
} else {
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
NOP();
NOP();
NOP();
NOP();
}
hf_start_return:
/* rollover should not happen as start and stop shall be
* called in pairs.
*/
ASSERT(clock_instance.m16src_refcount);
return (((NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk)) ? 1 : 0);
}
void clock_m16src_stop(void)
{
ASSERT(clock_instance.m16src_refcount);
if (--clock_instance.m16src_refcount) {
return;
}
DEBUG_RADIO_XTAL(0);
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
}
uint32_t clock_k32src_start(uint32_t src)
{
uint32_t intenset;
if ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk)) {
return 1;
}
NRF_CLOCK->TASKS_LFCLKSTOP = 1;
NVIC_DisableIRQ(POWER_CLOCK_IRQn);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
intenset = NRF_CLOCK->INTENSET;
NRF_CLOCK->INTENSET = CLOCK_INTENSET_LFCLKSTARTED_Msk;
NRF_CLOCK->LFCLKSRC = src;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) {
__WFE();
}
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
if (!(intenset & CLOCK_INTENSET_LFCLKSTARTED_Msk)) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_LFCLKSTARTED_Msk;
}
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
NVIC_EnableIRQ(POWER_CLOCK_IRQn);
/* Calibrate RC, and start timer for consecutive calibrations */
NRF_CLOCK->TASKS_CTSTOP = 1;
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_DONE_Msk | CLOCK_INTENCLR_CTTO_Msk;
NRF_CLOCK->EVENTS_DONE = 0;
NRF_CLOCK->EVENTS_CTTO = 0;
if (!src) {
/* Set the Calibration Timer initial value */
NRF_CLOCK->CTIV = 16; /* 4s in 0.25s units */
/* Enable DONE and CTTO IRQs */
NRF_CLOCK->INTENSET =
CLOCK_INTENSET_DONE_Msk | CLOCK_INTENSET_CTTO_Msk;
/* Start HF clock, if already started then explicitly
* assert IRQ
*/
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
if (clock_m16src_start(1)) {
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
}
}
return ((NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk)) ? 1 : 0;
}
void power_clock_isr(void)
{
uint8_t pof, hf_intenset, hf_stat, hf, lf, done, ctto;
pof = (NRF_POWER->EVENTS_POFWARN != 0);
hf_intenset =
((NRF_CLOCK->INTENSET & CLOCK_INTENSET_HFCLKSTARTED_Msk) != 0);
hf_stat = ((NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) != 0);
hf = (NRF_CLOCK->EVENTS_HFCLKSTARTED != 0);
lf = (NRF_CLOCK->EVENTS_LFCLKSTARTED != 0);
done = (NRF_CLOCK->EVENTS_DONE != 0);
ctto = (NRF_CLOCK->EVENTS_CTTO != 0);
ASSERT(pof || hf || lf || done || ctto);
if (pof) {
NRF_POWER->EVENTS_POFWARN = 0;
}
if (hf) {
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
}
if (hf_intenset && hf_stat) {
NRF_CLOCK->INTENCLR = CLOCK_INTENCLR_HFCLKSTARTED_Msk;
/* Start Calibration */
NRF_CLOCK->TASKS_CAL = 1;
}
if (lf) {
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
ASSERT(0);
}
if (done) {
NRF_CLOCK->EVENTS_DONE = 0;
/* Calibration done, stop 16M Xtal. */
clock_m16src_stop();
/* Start timer for next calibration. */
NRF_CLOCK->TASKS_CTSTART = 1;
}
if (ctto) {
NRF_CLOCK->EVENTS_CTTO = 0;
/* Start HF clock, if already started
* then explicitly assert IRQ
*/
NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
if (clock_m16src_start(1)) {
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
}
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _CLOCK_H_
#define _CLOCK_H_
uint32_t clock_m16src_start(uint32_t async);
void clock_m16src_stop(void);
uint32_t clock_k32src_start(uint32_t src);
void power_clock_isr(void);
#endif

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _CPU_H_
#define _CPU_H_
#include "nrf.h"
static inline void cpu_sleep(void)
{
__WFE();
__SEV();
__WFE();
}
#endif /* _CPU_H_ */

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#ifdef __GNUC__
#define ASSERT(x) do { \
if (!(x)) { \
__asm__ volatile (".inst 0xde00\n"); \
} \
} while (0)
#else
static __asm void _udf(void)
{
UND #0;
}
#define ASSERT(x) do { \
if (!(x)) { \
_udf(); \
} \
} while (0)
#endif
#if (DEBUG == 1)
/*
* P2 is outgoing debug pins.
* Debug pins are initialized to low.
*/
#define DEBUG_INIT() do { \
NRF_GPIO->DIRSET = 0x03FF0000; \
NRF_GPIO->OUTCLR = 0x03FF0000; } \
while (0)
#define DEBUG_CPU_SLEEP(flag) do { \
if (flag) { \
NRF_GPIO->OUTSET = 0x00010000; \
NRF_GPIO->OUTCLR = 0x00010000; } \
else { \
NRF_GPIO->OUTCLR = 0x00010000; \
NRF_GPIO->OUTSET = 0x00010000; } \
} while (0)
#define DEBUG_TICKER_ISR(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00020000; \
NRF_GPIO->OUTSET = 0x00020000; } \
else { \
NRF_GPIO->OUTSET = 0x00020000; \
NRF_GPIO->OUTCLR = 0x00020000; } \
} while (0)
#define DEBUG_TICKER_TASK(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00020000; \
NRF_GPIO->OUTSET = 0x00020000; } \
else { \
NRF_GPIO->OUTSET = 0x00020000; \
NRF_GPIO->OUTCLR = 0x00020000; } \
} while (0)
#define DEBUG_TICKER_JOB(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00040000; \
NRF_GPIO->OUTSET = 0x00040000; } \
else { \
NRF_GPIO->OUTSET = 0x00040000; \
NRF_GPIO->OUTCLR = 0x00040000; } \
} while (0)
#define DEBUG_RADIO_ISR(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00800000; \
NRF_GPIO->OUTSET = 0x00800000; } \
else { \
NRF_GPIO->OUTSET = 0x00800000; \
NRF_GPIO->OUTCLR = 0x00800000; } \
} while (0)
#define DEBUG_RADIO_XTAL(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x01000000; \
NRF_GPIO->OUTSET = 0x01000000; } \
else { \
NRF_GPIO->OUTSET = 0x01000000; \
NRF_GPIO->OUTCLR = 0x01000000; } \
} while (0)
#define DEBUG_RADIO_ACTIVE(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x02000000; \
NRF_GPIO->OUTSET = 0x02000000; } \
else { \
NRF_GPIO->OUTSET = 0x02000000; \
NRF_GPIO->OUTCLR = 0x02000000; } \
} while (0)
#define DEBUG_RADIO_CLOSE(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00000000; \
NRF_GPIO->OUTSET = 0x00000000; } \
else { \
NRF_GPIO->OUTCLR = 0x00780000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_A(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
else { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
} while (0)
#define DEBUG_RADIO_START_A(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
else { \
NRF_GPIO->OUTCLR = 0x00080000; \
NRF_GPIO->OUTSET = 0x00080000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_S(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
else { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
} while (0)
#define DEBUG_RADIO_START_S(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
else { \
NRF_GPIO->OUTCLR = 0x00100000; \
NRF_GPIO->OUTSET = 0x00100000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_O(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
else { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
} while (0)
#define DEBUG_RADIO_START_O(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
else { \
NRF_GPIO->OUTCLR = 0x00200000; \
NRF_GPIO->OUTSET = 0x00200000; } \
} while (0)
#define DEBUG_RADIO_PREPARE_M(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
else { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
} while (0)
#define DEBUG_RADIO_START_M(flag) do { \
if (flag) { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
else { \
NRF_GPIO->OUTCLR = 0x00400000; \
NRF_GPIO->OUTSET = 0x00400000; } \
} while (0)
#else
#define DEBUG_INIT()
#define DEBUG_CPU_SLEEP(flag)
#define DEBUG_TICKER_ISR(flag)
#define DEBUG_TICKER_TASK(flag)
#define DEBUG_TICKER_JOB(flag)
#define DEBUG_RADIO_ISR(flag)
#define DEBUG_RADIO_HCTO(flag)
#define DEBUG_RADIO_XTAL(flag)
#define DEBUG_RADIO_ACTIVE(flag)
#define DEBUG_RADIO_CLOSE(flag)
#define DEBUG_RADIO_PREPARE_A(flag)
#define DEBUG_RADIO_START_A(flag)
#define DEBUG_RADIO_PREPARE_S(flag)
#define DEBUG_RADIO_START_S(flag)
#define DEBUG_RADIO_PREPARE_O(flag)
#define DEBUG_RADIO_START_O(flag)
#define DEBUG_RADIO_PREPARE_M(flag)
#define DEBUG_RADIO_START_M(flag)
#endif /* DEBUG */
#endif /* _DEBUG_H_ */

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <toolchain.h>
#include <string.h>
#include "nrf.h"
#include "mem.h"
#include "ecb.h"
#include "debug.h"
void ecb_encrypt(uint8_t const *const key_le,
uint8_t const *const clear_text_le,
uint8_t * const cipher_text_le,
uint8_t * const cipher_text_be)
{
struct __packed {
uint8_t key[16];
uint8_t clear_text[16];
uint8_t cipher_text[16];
} ecb;
mem_rcopy(&ecb.key[0], key_le, sizeof(ecb.key));
mem_rcopy(&ecb.clear_text[0], clear_text_le, sizeof(ecb.clear_text));
do {
NRF_ECB->TASKS_STOPECB = 1;
NRF_ECB->ECBDATAPTR = (uint32_t) &ecb;
NRF_ECB->EVENTS_ENDECB = 0;
NRF_ECB->EVENTS_ERRORECB = 0;
NRF_ECB->TASKS_STARTECB = 1;
while ((NRF_ECB->EVENTS_ENDECB == 0) &&
(NRF_ECB->EVENTS_ERRORECB == 0) &&
(NRF_ECB->ECBDATAPTR != 0)) {
/*__WFE();*/
}
NRF_ECB->TASKS_STOPECB = 1;
} while ((NRF_ECB->EVENTS_ERRORECB != 0) || (NRF_ECB->ECBDATAPTR == 0));
NRF_ECB->ECBDATAPTR = 0;
if (cipher_text_le) {
mem_rcopy(cipher_text_le, &ecb.cipher_text[0],
sizeof(ecb.cipher_text));
}
if (cipher_text_be) {
memcpy(cipher_text_be, &ecb.cipher_text[0],
sizeof(ecb.cipher_text));
}
}
uint32_t ecb_encrypt_nonblocking(struct ecb *ecb)
{
/* prepare to be used in a BE AES h/w */
if (ecb->in_key_le) {
mem_rcopy(&ecb->in_key_be[0], ecb->in_key_le,
sizeof(ecb->in_key_be));
}
if (ecb->in_clear_text_le) {
mem_rcopy(&ecb->in_clear_text_be[0],
ecb->in_clear_text_le,
sizeof(ecb->in_clear_text_be));
}
/* setup the encryption h/w */
NRF_ECB->ECBDATAPTR = (uint32_t)ecb;
NRF_ECB->EVENTS_ENDECB = 0;
NRF_ECB->EVENTS_ERRORECB = 0;
NRF_ECB->INTENSET = ECB_INTENSET_ERRORECB_Msk | ECB_INTENSET_ENDECB_Msk;
/* setup interrupt */
NVIC_SetPriority(ECB_IRQn, 2);
NVIC_ClearPendingIRQ(ECB_IRQn);
NVIC_EnableIRQ(ECB_IRQn);
/* start the encryption h/w */
NRF_ECB->TASKS_STARTECB = 1;
return 0;
}
static void ecb_cleanup(void)
{
/* stop h/w */
NRF_ECB->TASKS_STOPECB = 1;
/* cleanup interrupt */
NVIC_DisableIRQ(ECB_IRQn);
}
void ecb_isr(void)
{
if (NRF_ECB->EVENTS_ERRORECB) {
struct ecb *ecb = (struct ecb *)NRF_ECB->ECBDATAPTR;
ecb_cleanup();
ecb->fp_ecb(1, 0, ecb->context);
}
else if (NRF_ECB->EVENTS_ENDECB) {
struct ecb *ecb = (struct ecb *)NRF_ECB->ECBDATAPTR;
ecb_cleanup();
ecb->fp_ecb(0, &ecb->out_cipher_text_be[0],
ecb->context);
}
else {
ASSERT(0);
}
}
struct ecb_ut_context {
uint32_t volatile done;
uint32_t status;
uint8_t cipher_text[16];
};
static void ecb_cb(uint32_t status, uint8_t *cipher_be, void *context)
{
struct ecb_ut_context *ecb_ut_context =
(struct ecb_ut_context *)context;
ecb_ut_context->done = 1;
ecb_ut_context->status = status;
if (!status) {
mem_rcopy(ecb_ut_context->cipher_text, cipher_be,
sizeof(ecb_ut_context->cipher_text));
}
}
uint32_t ecb_ut(void)
{
uint8_t key[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t clear_text[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55 };
uint8_t cipher_text[16];
uint32_t status = 0;
struct ecb ecb;
struct ecb_ut_context context;
ecb_encrypt(key, clear_text, cipher_text, 0);
context.done = 0;
ecb.in_key_le = key;
ecb.in_clear_text_le = clear_text;
ecb.fp_ecb = ecb_cb;
ecb.context = &context;
status = ecb_encrypt_nonblocking(&ecb);
do {
__WFE();
__SEV();
__WFE();
} while (!context.done);
if (context.status != 0) {
return context.status;
}
status = memcmp(cipher_text, context.cipher_text, sizeof(cipher_text));
if (status) {
return status;
}
return status;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ECB_H_
#define _ECB_H_
typedef void (*ecb_fp) (uint32_t status, uint8_t *cipher_be,
void *context);
struct ecb {
uint8_t in_key_be[16];
uint8_t in_clear_text_be[16];
uint8_t out_cipher_text_be[16];
/* if not null reverse copy into in_key_be */
uint8_t *in_key_le;
/* if not null reverse copy into in_clear_text_be */
uint8_t *in_clear_text_le;
ecb_fp fp_ecb;
void *context;
};
void ecb_encrypt(uint8_t const *const key_le,
uint8_t const *const clear_text_le,
uint8_t * const cipher_text_le,
uint8_t * const cipher_text_be);
uint32_t ecb_encrypt_nonblocking(struct ecb *ecb);
void ecb_isr(void);
uint32_t ecb_ut(void);
#endif /* _ECB_H_ */

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IRQ_H_
#define _IRQ_H_
void irq_enable(uint8_t irq);
void irq_disable(uint8_t irq);
void irq_pending_set(uint8_t irq);
uint8_t irq_enabled(uint8_t irq);
uint8_t irq_priority_equal(uint8_t irq);
#endif /* _IRQ_H_ */

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RTC_H_
#define _RTC_H_
void rtc_init(void);
uint32_t rtc_start(void);
uint32_t rtc_stop(void);
uint32_t rtc_tick_get(void);
uint32_t rtc_compare_set(uint8_t instance, uint32_t value);
#endif /* _RTC_H_ */

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _HAL_WORK_H_
#define _HAL_WORK_H_
#include "nrf.h"
#define WORK_TICKER_WORKER0_IRQ RTC0_IRQn
#define WORK_TICKER_JOB0_IRQ SWI4_IRQn
#define WORK_TICKER_WORKER1_IRQ SWI5_IRQn
#define WORK_TICKER_JOB1_IRQ SWI5_IRQn
#define WORK_TICKER_WORKER0_IRQ_PRIORITY 0
#define WORK_TICKER_JOB0_IRQ_PRIORITY 0
#define WORK_TICKER_WORKER1_IRQ_PRIORITY 2
#define WORK_TICKER_JOB1_IRQ_PRIORITY 2
#endif /* _HAL_WORK_H_ */

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nrf.h"
#include "hal_irq.h"
void irq_enable(uint8_t irq)
{
NVIC_EnableIRQ((IRQn_Type) irq);
}
void irq_disable(uint8_t irq)
{
NVIC_DisableIRQ((IRQn_Type) irq);
}
void irq_pending_set(uint8_t irq)
{
NVIC_SetPendingIRQ((IRQn_Type) irq);
}
uint8_t irq_enabled(uint8_t irq)
{
return ((NVIC->ISER[0] & (1 << ((uint32_t) (irq) & 0x1F))) != 0);
}
uint8_t irq_priority_equal(uint8_t irq)
{
uint32_t irq_current = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
uint32_t irq_priority_current = 4;
if (irq_current > 16) {
irq_priority_current =
NVIC_GetPriority((IRQn_Type) (irq_current - 16)) & 0xFF;
}
return ((NVIC_GetPriority((IRQn_Type) irq) & 0xFF) ==
irq_priority_current);
}

View file

@ -0,0 +1,552 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <misc/util.h>
#include "nrf.h"
#include "hal_work.h"
#include "defines.h"
#include "ccm.h"
#include "radio.h"
#if defined(NRF51)
#define RADIO_PDU_LEN_MAX (BIT(5) - 1)
#elif defined(NRF52)
#define RADIO_PDU_LEN_MAX (BIT(8) - 1)
#else
#error "Platform not defined."
#endif
static radio_isr_fp sfp_radio_isr;
void radio_isr(void)
{
if (sfp_radio_isr) {
sfp_radio_isr();
}
}
void radio_isr_set(radio_isr_fp fp_radio_isr)
{
sfp_radio_isr = fp_radio_isr; /* atomic assignment of 32-bit word */
NRF_RADIO->INTENSET = (0 |
/* RADIO_INTENSET_READY_Msk |
* RADIO_INTENSET_ADDRESS_Msk |
* RADIO_INTENSET_PAYLOAD_Msk |
* RADIO_INTENSET_END_Msk |
*/
RADIO_INTENSET_DISABLED_Msk
/* | RADIO_INTENSET_RSSIEND_Msk |
*/
);
NVIC_SetPriority(RADIO_IRQn, WORK_TICKER_WORKER0_IRQ_PRIORITY);
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
}
void radio_reset(void)
{
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Disabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
NRF_RADIO->POWER =
((RADIO_POWER_POWER_Enabled << RADIO_POWER_POWER_Pos) &
RADIO_POWER_POWER_Msk);
}
void radio_phy_set(uint8_t phy)
{
NRF_RADIO->MODE =
(((phy) ? (uint32_t)phy : RADIO_MODE_MODE_Ble_1Mbit) <<
RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk;
}
void radio_tx_power_set(uint32_t power)
{
/* TODO map power to h/w values. */
NRF_RADIO->TXPOWER = power;
}
void radio_freq_chnl_set(uint32_t chnl)
{
NRF_RADIO->FREQUENCY = chnl;
}
void radio_whiten_iv_set(uint32_t iv)
{
NRF_RADIO->DATAWHITEIV = iv;
}
void radio_aa_set(uint8_t *aa)
{
NRF_RADIO->TXADDRESS =
(((0UL) << RADIO_TXADDRESS_TXADDRESS_Pos) &
RADIO_TXADDRESS_TXADDRESS_Msk);
NRF_RADIO->RXADDRESSES =
((RADIO_RXADDRESSES_ADDR0_Enabled) << RADIO_RXADDRESSES_ADDR0_Pos);
NRF_RADIO->PREFIX0 = aa[3];
NRF_RADIO->BASE0 = (aa[2] << 24) | (aa[1] << 16) | (aa[0] << 8);
}
void radio_pkt_configure(uint8_t preamble16, uint8_t bits_len, uint8_t max_len)
{
#if defined(NRF51)
if (bits_len == 8) {
bits_len = 5;
}
#endif
NRF_RADIO->PCNF0 = ((((1UL) << RADIO_PCNF0_S0LEN_Pos) &
RADIO_PCNF0_S0LEN_Msk) |
((((uint32_t)bits_len) << RADIO_PCNF0_LFLEN_Pos) &
RADIO_PCNF0_LFLEN_Msk) |
((((uint32_t)8-bits_len) <<
RADIO_PCNF0_S1LEN_Pos) &
RADIO_PCNF0_S1LEN_Msk) |
(((RADIO_PCNF0_S1INCL_Include) <<
RADIO_PCNF0_S1INCL_Pos) &
RADIO_PCNF0_S1INCL_Msk) |
((((preamble16) ? RADIO_PCNF0_PLEN_16bit :
RADIO_PCNF0_PLEN_8bit) << RADIO_PCNF0_PLEN_Pos) &
RADIO_PCNF0_PLEN_Msk));
NRF_RADIO->PCNF1 = (((((uint32_t)max_len) << RADIO_PCNF1_MAXLEN_Pos) &
RADIO_PCNF1_MAXLEN_Msk) |
(((0UL) << RADIO_PCNF1_STATLEN_Pos) &
RADIO_PCNF1_STATLEN_Msk) |
(((3UL) << RADIO_PCNF1_BALEN_Pos) &
RADIO_PCNF1_BALEN_Msk) |
(((RADIO_PCNF1_ENDIAN_Little) <<
RADIO_PCNF1_ENDIAN_Pos) &
RADIO_PCNF1_ENDIAN_Msk) |
(((1UL) << RADIO_PCNF1_WHITEEN_Pos) &
RADIO_PCNF1_WHITEEN_Msk));
}
void radio_pkt_rx_set(void *rx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)rx_packet;
}
void radio_pkt_tx_set(void *tx_packet)
{
NRF_RADIO->PACKETPTR = (uint32_t)tx_packet;
}
void radio_rx_enable(void)
{
NRF_RADIO->TASKS_RXEN = 1;
}
void radio_tx_enable(void)
{
NRF_RADIO->TASKS_TXEN = 1;
}
void radio_disable(void)
{
NRF_RADIO->SHORTS = 0;
NRF_RADIO->TASKS_DISABLE = 1;
}
void radio_status_reset(void)
{
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_ADDRESS = 0;
NRF_RADIO->EVENTS_PAYLOAD = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
}
uint32_t radio_is_ready(void)
{
return NRF_RADIO->EVENTS_READY;
}
uint32_t radio_is_done(void)
{
return NRF_RADIO->EVENTS_END;
}
uint32_t radio_has_disabled(void)
{
return NRF_RADIO->EVENTS_DISABLED;
}
uint32_t radio_is_idle(void)
{
return (NRF_RADIO->STATE == 0);
}
void radio_crc_configure(uint32_t polynomial, uint32_t iv)
{
NRF_RADIO->CRCCNF =
(((RADIO_CRCCNF_SKIPADDR_Skip) << RADIO_CRCCNF_SKIPADDR_Pos) &
RADIO_CRCCNF_SKIPADDR_Msk) |
(((RADIO_CRCCNF_LEN_Three) << RADIO_CRCCNF_LEN_Pos) &
RADIO_CRCCNF_LEN_Msk);
NRF_RADIO->CRCPOLY = polynomial;
NRF_RADIO->CRCINIT = iv;
}
uint32_t radio_crc_is_valid(void)
{
return NRF_RADIO->CRCSTATUS;
}
static uint8_t ALIGNED(4) _pkt_empty[RADIO_EMPDU_SIZE_MAX];
static uint8_t ALIGNED(4) _pkt_scratch[
((RADIO_PDU_LEN_MAX + 3) > RADIO_ACPDU_SIZE_MAX) ?
(RADIO_PDU_LEN_MAX + 3) : RADIO_ACPDU_SIZE_MAX];
void *radio_pkt_empty_get(void)
{
return _pkt_empty;
}
void *radio_pkt_scratch_get(void)
{
return _pkt_scratch;
}
void radio_switch_complete_and_rx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_RXEN_Msk);
}
void radio_switch_complete_and_tx(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |
RADIO_SHORTS_DISABLED_TXEN_Msk);
}
void radio_switch_complete_and_disable(void)
{
NRF_RADIO->SHORTS =
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk);
}
void radio_rssi_measure(void)
{
NRF_RADIO->SHORTS |=
(RADIO_SHORTS_ADDRESS_RSSISTART_Msk |
RADIO_SHORTS_DISABLED_RSSISTOP_Msk);
}
uint32_t radio_rssi_get(void)
{
return NRF_RADIO->RSSISAMPLE;
}
void radio_rssi_status_reset(void)
{
NRF_RADIO->EVENTS_RSSIEND = 0;
}
uint32_t radio_rssi_is_ready(void)
{
return NRF_RADIO->EVENTS_RSSIEND;
}
void radio_filter_configure(uint8_t bitmask_enable,
uint8_t bitmask_addr_type,
uint8_t *bdaddr)
{
uint8_t index = 8;
while (index--) {
NRF_RADIO->DAB[index] = ((uint32_t)bdaddr[3] << 24) |
((uint32_t)bdaddr[2] << 16) |
((uint32_t)bdaddr[1] << 8) |
bdaddr[0];
NRF_RADIO->DAP[index] = ((uint32_t)bdaddr[5] << 8) | bdaddr[4];
bdaddr += BDADDR_SIZE;
}
NRF_RADIO->DACNF = ((uint32_t)bitmask_addr_type << 8) | bitmask_enable;
}
void radio_filter_disable(void)
{
NRF_RADIO->DACNF &= ~(0x000000FF);
}
void radio_filter_status_reset(void)
{
NRF_RADIO->EVENTS_DEVMATCH = 0;
NRF_RADIO->EVENTS_DEVMISS = 0;
}
uint32_t radio_filter_has_match(void)
{
return NRF_RADIO->EVENTS_DEVMATCH;
}
void radio_bc_configure(uint32_t n)
{
NRF_RADIO->BCC = n;
NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_BCSTART_Msk;
}
void radio_bc_status_reset(void)
{
NRF_RADIO->EVENTS_BCMATCH = 0;
}
uint32_t radio_bc_has_match(void)
{
return NRF_RADIO->EVENTS_BCMATCH;
}
void radio_tmr_status_reset(void)
{
NRF_RTC0->EVTENCLR = RTC_EVTENCLR_COMPARE2_Msk;
NRF_PPI->CHENCLR =
(PPI_CHEN_CH0_Msk | PPI_CHEN_CH1_Msk | PPI_CHEN_CH2_Msk |
PPI_CHEN_CH3_Msk | PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk |
PPI_CHEN_CH6_Msk);
}
void radio_tmr_tifs_set(uint32_t tifs)
{
NRF_RADIO->TIFS = tifs;
}
uint32_t radio_tmr_start(uint8_t trx, uint32_t ticks_start, uint32_t remainder)
{
if ((!(remainder / 1000000UL)) || (remainder & 0x80000000)) {
ticks_start--;
remainder += 30517578UL;
}
remainder /= 1000000UL;
NRF_TIMER0->TASKS_CLEAR = 1;
NRF_TIMER0->MODE = 0;
NRF_TIMER0->PRESCALER = 4;
NRF_TIMER0->BITMODE = 2; /* 24 - bit */
NRF_TIMER0->CC[0] = remainder;
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
NRF_RTC0->CC[2] = ticks_start;
NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE2_Msk;
NRF_RTC0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[1].EEP = (uint32_t)&(NRF_RTC0->EVENTS_COMPARE[2]);
NRF_PPI->CH[1].TEP = (uint32_t)&(NRF_TIMER0->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH1_Msk;
if (trx) {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_TXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
} else {
NRF_PPI->CH[0].EEP =
(uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP =
(uint32_t)&(NRF_RADIO->TASKS_RXEN);
NRF_PPI->CHENSET = PPI_CHEN_CH0_Msk;
}
return remainder;
}
void radio_tmr_stop(void)
{
NRF_TIMER0->TASKS_STOP = 1;
NRF_TIMER0->TASKS_SHUTDOWN = 1;
}
void radio_tmr_hcto_configure(uint32_t hcto)
{
NRF_TIMER0->CC[2] = hcto;
NRF_TIMER0->EVENTS_COMPARE[2] = 0;
NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[2]);
NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE);
NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk);
}
void radio_tmr_aa_capture(void)
{
NRF_PPI->CH[2].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[2].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[0]);
NRF_PPI->CH[3].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[3].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[1]);
NRF_PPI->CHENSET = (PPI_CHEN_CH2_Msk | PPI_CHEN_CH3_Msk);
}
uint32_t radio_tmr_aa_get(void)
{
return (NRF_TIMER0->CC[1] - NRF_TIMER0->CC[0]);
}
void radio_tmr_end_capture(void)
{
NRF_PPI->CH[2].EEP = (uint32_t)&(NRF_RADIO->EVENTS_END);
NRF_PPI->CH[2].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[2]);
NRF_PPI->CHENSET = PPI_CHEN_CH2_Msk;
}
uint32_t radio_tmr_end_get(void)
{
return NRF_TIMER0->CC[2];
}
static uint8_t ALIGNED(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16];
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
((CCM_MODE_MODE_Decryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk) |
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)_pkt_scratch;
NRF_CCM->OUTPTR = (uint32_t)pkt;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = 0;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_CRYPT);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
NRF_CCM->TASKS_KSGEN = 1;
return _pkt_scratch;
}
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt)
{
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;
NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;
NRF_CCM->MODE =
((CCM_MODE_MODE_Encryption << CCM_MODE_MODE_Pos) &
CCM_MODE_MODE_Msk) |
((CCM_MODE_LENGTH_Extended << CCM_MODE_LENGTH_Pos) &
CCM_MODE_LENGTH_Msk);
NRF_CCM->CNFPTR = (uint32_t)ccm;
NRF_CCM->INPTR = (uint32_t)pkt;
NRF_CCM->OUTPTR = (uint32_t)_pkt_scratch;
NRF_CCM->SCRATCHPTR = (uint32_t)_ccm_scratch;
NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk;
NRF_CCM->EVENTS_ENDKSGEN = 0;
NRF_CCM->EVENTS_ENDCRYPT = 0;
NRF_CCM->EVENTS_ERROR = 0;
#if NRF51
/* set up PPI to enable CCM */
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_READY);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_CCM->TASKS_KSGEN);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
#elif 0
/* encrypt tx packet */
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
NRF_CCM->TASKS_KSGEN = 1;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
NVIC_ClearPendingIRQ(CCM_AAR_IRQn);
ASSERT(NRF_CCM->EVENTS_ERROR == 0);
#else
/* start KSGEN early, but dont wait for ENDCRYPT */
NRF_CCM->TASKS_KSGEN = 1;
#endif
return _pkt_scratch;
}
uint32_t radio_ccm_is_done(void)
{
NRF_CCM->INTENSET = CCM_INTENSET_ENDCRYPT_Msk;
while (NRF_CCM->EVENTS_ENDCRYPT == 0) {
__WFE();
__SEV();
__WFE();
}
NRF_CCM->INTENCLR = CCM_INTENCLR_ENDCRYPT_Msk;
NVIC_ClearPendingIRQ(CCM_AAR_IRQn);
return (NRF_CCM->EVENTS_ERROR == 0);
}
uint32_t radio_ccm_mic_is_valid(void)
{
return NRF_CCM->MICSTATUS;
}
static uint8_t ALIGNED(4) _aar_scratch[3];
void radio_ar_configure(uint32_t nirk, void *irk)
{
NRF_AAR->ENABLE = 1;
NRF_AAR->NIRK = nirk;
NRF_AAR->IRKPTR = (uint32_t)irk;
NRF_AAR->ADDRPTR = (uint32_t)NRF_RADIO->PACKETPTR;
NRF_AAR->SCRATCHPTR = (uint32_t)_aar_scratch[0];
radio_bc_configure(64);
NRF_PPI->CH[6].EEP = (uint32_t)&(NRF_RADIO->EVENTS_BCMATCH);
NRF_PPI->CH[6].TEP = (uint32_t)&(NRF_AAR->TASKS_START);
NRF_PPI->CHENSET = PPI_CHEN_CH6_Msk;
}
uint32_t radio_ar_match_get(void)
{
return NRF_AAR->STATUS;
}
void radio_ar_status_reset(void)
{
if (radio_bc_has_match()) {
NRF_AAR->EVENTS_END = 0;
NRF_AAR->EVENTS_RESOLVED = 0;
NRF_AAR->EVENTS_NOTRESOLVED = 0;
}
radio_bc_status_reset();
}
uint32_t radio_ar_has_match(void)
{
return (radio_bc_has_match() &&
(NRF_AAR->EVENTS_END) &&
(NRF_AAR->EVENTS_RESOLVED));
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RADIO_H_
#define _RADIO_H_
/* Ramp up times from OPS.
*/
#define RADIO_TX_READY_DELAY_US 140
#define RADIO_RX_READY_DELAY_US 138
/* Chain delays from OPS.
* nRF51: Tx= 1us, Rx= 3us;
* nRF52: Tx= 0.6us, Rx= 9.4us.
*/
#define RADIO_TX_CHAIN_DELAY_US 1
#define RADIO_RX_CHAIN_DELAY_US 10
/* Radio Pkt Size */
#define RADIO_EMPDU_SIZE_MAX 3
#define RADIO_ACPDU_SIZE_OVERHEAD 3
#define RADIO_ACPDU_SIZE_MAX (37 + RADIO_ACPDU_SIZE_OVERHEAD)
typedef void (*radio_isr_fp) (void);
void radio_isr(void);
void radio_isr_set(radio_isr_fp fp_radio_isr);
void radio_reset(void);
void radio_phy_set(uint8_t phy);
void radio_tx_power_set(uint32_t power);
void radio_freq_chnl_set(uint32_t chnl);
void radio_whiten_iv_set(uint32_t iv);
void radio_aa_set(uint8_t *aa);
void radio_pkt_configure(uint8_t preamble16, uint8_t bits_len, uint8_t max_len);
void radio_pkt_rx_set(void *rx_packet);
void radio_pkt_tx_set(void *tx_packet);
void radio_rx_enable(void);
void radio_tx_enable(void);
void radio_disable(void);
void radio_status_reset(void);
uint32_t radio_is_ready(void);
uint32_t radio_is_done(void);
uint32_t radio_has_disabled(void);
uint32_t radio_is_idle(void);
void radio_crc_configure(uint32_t polynomial, uint32_t iv);
uint32_t radio_crc_is_valid(void);
void *radio_pkt_empty_get(void);
void *radio_pkt_scratch_get(void);
void radio_switch_complete_and_rx(void);
void radio_switch_complete_and_tx(void);
void radio_switch_complete_and_disable(void);
void radio_rssi_measure(void);
uint32_t radio_rssi_get(void);
void radio_rssi_status_reset(void);
uint32_t radio_rssi_is_ready(void);
void radio_filter_configure(uint8_t bitmask_enable,
uint8_t bitmask_addr_type,
uint8_t *bdaddr);
void radio_filter_disable(void);
void radio_filter_status_reset(void);
uint32_t radio_filter_has_match(void);
void radio_bc_configure(uint32_t n);
void radio_bc_status_reset(void);
uint32_t radio_bc_has_match(void);
void radio_tmr_status_reset(void);
void radio_tmr_tifs_set(uint32_t tifs);
uint32_t radio_tmr_start(uint8_t trx, uint32_t ticks_start, uint32_t remainder);
void radio_tmr_stop(void);
void radio_tmr_hcto_configure(uint32_t hcto);
void radio_tmr_aa_capture(void);
uint32_t radio_tmr_aa_get(void);
void radio_tmr_end_capture(void);
uint32_t radio_tmr_end_get(void);
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt);
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt);
uint32_t radio_ccm_is_done(void);
uint32_t radio_ccm_mic_is_valid(void);
void radio_ar_configure(uint32_t nirk, void *irk);
uint32_t radio_ar_match_get(void);
void radio_ar_status_reset(void);
uint32_t radio_ar_has_match(void);
#endif

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nrf.h"
#include "rand.h"
#include "debug.h"
#define RAND_RESERVED (4)
struct rand {
uint8_t count;
uint8_t first;
uint8_t last;
uint8_t rand[1];
};
static struct rand *_rand;
void rand_init(uint8_t *context, uint8_t context_len)
{
ASSERT(context_len > sizeof(struct rand));
_rand = (struct rand *)context;
_rand->count = context_len - sizeof(struct rand) + 1;
_rand->first = _rand->last = 0;
NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Msk;
NRF_RNG->EVENTS_VALRDY = 0;
NRF_RNG->INTENSET = RNG_INTENSET_VALRDY_Msk;
NVIC_SetPriority(RNG_IRQn, 2);
NVIC_EnableIRQ(RNG_IRQn);
NRF_RNG->TASKS_START = 1;
}
uint32_t rand_get(uint8_t octets, uint8_t *rand)
{
uint8_t reserved;
uint8_t first;
while (octets) {
if (_rand->first == _rand->last) {
break;
}
rand[--octets] = _rand->rand[_rand->first];
first = _rand->first + 1;
if (first == _rand->count) {
first = 0;
}
_rand->first = first;
}
reserved = RAND_RESERVED;
first = _rand->first;
while (reserved--) {
if (first == _rand->last) {
NRF_RNG->TASKS_START = 1;
break;
}
first++;
if (first == _rand->count) {
first = 0;
}
}
return octets;
}
void rng_isr(void)
{
if (NRF_RNG->EVENTS_VALRDY) {
uint8_t last;
last = _rand->last + 1;
if (last == _rand->count) {
last = 0;
}
if (last == _rand->first) {
/* this condition should not happen
* , but due to probable bug in HW
* , new value could be generated
* before task is stopped.
*/
NRF_RNG->TASKS_STOP = 1;
NRF_RNG->EVENTS_VALRDY = 0;
return;
}
_rand->rand[_rand->last] = NRF_RNG->VALUE;
_rand->last = last;
last = _rand->last + 1;
if (last == _rand->count) {
last = 0;
}
NRF_RNG->EVENTS_VALRDY = 0;
if (last == _rand->first) {
NRF_RNG->TASKS_STOP = 1;
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RAND_H_
#define _RAND_H_
void rand_init(uint8_t *context, uint8_t context_len);
uint32_t rand_get(uint8_t octets, uint8_t *rand);
void rng_isr(void);
#endif /* _RAND_H_ */

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nrf.h"
#include "debug.h"
/* RTC instance to use */
#define NRF_RTC NRF_RTC0
static uint8_t _rtc_refcount;
void rtc_init(void)
{
NRF_RTC->PRESCALER = 0;
NRF_RTC->EVTENSET =
(RTC_EVTENSET_COMPARE0_Msk | RTC_EVTENSET_COMPARE1_Msk);
NRF_RTC->INTENSET =
(RTC_INTENSET_COMPARE0_Msk | RTC_INTENSET_COMPARE1_Msk);
}
uint32_t rtc_start(void)
{
if (_rtc_refcount++) {
return 1;
}
NRF_RTC->TASKS_START = 1;
return 0;
}
uint32_t rtc_stop(void)
{
ASSERT(_rtc_refcount);
if (--_rtc_refcount) {
return 1;
}
NRF_RTC->TASKS_STOP = 1;
return 0;
}
uint32_t rtc_tick_get(void)
{
return NRF_RTC->COUNTER;
}
void rtc_compare_set(uint8_t instance, uint32_t value)
{
NRF_RTC->CC[instance] = value;
}