7571be3261
Acknowledgment is mandatory if legitimately requested by the package's "ACK requested" flag. The L2 layer will have to ensure that compliant ACK packages will always be sent out automatically as required by the standard. For IEEE 802.15.4 compliance, the NET_L2_IEEE802154_ACK_REPLY option is therefore being deprecated. Signed-off-by: Florian Grandel <fgrandel@code-for-humans.de>
1394 lines
31 KiB
C
1394 lines
31 KiB
C
/* ieee802154_cc2520.c - TI CC2520 driver */
|
|
|
|
#define DT_DRV_COMPAT ti_cc2520
|
|
|
|
/*
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define LOG_MODULE_NAME ieee802154_cc2520
|
|
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/debug/stack.h>
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/net_pkt.h>
|
|
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <string.h>
|
|
#include <zephyr/random/rand32.h>
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
#include <zephyr/crypto/crypto.h>
|
|
#include <zephyr/crypto/cipher.h>
|
|
|
|
#endif /* CONFIG_IEEE802154_CC2520_CRYPTO */
|
|
|
|
#include <zephyr/net/ieee802154_radio.h>
|
|
|
|
#include "ieee802154_cc2520.h"
|
|
|
|
/**
|
|
* Content is split as follows:
|
|
* 1 - Debug related functions
|
|
* 2 - Generic helper functions (for any parts)
|
|
* 3 - GPIO related functions
|
|
* 4 - TX related helper functions
|
|
* 5 - RX related helper functions
|
|
* 6 - Radio device API functions
|
|
* 7 - Legacy radio device API functions
|
|
* 8 - Initialization
|
|
*/
|
|
|
|
|
|
#define CC2520_AUTOMATISM (FRMCTRL0_AUTOCRC | FRMCTRL0_AUTOACK)
|
|
#define CC2520_TX_THRESHOLD (0x7F)
|
|
#define CC2520_FCS_LENGTH (2)
|
|
|
|
/*********
|
|
* DEBUG *
|
|
********/
|
|
#if LOG_LEVEL == LOG_LEVEL_DBG
|
|
static inline void cc2520_print_gpio_config(const struct device *dev)
|
|
{
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
|
|
LOG_DBG("GPIOCTRL0/1/2/3/4/5 = 0x%x/0x%x/0x%x/0x%x/0x%x/0x%x",
|
|
read_reg_gpioctrl0(cc2520),
|
|
read_reg_gpioctrl1(cc2520),
|
|
read_reg_gpioctrl2(cc2520),
|
|
read_reg_gpioctrl3(cc2520),
|
|
read_reg_gpioctrl4(cc2520),
|
|
read_reg_gpioctrl5(cc2520));
|
|
LOG_DBG("GPIOPOLARITY: 0x%x",
|
|
read_reg_gpiopolarity(cc2520));
|
|
LOG_DBG("GPIOCTRL: 0x%x",
|
|
read_reg_gpioctrl(cc2520));
|
|
}
|
|
|
|
static inline void cc2520_print_exceptions(struct cc2520_context *cc2520)
|
|
{
|
|
uint8_t flag = read_reg_excflag0(cc2520);
|
|
|
|
LOG_DBG("EXCFLAG0:");
|
|
|
|
if (flag & EXCFLAG0_RF_IDLE) {
|
|
LOG_DBG(" RF_IDLE");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_TX_FRM_DONE) {
|
|
LOG_DBG(" TX_FRM_DONE");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_TX_ACK_DONE) {
|
|
LOG_DBG(" TX_ACK_DONE");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_TX_UNDERFLOW) {
|
|
LOG_DBG(" TX_UNDERFLOW");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_TX_OVERFLOW) {
|
|
LOG_DBG(" TX_OVERFLOW");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_RX_UNDERFLOW) {
|
|
LOG_DBG(" RX_UNDERFLOW");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_RX_OVERFLOW) {
|
|
LOG_DBG(" RX_OVERFLOW");
|
|
}
|
|
|
|
if (flag & EXCFLAG0_RXENABLE_ZERO) {
|
|
LOG_DBG(" RXENABLE_ZERO");
|
|
}
|
|
|
|
flag = read_reg_excflag1(cc2520);
|
|
|
|
LOG_DBG("EXCFLAG1:");
|
|
|
|
if (flag & EXCFLAG1_RX_FRM_DONE) {
|
|
LOG_DBG(" RX_FRM_DONE");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_RX_FRM_ACCEPTED) {
|
|
LOG_DBG(" RX_FRM_ACCEPTED");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_SRC_MATCH_DONE) {
|
|
LOG_DBG(" SRC_MATCH_DONE");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_SRC_MATCH_FOUND) {
|
|
LOG_DBG(" SRC_MATCH_FOUND");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_FIFOP) {
|
|
LOG_DBG(" FIFOP");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_SFD) {
|
|
LOG_DBG(" SFD");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_DPU_DONE_L) {
|
|
LOG_DBG(" DPU_DONE_L");
|
|
}
|
|
|
|
if (flag & EXCFLAG1_DPU_DONE_H) {
|
|
LOG_DBG(" DPU_DONE_H");
|
|
}
|
|
}
|
|
|
|
static inline void cc2520_print_errors(struct cc2520_context *cc2520)
|
|
{
|
|
uint8_t flag = read_reg_excflag2(cc2520);
|
|
|
|
LOG_DBG("EXCFLAG2:");
|
|
|
|
if (flag & EXCFLAG2_MEMADDR_ERROR) {
|
|
LOG_DBG(" MEMADDR_ERROR");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_USAGE_ERROR) {
|
|
LOG_DBG(" USAGE_ERROR");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_OPERAND_ERROR) {
|
|
LOG_DBG(" OPERAND_ERROR");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_SPI_ERROR) {
|
|
LOG_DBG(" SPI_ERROR");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_RF_NO_LOCK) {
|
|
LOG_DBG(" RF_NO_LOCK");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_RX_FRM_ABORTED) {
|
|
LOG_DBG(" RX_FRM_ABORTED");
|
|
}
|
|
|
|
if (flag & EXCFLAG2_RFBUFMOV_TIMEOUT) {
|
|
LOG_DBG(" RFBUFMOV_TIMEOUT");
|
|
}
|
|
}
|
|
#else
|
|
#define cc2520_print_gpio_config(...)
|
|
#define cc2520_print_exceptions(...)
|
|
#define cc2520_print_errors(...)
|
|
#endif /* LOG_LEVEL == LOG_LEVEL_DBG */
|
|
|
|
|
|
/*********************
|
|
* Generic functions *
|
|
********************/
|
|
#define z_usleep(usec) k_busy_wait(usec)
|
|
|
|
bool z_cc2520_access(const struct device *dev, bool read, uint8_t ins,
|
|
uint16_t addr, void *data, size_t length)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
uint8_t cmd_buf[2];
|
|
struct spi_buf buf[2] = {
|
|
{
|
|
.buf = cmd_buf,
|
|
.len = 1,
|
|
},
|
|
{
|
|
.buf = data,
|
|
.len = length,
|
|
|
|
}
|
|
};
|
|
struct spi_buf_set tx = {
|
|
.buffers = buf,
|
|
};
|
|
|
|
|
|
cmd_buf[0] = ins;
|
|
|
|
if (ins == CC2520_INS_MEMRD || ins == CC2520_INS_MEMWR) {
|
|
buf[0].len = 2;
|
|
cmd_buf[0] |= (uint8_t)(addr >> 8);
|
|
cmd_buf[1] = (uint8_t)(addr & 0xff);
|
|
} else if (ins == CC2520_INS_REGRD || ins == CC2520_INS_REGWR) {
|
|
cmd_buf[0] |= (uint8_t)(addr & 0xff);
|
|
}
|
|
|
|
if (read) {
|
|
const struct spi_buf_set rx = {
|
|
.buffers = buf,
|
|
.count = 2
|
|
};
|
|
|
|
tx.count = 1;
|
|
|
|
return (spi_transceive_dt(&cfg->bus, &tx, &rx) == 0);
|
|
}
|
|
|
|
tx.count = data ? 2 : 1;
|
|
|
|
return (spi_write_dt(&cfg->bus, &tx) == 0);
|
|
}
|
|
|
|
static inline uint8_t cc2520_status(const struct device *dev)
|
|
{
|
|
uint8_t status;
|
|
|
|
if (z_cc2520_access(dev, true, CC2520_INS_SNOP, 0, &status, 1)) {
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool verify_osc_stabilization(const struct device *dev)
|
|
{
|
|
uint8_t timeout = 100U;
|
|
uint8_t status;
|
|
|
|
do {
|
|
status = cc2520_status(dev);
|
|
z_usleep(1);
|
|
timeout--;
|
|
} while (!(status & CC2520_STATUS_XOSC_STABLE_N_RUNNING) && timeout);
|
|
|
|
return !!(status & CC2520_STATUS_XOSC_STABLE_N_RUNNING);
|
|
}
|
|
|
|
|
|
static inline uint8_t *get_mac(const struct device *dev)
|
|
{
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RANDOM_MAC)
|
|
uint32_t *ptr = (uint32_t *)(cc2520->mac_addr + 4);
|
|
|
|
UNALIGNED_PUT(sys_rand32_get(), ptr);
|
|
|
|
cc2520->mac_addr[7] = (cc2520->mac_addr[7] & ~0x01) | 0x02;
|
|
#else
|
|
cc2520->mac_addr[4] = CONFIG_IEEE802154_CC2520_MAC4;
|
|
cc2520->mac_addr[5] = CONFIG_IEEE802154_CC2520_MAC5;
|
|
cc2520->mac_addr[6] = CONFIG_IEEE802154_CC2520_MAC6;
|
|
cc2520->mac_addr[7] = CONFIG_IEEE802154_CC2520_MAC7;
|
|
#endif
|
|
|
|
cc2520->mac_addr[0] = 0x00;
|
|
cc2520->mac_addr[1] = 0x12;
|
|
cc2520->mac_addr[2] = 0x4b;
|
|
cc2520->mac_addr[3] = 0x00;
|
|
|
|
return cc2520->mac_addr;
|
|
}
|
|
|
|
static int cc2520_set_pan_id(const struct device *dev, uint16_t pan_id)
|
|
{
|
|
LOG_DBG("0x%x", pan_id);
|
|
|
|
pan_id = sys_le16_to_cpu(pan_id);
|
|
|
|
if (!write_mem_pan_id(dev, (uint8_t *) &pan_id)) {
|
|
LOG_ERR("Failed");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_set_short_addr(const struct device *dev,
|
|
uint16_t short_addr)
|
|
{
|
|
LOG_DBG("0x%x", short_addr);
|
|
|
|
short_addr = sys_le16_to_cpu(short_addr);
|
|
|
|
if (!write_mem_short_addr(dev, (uint8_t *) &short_addr)) {
|
|
LOG_ERR("Failed");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_set_ieee_addr(const struct device *dev,
|
|
const uint8_t *ieee_addr)
|
|
{
|
|
if (!write_mem_ext_addr(dev, (void *)ieee_addr)) {
|
|
LOG_ERR("Failed");
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
|
|
ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************
|
|
* GPIO functions *
|
|
*****************/
|
|
static inline void set_reset(const struct device *dev, uint32_t value)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
gpio_pin_set_raw(cfg->reset.port, cfg->reset.pin, value);
|
|
}
|
|
|
|
static inline void set_vreg_en(const struct device *dev, uint32_t value)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
gpio_pin_set_raw(cfg->vreg_en.port, cfg->vreg_en.pin, value);
|
|
}
|
|
|
|
static inline uint32_t get_fifo(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
return gpio_pin_get_raw(cfg->fifo.port, cfg->fifo.pin);
|
|
}
|
|
|
|
static inline uint32_t get_fifop(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
return gpio_pin_get_raw(cfg->fifop.port, cfg->fifop.pin);
|
|
}
|
|
|
|
static inline uint32_t get_cca(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
return gpio_pin_get_raw(cfg->cca.port, cfg->cca.pin);
|
|
}
|
|
|
|
static inline void sfd_int_handler(const struct device *port,
|
|
struct gpio_callback *cb, uint32_t pins)
|
|
{
|
|
struct cc2520_context *cc2520 =
|
|
CONTAINER_OF(cb, struct cc2520_context, sfd_cb);
|
|
|
|
if (atomic_get(&cc2520->tx) == 1) {
|
|
atomic_set(&cc2520->tx, 0);
|
|
k_sem_give(&cc2520->tx_sync);
|
|
}
|
|
}
|
|
|
|
static inline void fifop_int_handler(const struct device *port,
|
|
struct gpio_callback *cb, uint32_t pins)
|
|
{
|
|
struct cc2520_context *cc2520 =
|
|
CONTAINER_OF(cb, struct cc2520_context, fifop_cb);
|
|
|
|
/* Note: Errata document - 1.2 */
|
|
if (!get_fifop(cc2520->dev) && !get_fifop(cc2520->dev)) {
|
|
return;
|
|
}
|
|
|
|
if (!get_fifo(cc2520->dev)) {
|
|
cc2520->overflow = true;
|
|
}
|
|
|
|
k_sem_give(&cc2520->rx_lock);
|
|
}
|
|
|
|
static void enable_fifop_interrupt(const struct device *dev,
|
|
bool enable)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
gpio_flags_t mode = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
|
|
|
|
gpio_pin_interrupt_configure_dt(&cfg->fifop, mode);
|
|
}
|
|
|
|
static void enable_sfd_interrupt(const struct device *dev,
|
|
bool enable)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
gpio_flags_t mode = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
|
|
|
|
gpio_pin_interrupt_configure_dt(&cfg->sfd, mode);
|
|
}
|
|
|
|
static inline int setup_gpio_callbacks(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
|
|
gpio_init_callback(&cc2520->sfd_cb, sfd_int_handler, BIT(cfg->sfd.pin));
|
|
if (gpio_add_callback(cfg->sfd.port, &cc2520->sfd_cb) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
|
|
gpio_init_callback(&cc2520->fifop_cb, fifop_int_handler, BIT(cfg->fifop.pin));
|
|
if (gpio_add_callback(cfg->fifop.port, &cc2520->fifop_cb) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************
|
|
* TX functions *
|
|
***************/
|
|
static inline bool write_txfifo_length(const struct device *dev, uint8_t len)
|
|
{
|
|
uint8_t length = len + CC2520_FCS_LENGTH;
|
|
|
|
return z_cc2520_access(dev, false, CC2520_INS_TXBUF, 0, &length, 1);
|
|
}
|
|
|
|
static inline bool write_txfifo_content(const struct device *dev,
|
|
uint8_t *frame, uint8_t len)
|
|
{
|
|
return z_cc2520_access(dev, false, CC2520_INS_TXBUF, 0, frame, len);
|
|
}
|
|
|
|
static inline bool verify_txfifo_status(const struct device *dev,
|
|
uint8_t len)
|
|
{
|
|
if (read_reg_txfifocnt(dev) < len ||
|
|
(read_reg_excflag0(dev) & EXCFLAG0_TX_UNDERFLOW)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool verify_tx_done(const struct device *dev)
|
|
{
|
|
uint8_t timeout = 10U;
|
|
uint8_t status;
|
|
|
|
do {
|
|
z_usleep(1);
|
|
timeout--;
|
|
status = read_reg_excflag0(dev);
|
|
} while (!(status & EXCFLAG0_TX_FRM_DONE) && timeout);
|
|
|
|
return !!(status & EXCFLAG0_TX_FRM_DONE);
|
|
}
|
|
|
|
/****************
|
|
* RX functions *
|
|
***************/
|
|
|
|
static inline void flush_rxfifo(const struct device *dev)
|
|
{
|
|
/* Note: Errata document - 1.1 */
|
|
enable_fifop_interrupt(dev, false);
|
|
|
|
instruct_sflushrx(dev);
|
|
instruct_sflushrx(dev);
|
|
|
|
enable_fifop_interrupt(dev, true);
|
|
|
|
write_reg_excflag0(dev, EXCFLAG0_RESET_RX_FLAGS);
|
|
}
|
|
|
|
static inline uint8_t read_rxfifo_length(const struct device *dev)
|
|
{
|
|
uint8_t len;
|
|
|
|
|
|
if (z_cc2520_access(dev, true, CC2520_INS_RXBUF, 0, &len, 1)) {
|
|
return len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline bool read_rxfifo_content(const struct device *dev,
|
|
struct net_buf *buf, uint8_t len)
|
|
{
|
|
if (!z_cc2520_access(dev, true, CC2520_INS_RXBUF, 0, buf->data, len)) {
|
|
return false;
|
|
}
|
|
|
|
if (read_reg_excflag0(dev) & EXCFLAG0_RX_UNDERFLOW) {
|
|
LOG_ERR("RX underflow!");
|
|
return false;
|
|
}
|
|
|
|
net_buf_add(buf, len);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline void insert_radio_noise_details(struct net_pkt *pkt, uint8_t *status)
|
|
{
|
|
uint8_t lqi;
|
|
|
|
net_pkt_set_ieee802154_rssi_dbm(pkt, (int8_t) status[0]);
|
|
|
|
/**
|
|
* CC2520 does not provide an LQI but a correlation factor.
|
|
* See Section 20.6
|
|
* Such calculation can be loosely used to transform it to lqi:
|
|
* corr <= 50 ? lqi = 0
|
|
* or:
|
|
* corr >= 110 ? lqi = 255
|
|
* else:
|
|
* lqi = (lqi - 50) * 4
|
|
*/
|
|
lqi = status[1] & CC2520_FCS_CORRELATION;
|
|
if (lqi <= 50U) {
|
|
lqi = 0U;
|
|
} else if (lqi >= 110U) {
|
|
lqi = 255U;
|
|
} else {
|
|
lqi = (lqi - 50U) << 2;
|
|
}
|
|
|
|
net_pkt_set_ieee802154_lqi(pkt, lqi);
|
|
}
|
|
|
|
static inline bool verify_crc(const struct device *dev, struct net_pkt *pkt)
|
|
{
|
|
uint8_t status[2];
|
|
|
|
if (!z_cc2520_access(dev, true, CC2520_INS_RXBUF, 0, &status, 2)) {
|
|
return false;
|
|
}
|
|
|
|
if (!(status[1] & CC2520_FCS_CRC_OK)) {
|
|
return false;
|
|
}
|
|
|
|
insert_radio_noise_details(pkt, status);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool verify_rxfifo_validity(const struct device *dev,
|
|
uint8_t pkt_len)
|
|
{
|
|
if (pkt_len < 2 || read_reg_rxfifocnt(dev) != pkt_len) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cc2520_rx(void *arg)
|
|
{
|
|
const struct device *dev = arg;
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
struct net_pkt *pkt;
|
|
uint8_t pkt_len;
|
|
|
|
while (1) {
|
|
pkt = NULL;
|
|
|
|
k_sem_take(&cc2520->rx_lock, K_FOREVER);
|
|
|
|
if (cc2520->overflow) {
|
|
LOG_ERR("RX overflow!");
|
|
cc2520->overflow = false;
|
|
|
|
goto flush;
|
|
}
|
|
|
|
pkt_len = read_rxfifo_length(dev) & 0x7f;
|
|
if (!verify_rxfifo_validity(dev, pkt_len)) {
|
|
LOG_ERR("Invalid content");
|
|
goto flush;
|
|
}
|
|
|
|
pkt = net_pkt_rx_alloc_with_buffer(cc2520->iface, pkt_len,
|
|
AF_UNSPEC, 0, K_NO_WAIT);
|
|
if (!pkt) {
|
|
LOG_ERR("No pkt available");
|
|
goto flush;
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_IEEE802154_RAW_MODE)) {
|
|
pkt_len -= 2U;
|
|
}
|
|
|
|
if (!read_rxfifo_content(dev, pkt->buffer, pkt_len)) {
|
|
LOG_ERR("No content read");
|
|
goto flush;
|
|
}
|
|
|
|
if (!verify_crc(dev, pkt)) {
|
|
LOG_ERR("Bad packet CRC");
|
|
goto out;
|
|
}
|
|
|
|
if (ieee802154_handle_ack(cc2520->iface, pkt) == NET_OK) {
|
|
LOG_DBG("ACK packet handled");
|
|
goto out;
|
|
}
|
|
|
|
LOG_DBG("Caught a packet (%u)", pkt_len);
|
|
|
|
if (net_recv_data(cc2520->iface, pkt) < 0) {
|
|
LOG_DBG("Packet dropped by NET stack");
|
|
goto out;
|
|
}
|
|
|
|
log_stack_usage(&cc2520->cc2520_rx_thread);
|
|
continue;
|
|
flush:
|
|
cc2520_print_exceptions(cc2520);
|
|
cc2520_print_errors(cc2520);
|
|
flush_rxfifo(dev);
|
|
out:
|
|
if (pkt) {
|
|
net_pkt_unref(pkt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************
|
|
* Radio device API *
|
|
*******************/
|
|
static enum ieee802154_hw_caps cc2520_get_capabilities(const struct device *dev)
|
|
{
|
|
/* TODO: Add support for IEEE802154_HW_PROMISC */
|
|
return IEEE802154_HW_FCS | IEEE802154_HW_2_4_GHZ | IEEE802154_HW_FILTER |
|
|
IEEE802154_HW_RX_TX_ACK;
|
|
}
|
|
|
|
static int cc2520_cca(const struct device *dev)
|
|
{
|
|
if (!get_cca(dev)) {
|
|
LOG_WRN("Busy");
|
|
return -EBUSY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_set_channel(const struct device *dev, uint16_t channel)
|
|
{
|
|
LOG_DBG("%u", channel);
|
|
|
|
if (channel < 11 || channel > 26) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* See chapter 16 */
|
|
channel = 11 + (channel - 11) * 5U;
|
|
|
|
if (!write_reg_freqctrl(dev, FREQCTRL_FREQ(channel))) {
|
|
LOG_ERR("Failed");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_filter(const struct device *dev,
|
|
bool set,
|
|
enum ieee802154_filter_type type,
|
|
const struct ieee802154_filter *filter)
|
|
{
|
|
LOG_DBG("Applying filter %u", type);
|
|
|
|
if (!set) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) {
|
|
return cc2520_set_ieee_addr(dev, filter->ieee_addr);
|
|
} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
|
|
return cc2520_set_short_addr(dev, filter->short_addr);
|
|
} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
|
|
return cc2520_set_pan_id(dev, filter->pan_id);
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int cc2520_set_txpower(const struct device *dev, int16_t dbm)
|
|
{
|
|
uint8_t pwr;
|
|
|
|
LOG_DBG("%d", dbm);
|
|
|
|
/* See chapter 19 part 8 */
|
|
switch (dbm) {
|
|
case 5:
|
|
pwr = 0xF7;
|
|
break;
|
|
case 3:
|
|
pwr = 0xF2;
|
|
break;
|
|
case 2:
|
|
pwr = 0xAB;
|
|
break;
|
|
case 1:
|
|
pwr = 0x13;
|
|
break;
|
|
case 0:
|
|
pwr = 0x32;
|
|
break;
|
|
case -2:
|
|
pwr = 0x81;
|
|
break;
|
|
case -4:
|
|
pwr = 0x88;
|
|
break;
|
|
case -7:
|
|
pwr = 0x2C;
|
|
break;
|
|
case -18:
|
|
pwr = 0x03;
|
|
break;
|
|
default:
|
|
goto error;
|
|
}
|
|
|
|
if (!write_reg_txpower(dev, pwr)) {
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
LOG_ERR("Failed");
|
|
return -EIO;
|
|
}
|
|
|
|
static int cc2520_tx(const struct device *dev,
|
|
enum ieee802154_tx_mode mode,
|
|
struct net_pkt *pkt,
|
|
struct net_buf *frag)
|
|
{
|
|
uint8_t *frame = frag->data;
|
|
uint8_t len = frag->len;
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
uint8_t retry = 2U;
|
|
bool status;
|
|
|
|
if (mode != IEEE802154_TX_MODE_DIRECT) {
|
|
NET_ERR("TX mode %d not supported", mode);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
LOG_DBG("%p (%u)", frag, len);
|
|
|
|
if (!write_reg_excflag0(dev, EXCFLAG0_RESET_TX_FLAGS) ||
|
|
!write_txfifo_length(dev, len) ||
|
|
!write_txfifo_content(dev, frame, len)) {
|
|
LOG_ERR("Cannot feed in TX fifo");
|
|
goto error;
|
|
}
|
|
|
|
if (!verify_txfifo_status(dev, len)) {
|
|
LOG_ERR("Did not write properly into TX FIFO");
|
|
goto error;
|
|
}
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
k_sem_take(&cc2520->access_lock, K_FOREVER);
|
|
#endif
|
|
|
|
/* 1 retry is allowed here */
|
|
do {
|
|
atomic_set(&cc2520->tx, 1);
|
|
k_sem_init(&cc2520->tx_sync, 0, K_SEM_MAX_LIMIT);
|
|
|
|
if (!instruct_stxoncca(dev)) {
|
|
LOG_ERR("Cannot start transmission");
|
|
goto error;
|
|
}
|
|
|
|
/* TODO: Implement standard conforming CSMA/CA or use the soft MAC's default. */
|
|
k_sem_take(&cc2520->tx_sync, K_MSEC(10));
|
|
|
|
retry--;
|
|
status = verify_tx_done(dev);
|
|
} while (!status && retry);
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
k_sem_give(&cc2520->access_lock);
|
|
#endif
|
|
|
|
if (status) {
|
|
return 0;
|
|
}
|
|
|
|
error:
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
k_sem_give(&cc2520->access_lock);
|
|
#endif
|
|
LOG_ERR("No TX_FRM_DONE");
|
|
cc2520_print_exceptions(cc2520);
|
|
cc2520_print_errors(cc2520);
|
|
|
|
atomic_set(&cc2520->tx, 0);
|
|
instruct_sflushtx(dev);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int cc2520_start(const struct device *dev)
|
|
{
|
|
if (!instruct_sxoscon(dev) ||
|
|
!instruct_srxon(dev) ||
|
|
!verify_osc_stabilization(dev)) {
|
|
LOG_ERR("Error starting CC2520");
|
|
return -EIO;
|
|
}
|
|
|
|
flush_rxfifo(dev);
|
|
|
|
enable_fifop_interrupt(dev, true);
|
|
enable_sfd_interrupt(dev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_stop(const struct device *dev)
|
|
{
|
|
flush_rxfifo(dev);
|
|
|
|
enable_fifop_interrupt(dev, false);
|
|
enable_sfd_interrupt(dev, false);
|
|
|
|
if (!instruct_srfoff(dev) ||
|
|
!instruct_sxoscoff(dev)) {
|
|
LOG_ERR("Error stopping CC2520");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************
|
|
* Initialization *
|
|
*****************/
|
|
static int power_on_and_setup(const struct device *dev)
|
|
{
|
|
/* Switching to LPM2 mode */
|
|
set_reset(dev, 0);
|
|
z_usleep(150);
|
|
|
|
set_vreg_en(dev, 0);
|
|
z_usleep(250);
|
|
|
|
/* Then to ACTIVE mode */
|
|
set_vreg_en(dev, 1);
|
|
z_usleep(250);
|
|
|
|
set_reset(dev, 1);
|
|
z_usleep(150);
|
|
|
|
if (!verify_osc_stabilization(dev)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* Default settings to always write (see chapter 28 part 1) */
|
|
if (!write_reg_txpower(dev, CC2520_TXPOWER_DEFAULT) ||
|
|
!write_reg_ccactrl0(dev, CC2520_CCACTRL0_DEFAULT) ||
|
|
!write_reg_mdmctrl0(dev, CC2520_MDMCTRL0_DEFAULT) ||
|
|
!write_reg_mdmctrl1(dev, CC2520_MDMCTRL1_DEFAULT) ||
|
|
!write_reg_rxctrl(dev, CC2520_RXCTRL_DEFAULT) ||
|
|
!write_reg_fsctrl(dev, CC2520_FSCTRL_DEFAULT) ||
|
|
!write_reg_fscal1(dev, CC2520_FSCAL1_DEFAULT) ||
|
|
!write_reg_agcctrl1(dev, CC2520_AGCCTRL1_DEFAULT) ||
|
|
!write_reg_adctest0(dev, CC2520_ADCTEST0_DEFAULT) ||
|
|
!write_reg_adctest1(dev, CC2520_ADCTEST1_DEFAULT) ||
|
|
!write_reg_adctest2(dev, CC2520_ADCTEST2_DEFAULT)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* EXTCLOCK0: Disabling external clock
|
|
* FRMCTRL0: AUTOACK and AUTOCRC enabled
|
|
* FRMCTRL1: SET_RXENMASK_ON_TX and IGNORE_TX_UNDERF
|
|
* FRMFILT0: Frame filtering (setting CC2520_FRAME_FILTERING)
|
|
* FIFOPCTRL: Set TX threshold (setting CC2520_TX_THRESHOLD)
|
|
*/
|
|
if (!write_reg_extclock(dev, 0) ||
|
|
!write_reg_frmctrl0(dev, CC2520_AUTOMATISM) ||
|
|
!write_reg_frmctrl1(dev, FRMCTRL1_IGNORE_TX_UNDERF |
|
|
FRMCTRL1_SET_RXENMASK_ON_TX) ||
|
|
!write_reg_frmfilt0(dev, FRMFILT0_FRAME_FILTER_EN |
|
|
FRMFILT0_MAX_FRAME_VERSION(3)) ||
|
|
!write_reg_frmfilt1(dev, FRMFILT1_ACCEPT_ALL) ||
|
|
!write_reg_srcmatch(dev, SRCMATCH_DEFAULTS) ||
|
|
!write_reg_fifopctrl(dev,
|
|
FIFOPCTRL_FIFOP_THR(CC2520_TX_THRESHOLD))) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* Cleaning up TX fifo */
|
|
instruct_sflushtx(dev);
|
|
|
|
if (setup_gpio_callbacks(dev) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
cc2520_print_gpio_config(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int configure_gpios(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
|
|
if (!device_is_ready(cfg->vreg_en.port) ||
|
|
!device_is_ready(cfg->reset.port) ||
|
|
!device_is_ready(cfg->fifo.port) ||
|
|
!device_is_ready(cfg->cca.port) ||
|
|
!device_is_ready(cfg->sfd.port) ||
|
|
!device_is_ready(cfg->fifop.port)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
gpio_pin_configure_dt(&cfg->vreg_en, GPIO_OUTPUT_LOW);
|
|
gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_LOW);
|
|
gpio_pin_configure_dt(&cfg->fifo, GPIO_INPUT);
|
|
gpio_pin_configure_dt(&cfg->cca, GPIO_INPUT);
|
|
gpio_pin_configure_dt(&cfg->sfd, GPIO_INPUT);
|
|
gpio_pin_configure_dt(&cfg->fifop, GPIO_INPUT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_init(const struct device *dev)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
|
|
cc2520->dev = dev;
|
|
|
|
atomic_set(&cc2520->tx, 0);
|
|
k_sem_init(&cc2520->rx_lock, 0, K_SEM_MAX_LIMIT);
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
k_sem_init(&cc2520->access_lock, 1, 1);
|
|
#endif
|
|
|
|
if (configure_gpios(dev) != 0) {
|
|
LOG_ERR("Configuring GPIOS failed");
|
|
return -EIO;
|
|
}
|
|
|
|
if (!spi_is_ready_dt(&cfg->bus)) {
|
|
LOG_ERR("SPI bus %s not ready", cfg->bus.bus->name);
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("GPIO and SPI configured");
|
|
|
|
if (power_on_and_setup(dev) != 0) {
|
|
LOG_ERR("Configuring CC2520 failed");
|
|
return -EIO;
|
|
}
|
|
|
|
k_thread_create(&cc2520->cc2520_rx_thread, cc2520->cc2520_rx_stack,
|
|
CONFIG_IEEE802154_CC2520_RX_STACK_SIZE,
|
|
(k_thread_entry_t)cc2520_rx,
|
|
(void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT);
|
|
k_thread_name_set(&cc2520->cc2520_rx_thread, "cc2520_rx");
|
|
|
|
LOG_INF("CC2520 initialized");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cc2520_iface_init(struct net_if *iface)
|
|
{
|
|
const struct device *dev = net_if_get_device(iface);
|
|
struct cc2520_context *cc2520 = dev->data;
|
|
uint8_t *mac = get_mac(dev);
|
|
|
|
net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);
|
|
|
|
cc2520->iface = iface;
|
|
|
|
ieee802154_init(iface);
|
|
}
|
|
|
|
static const struct cc2520_config cc2520_config = {
|
|
.bus = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8), 0),
|
|
.vreg_en = GPIO_DT_SPEC_INST_GET(0, vreg_en_gpios),
|
|
.reset = GPIO_DT_SPEC_INST_GET(0, reset_gpios),
|
|
.fifo = GPIO_DT_SPEC_INST_GET(0, fifo_gpios),
|
|
.cca = GPIO_DT_SPEC_INST_GET(0, cca_gpios),
|
|
.sfd = GPIO_DT_SPEC_INST_GET(0, sfd_gpios),
|
|
.fifop = GPIO_DT_SPEC_INST_GET(0, fifop_gpios)
|
|
};
|
|
|
|
static struct cc2520_context cc2520_context_data;
|
|
|
|
static struct ieee802154_radio_api cc2520_radio_api = {
|
|
.iface_api.init = cc2520_iface_init,
|
|
|
|
.get_capabilities = cc2520_get_capabilities,
|
|
.cca = cc2520_cca,
|
|
.set_channel = cc2520_set_channel,
|
|
.filter = cc2520_filter,
|
|
.set_txpower = cc2520_set_txpower,
|
|
.start = cc2520_start,
|
|
.stop = cc2520_stop,
|
|
.tx = cc2520_tx,
|
|
};
|
|
|
|
#if defined(CONFIG_IEEE802154_RAW_MODE)
|
|
DEVICE_DT_INST_DEFINE(0, cc2520_init, NULL, &cc2520_context_data, NULL,
|
|
POST_KERNEL, CONFIG_IEEE802154_CC2520_INIT_PRIO,
|
|
&cc2520_radio_api);
|
|
#else
|
|
NET_DEVICE_DT_INST_DEFINE(0, cc2520_init, NULL, &cc2520_context_data,
|
|
&cc2520_config, CONFIG_IEEE802154_CC2520_INIT_PRIO,
|
|
&cc2520_radio_api, IEEE802154_L2,
|
|
NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
static inline bool cc2520_read_ram(const struct device *dev, uint16_t addr,
|
|
uint8_t *data_buf, uint8_t len)
|
|
{
|
|
return z_cc2520_access(dev, true, CC2520_INS_MEMRD,
|
|
addr, data_buf, len);
|
|
}
|
|
|
|
static inline bool cc2520_write_ram(const struct device *dev, uint16_t addr,
|
|
uint8_t *data_buf, uint8_t len)
|
|
{
|
|
return z_cc2520_access(dev, false, CC2520_INS_MEMWR,
|
|
addr, data_buf, len);
|
|
}
|
|
|
|
static inline bool instruct_uccm_ccm(const struct device *dev,
|
|
bool uccm,
|
|
uint8_t key_addr,
|
|
uint8_t auth_crypt,
|
|
uint8_t nonce_addr,
|
|
uint16_t input_addr,
|
|
uint16_t output_addr,
|
|
uint8_t in_len,
|
|
uint8_t m)
|
|
{
|
|
const struct cc2520_config *cfg = dev->config;
|
|
struct cc2520_context *ctx = dev->data;
|
|
uint8_t cmd[9];
|
|
const struct spi_buf buf[1] = {
|
|
{
|
|
.buf = cmd,
|
|
.len = 9,
|
|
},
|
|
};
|
|
const struct spi_buf_set tx = {
|
|
.buffers = buf,
|
|
.count = 1
|
|
};
|
|
|
|
int ret;
|
|
|
|
LOG_DBG("%sCCM(P={01} K={%02x} C={%02x} N={%02x}"
|
|
" A={%03x} E={%03x} F{%02x} M={%02x})",
|
|
uccm ? "U" : "", key_addr, auth_crypt, nonce_addr,
|
|
input_addr, output_addr, in_len, m);
|
|
|
|
cmd[0] = uccm ? CC2520_INS_UCCM | 1 : CC2520_INS_CCM | 1;
|
|
cmd[1] = key_addr;
|
|
cmd[2] = (auth_crypt & 0x7f);
|
|
cmd[3] = nonce_addr;
|
|
cmd[4] = (uint8_t)(((input_addr & 0x0f00) >> 4) |
|
|
((output_addr & 0x0f00) >> 8));
|
|
cmd[5] = (uint8_t)(input_addr & 0x00ff);
|
|
cmd[6] = (uint8_t)(output_addr & 0x00ff);
|
|
cmd[7] = (in_len & 0x7f);
|
|
cmd[8] = (m & 0x03);
|
|
|
|
k_sem_take(&ctx->access_lock, K_FOREVER);
|
|
|
|
ret = spi_write_dt(&cfg->bus, &tx);
|
|
|
|
k_sem_give(&ctx->access_lock);
|
|
|
|
if (ret) {
|
|
LOG_ERR("%sCCM Failed", uccm ? "U" : "");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline void generate_nonce(uint8_t *ccm_nonce, uint8_t *nonce,
|
|
struct cipher_aead_pkt *apkt, uint8_t m)
|
|
{
|
|
nonce[0] = 0 | (apkt->ad_len ? 0x40 : 0) | (m << 3) | 1;
|
|
|
|
memcpy(&nonce[1], ccm_nonce, 13);
|
|
|
|
nonce[14] = (uint8_t)(apkt->pkt->in_len >> 8);
|
|
nonce[15] = (uint8_t)(apkt->pkt->in_len);
|
|
|
|
/* See section 26.8.1 */
|
|
sys_mem_swap(nonce, 16);
|
|
}
|
|
|
|
static int insert_crypto_parameters(struct cipher_ctx *ctx,
|
|
struct cipher_aead_pkt *apkt,
|
|
uint8_t *ccm_nonce, uint8_t *auth_crypt)
|
|
{
|
|
const struct device *cc2520 = ctx->device;
|
|
uint8_t data[128];
|
|
uint8_t *in_buf;
|
|
uint8_t in_len;
|
|
uint8_t m = 0U;
|
|
|
|
if (!apkt->pkt->out_buf || !apkt->pkt->out_buf_max) {
|
|
LOG_ERR("Out buffer needs to be set");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ctx->key.bit_stream || !ctx->keylen) {
|
|
LOG_ERR("No key installed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!(ctx->flags & CAP_INPLACE_OPS)) {
|
|
LOG_ERR("It supports only in-place operation");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!apkt->ad || !apkt->ad_len) {
|
|
LOG_ERR("CCM needs associated data");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (apkt->pkt->in_buf && apkt->pkt->in_buf - apkt->ad_len != apkt->ad) {
|
|
LOG_ERR("In-place needs ad and input in same memory");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!apkt->pkt->in_buf) {
|
|
if (!ctx->mode_params.ccm_info.tag_len) {
|
|
LOG_ERR("Auth only needs a tag length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
in_buf = apkt->ad;
|
|
in_len = apkt->ad_len;
|
|
|
|
*auth_crypt = 0U;
|
|
} else {
|
|
in_buf = data;
|
|
|
|
memcpy(in_buf, apkt->ad, apkt->ad_len);
|
|
memcpy(in_buf + apkt->ad_len,
|
|
apkt->pkt->in_buf, apkt->pkt->in_len);
|
|
in_len = apkt->ad_len + apkt->pkt->in_len;
|
|
|
|
*auth_crypt = !apkt->tag ? apkt->pkt->in_len :
|
|
apkt->pkt->in_len - ctx->mode_params.ccm_info.tag_len;
|
|
}
|
|
|
|
if (ctx->mode_params.ccm_info.tag_len) {
|
|
if ((ctx->mode_params.ccm_info.tag_len >> 2) > 3) {
|
|
m = 3U;
|
|
} else {
|
|
m = ctx->mode_params.ccm_info.tag_len >> 2;
|
|
}
|
|
}
|
|
|
|
/* Writing the frame in RAM */
|
|
if (!cc2520_write_ram(cc2520, CC2520_MEM_DATA, in_buf, in_len)) {
|
|
LOG_ERR("Cannot write the frame in RAM");
|
|
return -EIO;
|
|
}
|
|
|
|
/* See section 26.8.1 */
|
|
sys_memcpy_swap(data, ctx->key.bit_stream, ctx->keylen);
|
|
|
|
/* Writing the key in RAM */
|
|
if (!cc2520_write_ram(cc2520, CC2520_MEM_KEY, data, 16)) {
|
|
LOG_ERR("Cannot write the key in RAM");
|
|
return -EIO;
|
|
}
|
|
|
|
generate_nonce(ccm_nonce, data, apkt, m);
|
|
|
|
/* Writing the nonce in RAM */
|
|
if (!cc2520_write_ram(cc2520, CC2520_MEM_NONCE, data, 16)) {
|
|
LOG_ERR("Cannot write the nonce in RAM");
|
|
return -EIO;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
static int cc2520_crypto_ccm(struct cipher_ctx *ctx,
|
|
struct cipher_aead_pkt *apkt,
|
|
uint8_t *ccm_nonce)
|
|
{
|
|
const struct device *cc2520 = ctx->device;
|
|
uint8_t auth_crypt;
|
|
int m;
|
|
|
|
if (!apkt || !apkt->pkt) {
|
|
LOG_ERR("Invalid crypto packet to operate with");
|
|
return -EINVAL;
|
|
}
|
|
|
|
m = insert_crypto_parameters(ctx, apkt, ccm_nonce, &auth_crypt);
|
|
if (m < 0) {
|
|
LOG_ERR("Inserting crypto parameters failed");
|
|
return m;
|
|
}
|
|
|
|
apkt->pkt->out_len = apkt->pkt->in_len + apkt->ad_len +
|
|
(m ? ctx->mode_params.ccm_info.tag_len : 0);
|
|
|
|
if (apkt->pkt->out_len > apkt->pkt->out_buf_max) {
|
|
LOG_ERR("Result will not fit into out buffer %u vs %u",
|
|
apkt->pkt->out_len, apkt->pkt->out_buf_max);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (!instruct_uccm_ccm(cc2520, false, CC2520_MEM_KEY >> 4, auth_crypt,
|
|
CC2520_MEM_NONCE >> 4, CC2520_MEM_DATA,
|
|
0x000, apkt->ad_len, m) ||
|
|
!cc2520_read_ram(cc2520, CC2520_MEM_DATA,
|
|
apkt->pkt->out_buf, apkt->pkt->out_len)) {
|
|
LOG_ERR("CCM or reading result from RAM failed");
|
|
return -EIO;
|
|
}
|
|
|
|
if (apkt->tag) {
|
|
memcpy(apkt->tag, apkt->pkt->out_buf + apkt->pkt->in_len,
|
|
ctx->mode_params.ccm_info.tag_len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_crypto_uccm(struct cipher_ctx *ctx,
|
|
struct cipher_aead_pkt *apkt,
|
|
uint8_t *ccm_nonce)
|
|
{
|
|
const struct device *cc2520 = ctx->device;
|
|
uint8_t auth_crypt;
|
|
int m;
|
|
|
|
if (!apkt || !apkt->pkt) {
|
|
LOG_ERR("Invalid crypto packet to operate with");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ctx->mode_params.ccm_info.tag_len && !apkt->tag) {
|
|
LOG_ERR("In case of MIC you need to provide a tag");
|
|
return -EINVAL;
|
|
}
|
|
|
|
m = insert_crypto_parameters(ctx, apkt, ccm_nonce, &auth_crypt);
|
|
if (m < 0) {
|
|
return m;
|
|
}
|
|
|
|
apkt->pkt->out_len = apkt->pkt->in_len + apkt->ad_len;
|
|
|
|
if (!instruct_uccm_ccm(cc2520, true, CC2520_MEM_KEY >> 4, auth_crypt,
|
|
CC2520_MEM_NONCE >> 4, CC2520_MEM_DATA,
|
|
0x000, apkt->ad_len, m) ||
|
|
!cc2520_read_ram(cc2520, CC2520_MEM_DATA,
|
|
apkt->pkt->out_buf, apkt->pkt->out_len)) {
|
|
LOG_ERR("UCCM or reading result from RAM failed");
|
|
return -EIO;
|
|
}
|
|
|
|
if (m && (!(read_reg_dpustat(cc2520) & DPUSTAT_AUTHSTAT_H))) {
|
|
LOG_ERR("Authentication of the frame failed");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_crypto_hw_caps(const struct device *dev)
|
|
{
|
|
return CAP_RAW_KEY | CAP_INPLACE_OPS | CAP_SYNC_OPS;
|
|
}
|
|
|
|
static int cc2520_crypto_begin_session(const struct device *dev,
|
|
struct cipher_ctx *ctx,
|
|
enum cipher_algo algo,
|
|
enum cipher_mode mode,
|
|
enum cipher_op op_type)
|
|
{
|
|
if (algo != CRYPTO_CIPHER_ALGO_AES || mode != CRYPTO_CIPHER_MODE_CCM) {
|
|
LOG_ERR("Wrong algo (%u) or mode (%u)", algo, mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ctx->mode_params.ccm_info.nonce_len != 13U) {
|
|
LOG_ERR("Nonce length erroneous (%u)",
|
|
ctx->mode_params.ccm_info.nonce_len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (op_type == CRYPTO_CIPHER_OP_ENCRYPT) {
|
|
ctx->ops.ccm_crypt_hndlr = cc2520_crypto_ccm;
|
|
} else {
|
|
ctx->ops.ccm_crypt_hndlr = cc2520_crypto_uccm;
|
|
}
|
|
|
|
ctx->ops.cipher_mode = mode;
|
|
ctx->device = dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_crypto_free_session(const struct device *dev,
|
|
struct cipher_ctx *ctx)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
ctx->ops.ccm_crypt_hndlr = NULL;
|
|
ctx->device = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cc2520_crypto_init(const struct device *dev)
|
|
{
|
|
LOG_INF("CC2520 crypto part initialized");
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct crypto_driver_api cc2520_crypto_api = {
|
|
.query_hw_caps = cc2520_crypto_hw_caps,
|
|
.cipher_begin_session = cc2520_crypto_begin_session,
|
|
.cipher_free_session = cc2520_crypto_free_session,
|
|
.cipher_async_callback_set = NULL
|
|
};
|
|
|
|
DEVICE_DEFINE(cc2520_crypto, "cc2520_crypto",
|
|
cc2520_crypto_init, NULL,
|
|
&cc2520_context_data, NULL, POST_KERNEL,
|
|
CONFIG_IEEE802154_CC2520_CRYPTO_INIT_PRIO, &cc2520_crypto_api);
|
|
|
|
#endif /* CONFIG_IEEE802154_CC2520_CRYPTO */
|