2016-11-11 22:46:28 +01:00
|
|
|
/* ieee802154_cc2520.c - TI CC2520 driver */
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-03-18 13:08:40 +01:00
|
|
|
*/
|
|
|
|
|
2017-01-10 14:24:01 +01:00
|
|
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_IEEE802154_DRIVER_LEVEL
|
2016-10-21 09:51:33 +02:00
|
|
|
#define SYS_LOG_DOMAIN "dev/cc2520"
|
2016-12-17 18:56:56 +01:00
|
|
|
#include <logging/sys_log.h>
|
2016-08-10 11:05:51 +02:00
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
#include <kernel.h>
|
2016-03-18 13:08:40 +01:00
|
|
|
#include <arch/cpu.h>
|
|
|
|
|
|
|
|
#include <board.h>
|
|
|
|
#include <device.h>
|
|
|
|
#include <init.h>
|
2016-06-15 10:46:10 +02:00
|
|
|
#include <net/net_if.h>
|
|
|
|
#include <net/nbuf.h>
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
#include <misc/byteorder.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <rand32.h>
|
|
|
|
|
|
|
|
#include <gpio.h>
|
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
|
|
|
|
#include <crypto/cipher.h>
|
|
|
|
#include <crypto/cipher_structs.h>
|
|
|
|
|
|
|
|
#endif /* CONFIG_IEEE802154_CC2520_CRYPTO */
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
#include <net/ieee802154_radio.h>
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
#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 *
|
|
|
|
********/
|
2017-01-10 14:24:01 +01:00
|
|
|
#if CONFIG_SYS_LOG_IEEE802154_DRIVER_LEVEL == 4
|
2016-03-18 13:08:40 +01:00
|
|
|
static inline void _cc2520_print_gpio_config(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("GPIOCTRL0/1/2/3/4/5 = 0x%x/0x%x/0x%x/0x%x/0x%x/0x%x",
|
|
|
|
read_reg_gpioctrl0(&cc2520->spi),
|
|
|
|
read_reg_gpioctrl1(&cc2520->spi),
|
|
|
|
read_reg_gpioctrl2(&cc2520->spi),
|
|
|
|
read_reg_gpioctrl3(&cc2520->spi),
|
|
|
|
read_reg_gpioctrl4(&cc2520->spi),
|
|
|
|
read_reg_gpioctrl5(&cc2520->spi));
|
|
|
|
SYS_LOG_DBG("GPIOPOLARITY: 0x%x",
|
|
|
|
read_reg_gpiopolarity(&cc2520->spi));
|
|
|
|
SYS_LOG_DBG("GPIOCTRL: 0x%x",
|
|
|
|
read_reg_gpioctrl(&cc2520->spi));
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void _cc2520_print_exceptions(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint8_t flag = read_reg_excflag0(&cc2520->spi);
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("EXCFLAG0:");
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_RF_IDLE) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RF_IDLE ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_TX_FRM_DONE) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("TX_FRM_DONE ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_TX_ACK_DONE) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("TX_ACK_DONE ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_TX_UNDERFLOW) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("TX_UNDERFLOW ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_TX_OVERFLOW) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("TX_OVERFLOW ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_RX_UNDERFLOW) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RX_UNDERFLOW ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_RX_OVERFLOW) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RX_OVERFLOW ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG0_RXENABLE_ZERO) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RXENABLE_ZERO");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
|
|
|
SYS_LOG_BACKEND_FN("\n");
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
flag = read_reg_excflag1(&cc2520->spi);
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("EXCFLAG1:");
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_RX_FRM_DONE) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RX_FRM_DONE ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_RX_FRM_ACCEPTED) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RX_FRM_ACCEPTED ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_SRC_MATCH_DONE) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("SRC_MATCH_DONE ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_SRC_MATCH_FOUND) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("SRC_MATCH_FOUND ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_FIFOP) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("FIFOP ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_SFD) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("SFD ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_DPU_DONE_L) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("DPU_DONE_L ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG1_DPU_DONE_H) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("DPU_DONE_H");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
|
|
|
SYS_LOG_BACKEND_FN("\n");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void _cc2520_print_errors(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint8_t flag = read_reg_excflag2(&cc2520->spi);
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("EXCFLAG2:");
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_MEMADDR_ERROR) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("MEMADDR_ERROR ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_USAGE_ERROR) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("USAGE_ERROR ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_OPERAND_ERROR) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("OPERAND_ERROR ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_SPI_ERROR) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("SPI_ERROR ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_RF_NO_LOCK) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RF_NO_LOCK ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_RX_FRM_ABORTED) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RX_FRM_ABORTED ");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
if (flag & EXCFLAG2_RFBUFMOV_TIMEOUT) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_BACKEND_FN("RFBUFMOV_TIMEOUT");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
|
|
|
|
SYS_LOG_BACKEND_FN("\n");
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-12-02 10:49:34 +01:00
|
|
|
#else
|
|
|
|
#define _cc2520_print_gpio_config(...)
|
|
|
|
#define _cc2520_print_exceptions(...)
|
|
|
|
#define _cc2520_print_errors(...)
|
2017-01-10 14:24:01 +01:00
|
|
|
#endif /* CONFIG_SYS_LOG_IEEE802154_DRIVER_LEVEL == 4 */
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* Generic functions *
|
|
|
|
********************/
|
2016-11-09 16:53:16 +01:00
|
|
|
#define _usleep(usec) k_busy_wait(usec)
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
uint8_t _cc2520_read_reg(struct cc2520_spi *spi,
|
|
|
|
bool freg, uint8_t addr)
|
|
|
|
{
|
2016-06-20 09:23:57 +02:00
|
|
|
uint8_t len = freg ? 2 : 3;
|
|
|
|
|
|
|
|
spi->cmd_buf[0] = freg ? CC2520_INS_REGRD | addr : CC2520_INS_MEMRD;
|
|
|
|
spi->cmd_buf[1] = freg ? 0 : addr;
|
2016-03-18 13:08:40 +01:00
|
|
|
spi->cmd_buf[2] = 0;
|
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
2016-06-20 09:23:57 +02:00
|
|
|
if (spi_transceive(spi->dev, spi->cmd_buf, len,
|
|
|
|
spi->cmd_buf, len) == 0) {
|
|
|
|
return spi->cmd_buf[len - 1];
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _cc2520_write_reg(struct cc2520_spi *spi, bool freg,
|
|
|
|
uint8_t addr, uint8_t value)
|
|
|
|
{
|
2016-06-20 09:23:57 +02:00
|
|
|
uint8_t len = freg ? 2 : 3;
|
|
|
|
|
|
|
|
spi->cmd_buf[0] = freg ? CC2520_INS_REGWR | addr : CC2520_INS_MEMWR;
|
|
|
|
spi->cmd_buf[1] = freg ? value : addr;
|
|
|
|
spi->cmd_buf[2] = freg ? 0 : value;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
2016-06-20 09:23:57 +02:00
|
|
|
return (spi_write(spi->dev, spi->cmd_buf, len) == 0);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool _cc2520_write_ram(struct cc2520_spi *spi, uint16_t addr,
|
|
|
|
uint8_t *data_buf, uint8_t len)
|
|
|
|
{
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifndef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
uint8_t *cmd_data = spi->cmd_buf;
|
|
|
|
#else
|
|
|
|
uint8_t cmd_data[128];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
cmd_data[0] = CC2520_INS_MEMWR | (addr >> 8);
|
|
|
|
cmd_data[1] = addr;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
memcpy(&cmd_data[2], data_buf, len);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
return (spi_write(spi->dev, cmd_data, len + 2) == 0);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t _cc2520_status(struct cc2520_spi *spi)
|
|
|
|
{
|
|
|
|
spi->cmd_buf[0] = CC2520_INS_SNOP;
|
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
|
|
|
if (spi_transceive(spi->dev, spi->cmd_buf, 1,
|
2016-04-26 16:43:02 +02:00
|
|
|
spi->cmd_buf, 1) == 0) {
|
2016-03-18 13:08:40 +01:00
|
|
|
return spi->cmd_buf[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool verify_osc_stabilization(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint8_t timeout = 100;
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
do {
|
|
|
|
status = _cc2520_status(&cc2520->spi);
|
|
|
|
_usleep(1);
|
|
|
|
timeout--;
|
|
|
|
} while (!(status & CC2520_STATUS_XOSC_STABLE_N_RUNNING) && timeout);
|
|
|
|
|
|
|
|
return !!(status & CC2520_STATUS_XOSC_STABLE_N_RUNNING);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static inline uint8_t *get_mac(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
2017-03-16 13:44:12 +01:00
|
|
|
|
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RANDOM_MAC)
|
2016-10-31 13:32:02 +01:00
|
|
|
uint32_t *ptr = (uint32_t *)(cc2520->mac_addr + 4);
|
2016-06-15 10:46:10 +02:00
|
|
|
|
2017-03-16 13:44:12 +01:00
|
|
|
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
|
|
|
|
|
2016-10-31 13:32:02 +01:00
|
|
|
cc2520->mac_addr[0] = 0x00;
|
|
|
|
cc2520->mac_addr[1] = 0x12;
|
|
|
|
cc2520->mac_addr[2] = 0x4b;
|
|
|
|
cc2520->mac_addr[3] = 0x00;
|
2016-06-15 10:46:10 +02:00
|
|
|
|
|
|
|
return cc2520->mac_addr;
|
|
|
|
}
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
/******************
|
|
|
|
* GPIO functions *
|
|
|
|
*****************/
|
|
|
|
static inline void set_reset(struct device *dev, uint32_t value)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_write(cc2520->gpios[CC2520_GPIO_IDX_RESET].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_RESET].pin, value);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void set_vreg_en(struct device *dev, uint32_t value)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_write(cc2520->gpios[CC2520_GPIO_IDX_VREG_EN].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_VREG_EN].pin, value);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t get_fifo(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint32_t pin_value;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_read(cc2520->gpios[CC2520_GPIO_IDX_FIFO].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFO].pin, &pin_value);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
return pin_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t get_fifop(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint32_t pin_value;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_read(cc2520->gpios[CC2520_GPIO_IDX_FIFOP].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFOP].pin, &pin_value);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
return pin_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t get_cca(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint32_t pin_value;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_read(cc2520->gpios[CC2520_GPIO_IDX_CCA].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_CCA].pin, &pin_value);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
return pin_value;
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:28:05 +02:00
|
|
|
static inline void sfd_int_handler(struct device *port,
|
|
|
|
struct gpio_callback *cb, uint32_t pins)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-04-12 11:28:05 +02:00
|
|
|
struct cc2520_context *cc2520 =
|
|
|
|
CONTAINER_OF(cb, struct cc2520_context, sfd_cb);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
if (atomic_get(&cc2520->tx) == 1) {
|
|
|
|
atomic_set(&cc2520->tx, 0);
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_give(&cc2520->tx_sync);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-12 11:28:05 +02:00
|
|
|
static inline void fifop_int_handler(struct device *port,
|
|
|
|
struct gpio_callback *cb, uint32_t pins)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-04-12 11:28:05 +02:00
|
|
|
struct cc2520_context *cc2520 =
|
|
|
|
CONTAINER_OF(cb, struct cc2520_context, fifop_cb);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
/* Note: Errata document - 1.2 */
|
|
|
|
if (!get_fifop(cc2520) && !get_fifop(cc2520)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!get_fifo(cc2520)) {
|
|
|
|
cc2520->overflow = true;
|
|
|
|
}
|
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_give(&cc2520->rx_lock);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void enable_fifop_interrupt(struct cc2520_context *cc2520,
|
|
|
|
bool enable)
|
|
|
|
{
|
|
|
|
if (enable) {
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_enable_callback(
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFOP].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFOP].pin);
|
2016-03-18 13:08:40 +01:00
|
|
|
} else {
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_disable_callback(
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFOP].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_FIFOP].pin);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void enable_sfd_interrupt(struct cc2520_context *cc2520,
|
|
|
|
bool enable)
|
|
|
|
{
|
|
|
|
if (enable) {
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_enable_callback(
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_SFD].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_SFD].pin);
|
2016-03-18 13:08:40 +01:00
|
|
|
} else {
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_pin_disable_callback(
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_SFD].dev,
|
|
|
|
cc2520->gpios[CC2520_GPIO_IDX_SFD].pin);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void setup_gpio_callbacks(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_init_callback(&cc2520->sfd_cb, sfd_int_handler,
|
|
|
|
BIT(cc2520->gpios[CC2520_GPIO_IDX_SFD].pin));
|
|
|
|
gpio_add_callback(cc2520->gpios[CC2520_GPIO_IDX_SFD].dev,
|
2016-04-12 11:28:05 +02:00
|
|
|
&cc2520->sfd_cb);
|
|
|
|
|
2016-11-28 17:06:15 +01:00
|
|
|
gpio_init_callback(&cc2520->fifop_cb, fifop_int_handler,
|
|
|
|
BIT(cc2520->gpios[CC2520_GPIO_IDX_FIFOP].pin));
|
|
|
|
gpio_add_callback(cc2520->gpios[CC2520_GPIO_IDX_FIFOP].dev,
|
2016-04-12 11:28:05 +02:00
|
|
|
&cc2520->fifop_cb);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************
|
|
|
|
* TX functions *
|
|
|
|
***************/
|
|
|
|
static inline bool write_txfifo_length(struct cc2520_spi *spi,
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
uint8_t len)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
spi->cmd_buf[0] = CC2520_INS_TXBUF;
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
spi->cmd_buf[1] = len + CC2520_FCS_LENGTH;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return (spi_write(spi->dev, spi->cmd_buf, 2) == 0);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool write_txfifo_content(struct cc2520_spi *spi,
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
uint8_t *frame, uint8_t len)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-11-08 18:18:44 +01:00
|
|
|
uint8_t cmd[128];
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
cmd[0] = CC2520_INS_TXBUF;
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
memcpy(&cmd[1], frame, len);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
return (spi_write(spi->dev, cmd, len + 1) == 0);
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool verify_txfifo_status(struct cc2520_context *cc2520,
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
uint8_t len)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
if (read_reg_txfifocnt(&cc2520->spi) < len ||
|
2016-03-18 13:08:40 +01:00
|
|
|
(read_reg_excflag0(&cc2520->spi) & EXCFLAG0_TX_UNDERFLOW)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool verify_tx_done(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
uint8_t timeout = 10;
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
do {
|
|
|
|
_usleep(1);
|
|
|
|
timeout--;
|
|
|
|
status = read_reg_excflag0(&cc2520->spi);
|
|
|
|
} while (!(status & EXCFLAG0_TX_FRM_DONE) && timeout);
|
|
|
|
|
|
|
|
return !!(status & EXCFLAG0_TX_FRM_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
|
|
* RX functions *
|
|
|
|
***************/
|
2016-05-04 11:16:17 +02:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
static inline void flush_rxfifo(struct cc2520_context *cc2520)
|
|
|
|
{
|
|
|
|
/* Note: Errata document - 1.1 */
|
|
|
|
enable_fifop_interrupt(cc2520, false);
|
|
|
|
|
|
|
|
instruct_sflushrx(&cc2520->spi);
|
|
|
|
instruct_sflushrx(&cc2520->spi);
|
|
|
|
|
|
|
|
enable_fifop_interrupt(cc2520, true);
|
|
|
|
|
|
|
|
write_reg_excflag0(&cc2520->spi, EXCFLAG0_RESET_RX_FLAGS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t read_rxfifo_length(struct cc2520_spi *spi)
|
|
|
|
{
|
|
|
|
spi->cmd_buf[0] = CC2520_INS_RXBUF;
|
2016-05-04 11:16:17 +02:00
|
|
|
spi->cmd_buf[1] = 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
2016-05-04 11:16:17 +02:00
|
|
|
if (spi_transceive(spi->dev, spi->cmd_buf, 2,
|
2016-04-26 16:43:02 +02:00
|
|
|
spi->cmd_buf, 2) == 0) {
|
2016-03-18 13:08:40 +01:00
|
|
|
return spi->cmd_buf[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-04 11:16:17 +02:00
|
|
|
static inline bool read_rxfifo_content(struct cc2520_spi *spi,
|
|
|
|
struct net_buf *buf, uint8_t len)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-11-16 15:44:33 +01:00
|
|
|
uint8_t data[128+1];
|
2016-05-04 11:16:17 +02:00
|
|
|
|
|
|
|
data[0] = CC2520_INS_RXBUF;
|
|
|
|
memset(&data[1], 0, len);
|
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
|
|
|
if (spi_transceive(spi->dev, data, len+1, data, len+1) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read_reg_excflag0(spi) & EXCFLAG0_RX_UNDERFLOW) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("RX underflow!");
|
2016-05-04 11:16:17 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
memcpy(buf->data, &data[1], len);
|
|
|
|
net_buf_add(buf, len);
|
2016-05-04 11:16:17 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-12 15:33:05 +02:00
|
|
|
static inline bool verify_crc(struct cc2520_context *cc2520,
|
|
|
|
struct net_buf *buf)
|
2016-11-15 15:30:23 +01:00
|
|
|
{
|
|
|
|
cc2520->spi.cmd_buf[0] = CC2520_INS_RXBUF;
|
|
|
|
cc2520->spi.cmd_buf[1] = 0;
|
|
|
|
cc2520->spi.cmd_buf[2] = 0;
|
|
|
|
|
|
|
|
spi_slave_select(cc2520->spi.dev, cc2520->spi.slave);
|
|
|
|
|
|
|
|
if (spi_transceive(cc2520->spi.dev, cc2520->spi.cmd_buf, 3,
|
|
|
|
cc2520->spi.cmd_buf, 3) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cc2520->spi.cmd_buf[2] & CC2520_FCS_CRC_OK)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-12 15:33:05 +02:00
|
|
|
net_nbuf_set_ieee802154_rssi(buf, cc2520->spi.cmd_buf[1]);
|
|
|
|
|
2016-11-15 15:30:23 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
cc2520->lqi = cc2520->spi.cmd_buf[2] & CC2520_FCS_CORRELATION;
|
|
|
|
if (cc2520->lqi <= 50) {
|
|
|
|
cc2520->lqi = 0;
|
|
|
|
} else if (cc2520->lqi >= 110) {
|
|
|
|
cc2520->lqi = 255;
|
|
|
|
} else {
|
|
|
|
cc2520->lqi = (cc2520->lqi - 50) << 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-04 11:16:17 +02:00
|
|
|
static inline bool verify_rxfifo_validity(struct cc2520_spi *spi,
|
|
|
|
uint8_t pkt_len)
|
|
|
|
{
|
|
|
|
if (pkt_len < 2 || read_reg_rxfifocnt(spi) != pkt_len) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
static void cc2520_rx(int arg)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct device *dev = INT_TO_POINTER(arg);
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
struct net_buf *pkt_buf = NULL;
|
2016-11-04 12:25:00 +01:00
|
|
|
struct net_buf *buf;
|
2016-03-18 13:08:40 +01:00
|
|
|
uint8_t pkt_len;
|
|
|
|
|
|
|
|
while (1) {
|
2016-11-04 12:25:00 +01:00
|
|
|
buf = NULL;
|
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_take(&cc2520->rx_lock, K_FOREVER);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
if (cc2520->overflow) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("RX overflow!");
|
2016-03-18 13:08:40 +01:00
|
|
|
cc2520->overflow = false;
|
2016-06-15 10:46:10 +02:00
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
goto flush;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt_len = read_rxfifo_length(&cc2520->spi) & 0x7f;
|
|
|
|
if (!verify_rxfifo_validity(&cc2520->spi, pkt_len)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Invalid content");
|
2016-03-18 13:08:40 +01:00
|
|
|
goto flush;
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:56:15 +01:00
|
|
|
buf = net_nbuf_get_reserve_rx(0, K_NO_WAIT);
|
2016-06-15 10:46:10 +02:00
|
|
|
if (!buf) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("No buf available");
|
2016-03-18 13:08:40 +01:00
|
|
|
goto flush;
|
|
|
|
}
|
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RAW)
|
2016-09-26 11:51:58 +02:00
|
|
|
/**
|
|
|
|
* Reserve 1 byte for length
|
|
|
|
*/
|
2017-02-24 12:32:56 +01:00
|
|
|
net_nbuf_set_ll_reserve(buf, 1);
|
2016-09-26 11:51:58 +02:00
|
|
|
#endif
|
2017-02-24 12:32:56 +01:00
|
|
|
pkt_buf = net_nbuf_get_frag(buf, K_NO_WAIT);
|
2016-06-15 10:46:10 +02:00
|
|
|
if (!pkt_buf) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("No pkt_buf available");
|
2016-11-04 12:25:00 +01:00
|
|
|
goto flush;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-06-15 10:46:10 +02:00
|
|
|
|
|
|
|
net_buf_frag_insert(buf, pkt_buf);
|
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RAW)
|
2016-11-16 15:44:33 +01:00
|
|
|
if (!read_rxfifo_content(&cc2520->spi, pkt_buf, pkt_len)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("No content read");
|
2016-11-16 15:44:33 +01:00
|
|
|
goto flush;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pkt_buf->data[pkt_len - 1] & CC2520_FCS_CRC_OK)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
cc2520->lqi = pkt_buf->data[pkt_len - 1] &
|
|
|
|
CC2520_FCS_CORRELATION;
|
|
|
|
if (cc2520->lqi <= 50) {
|
|
|
|
cc2520->lqi = 0;
|
|
|
|
} else if (cc2520->lqi >= 110) {
|
|
|
|
cc2520->lqi = 255;
|
|
|
|
} else {
|
|
|
|
cc2520->lqi = (cc2520->lqi - 50) << 2;
|
|
|
|
}
|
|
|
|
#else
|
2016-11-15 15:30:23 +01:00
|
|
|
if (!read_rxfifo_content(&cc2520->spi, pkt_buf, pkt_len - 2)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("No content read");
|
2016-11-04 12:25:00 +01:00
|
|
|
goto flush;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2017-04-12 15:33:05 +02:00
|
|
|
if (!verify_crc(cc2520, buf)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Bad packet CRC");
|
2016-06-15 10:46:10 +02:00
|
|
|
goto out;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
2016-11-16 15:44:33 +01:00
|
|
|
#endif
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
if (ieee802154_radio_handle_ack(cc2520->iface, buf) == NET_OK) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("ACK packet handled");
|
2016-06-15 10:46:10 +02:00
|
|
|
goto out;
|
|
|
|
}
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RAW)
|
2016-11-08 17:22:03 +01:00
|
|
|
net_buf_add_u8(pkt_buf, cc2520->lqi);
|
|
|
|
#endif
|
2016-09-30 14:26:37 +02:00
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("Caught a packet (%u) (LQI: %u)",
|
2016-11-08 17:22:03 +01:00
|
|
|
pkt_len, cc2520->lqi);
|
2016-06-15 10:46:10 +02:00
|
|
|
|
|
|
|
if (net_recv_data(cc2520->iface, buf) < 0) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("Packet dropped by NET stack");
|
2016-06-15 10:46:10 +02:00
|
|
|
goto out;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
net_analyze_stack("CC2520 Rx Fiber stack",
|
2016-11-16 02:27:29 +01:00
|
|
|
(unsigned char *)cc2520->cc2520_rx_stack,
|
2017-02-06 11:49:16 +01:00
|
|
|
CONFIG_IEEE802154_CC2520_RX_STACK_SIZE);
|
2016-11-04 12:25:00 +01:00
|
|
|
continue;
|
2016-03-18 13:08:40 +01:00
|
|
|
flush:
|
2016-12-02 10:49:34 +01:00
|
|
|
_cc2520_print_exceptions(cc2520);
|
|
|
|
_cc2520_print_errors(cc2520);
|
2016-03-18 13:08:40 +01:00
|
|
|
flush_rxfifo(cc2520);
|
2016-11-04 12:25:00 +01:00
|
|
|
out:
|
|
|
|
if (buf) {
|
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************
|
|
|
|
* Radio device API *
|
|
|
|
*******************/
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_cca(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
|
|
|
if (!get_cca(cc2520)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("Busy");
|
2016-06-15 10:46:10 +02:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cc2520_set_channel(struct device *dev, uint16_t channel)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("%u", channel);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
if (channel < 11 || channel > 26) {
|
2016-04-27 14:49:41 +02:00
|
|
|
return -EINVAL;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* See chapter 16 */
|
|
|
|
channel = 11 + 5 * (channel - 11);
|
|
|
|
|
|
|
|
if (!write_reg_freqctrl(&cc2520->spi, FREQCTRL_FREQ(channel))) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_set_pan_id(struct device *dev, uint16_t pan_id)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("0x%x", pan_id);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
pan_id = sys_le16_to_cpu(pan_id);
|
|
|
|
|
|
|
|
if (!write_mem_pan_id(&cc2520->spi, (uint8_t *) &pan_id)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_set_short_addr(struct device *dev, uint16_t short_addr)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("0x%x", short_addr);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
short_addr = sys_le16_to_cpu(short_addr);
|
|
|
|
|
|
|
|
if (!write_mem_short_addr(&cc2520->spi, (uint8_t *) &short_addr)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_set_ieee_addr(struct device *dev, const uint8_t *ieee_addr)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
2016-09-26 10:27:42 +02:00
|
|
|
if (!write_mem_ext_addr(&cc2520->spi, (void *)ieee_addr)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
2016-09-20 10:03:24 +02:00
|
|
|
ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
|
|
|
|
ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);
|
2016-04-08 16:21:10 +02:00
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_set_txpower(struct device *dev, int16_t dbm)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
uint8_t pwr;
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("%d", dbm);
|
2016-06-15 10:46:10 +02:00
|
|
|
|
|
|
|
/* 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(&cc2520->spi, pwr)) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("Failed");
|
2016-06-15 10:46:10 +02:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
static int cc2520_tx(struct device *dev,
|
|
|
|
struct net_buf *buf,
|
|
|
|
struct net_buf *frag)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
uint8_t *frame = frag->data - net_nbuf_ll_reserve(buf);
|
|
|
|
uint8_t len = net_nbuf_ll_reserve(buf) + frag->len;
|
2016-03-18 13:08:40 +01:00
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
uint8_t retry = 2;
|
|
|
|
bool status;
|
|
|
|
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
SYS_LOG_DBG("%p (%u)", frag, len);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
if (!write_reg_excflag0(&cc2520->spi, EXCFLAG0_RESET_TX_FLAGS) ||
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
!write_txfifo_length(&cc2520->spi, len) ||
|
|
|
|
!write_txfifo_content(&cc2520->spi, frame, len)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Cannot feed in TX fifo");
|
2016-03-18 13:08:40 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
if (!verify_txfifo_status(cc2520, len)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Did not write properly into TX FIFO");
|
2016-03-18 13:08:40 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
k_sem_take(&cc2520->access_lock, K_FOREVER);
|
|
|
|
#endif
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
/* 1 retry is allowed here */
|
|
|
|
do {
|
|
|
|
atomic_set(&cc2520->tx, 1);
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_init(&cc2520->tx_sync, 0, UINT_MAX);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
if (!instruct_stxoncca(&cc2520->spi)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Cannot start transmission");
|
2016-03-18 13:08:40 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_take(&cc2520->tx_sync, 10);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
retry--;
|
|
|
|
status = verify_tx_done(cc2520);
|
|
|
|
} while (!status && retry);
|
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
k_sem_give(&cc2520->access_lock);
|
|
|
|
#endif
|
2016-12-02 10:49:34 +01:00
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
if (status) {
|
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
k_sem_give(&cc2520->access_lock);
|
|
|
|
#endif
|
|
|
|
SYS_LOG_ERR("No TX_FRM_DONE");
|
|
|
|
_cc2520_print_exceptions(cc2520);
|
|
|
|
_cc2520_print_errors(cc2520);
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
atomic_set(&cc2520->tx, 0);
|
|
|
|
instruct_sflushtx(&cc2520->spi);
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_start(struct device *dev)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-06-15 10:41:36 +02:00
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("");
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-04-13 11:09:19 +02:00
|
|
|
if (!instruct_sxoscon(&cc2520->spi) ||
|
|
|
|
!instruct_srxon(&cc2520->spi) ||
|
2016-03-18 13:08:40 +01:00
|
|
|
!verify_osc_stabilization(cc2520)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Error starting CC2520");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
flush_rxfifo(cc2520);
|
|
|
|
|
|
|
|
enable_fifop_interrupt(cc2520, true);
|
|
|
|
enable_sfd_interrupt(cc2520, true);
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static int cc2520_stop(struct device *dev)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
2016-06-15 10:41:36 +02:00
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("");
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-11-16 12:46:06 +01:00
|
|
|
flush_rxfifo(cc2520);
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
enable_fifop_interrupt(cc2520, false);
|
|
|
|
enable_sfd_interrupt(cc2520, false);
|
|
|
|
|
2016-04-13 11:09:19 +02:00
|
|
|
if (!instruct_srfoff(&cc2520->spi) ||
|
|
|
|
!instruct_sxoscoff(&cc2520->spi)) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Error stopping CC2520");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-11-08 17:22:03 +01:00
|
|
|
static uint8_t cc2520_get_lqi(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
|
|
|
return cc2520->lqi;
|
|
|
|
}
|
2016-03-18 13:08:40 +01:00
|
|
|
|
|
|
|
/******************
|
|
|
|
* Initialization *
|
|
|
|
*****************/
|
|
|
|
static int power_on_and_setup(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
|
|
|
/* Switching to LPM2 mode */
|
|
|
|
set_reset(dev, 0);
|
|
|
|
_usleep(150);
|
|
|
|
|
|
|
|
set_vreg_en(dev, 0);
|
|
|
|
_usleep(250);
|
|
|
|
|
|
|
|
/* Then to ACTIVE mode */
|
|
|
|
set_vreg_en(dev, 1);
|
|
|
|
_usleep(250);
|
|
|
|
|
|
|
|
set_reset(dev, 1);
|
|
|
|
_usleep(150);
|
|
|
|
|
|
|
|
if (!verify_osc_stabilization(cc2520)) {
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Default settings to always write (see chapter 28 part 1) */
|
|
|
|
if (!write_reg_txpower(&cc2520->spi, CC2520_TXPOWER_DEFAULT) ||
|
|
|
|
!write_reg_ccactrl0(&cc2520->spi, CC2520_CCACTRL0_DEFAULT) ||
|
|
|
|
!write_reg_mdmctrl0(&cc2520->spi, CC2520_MDMCTRL0_DEFAULT) ||
|
|
|
|
!write_reg_mdmctrl1(&cc2520->spi, CC2520_MDMCTRL1_DEFAULT) ||
|
|
|
|
!write_reg_rxctrl(&cc2520->spi, CC2520_RXCTRL_DEFAULT) ||
|
|
|
|
!write_reg_fsctrl(&cc2520->spi, CC2520_FSCTRL_DEFAULT) ||
|
|
|
|
!write_reg_fscal1(&cc2520->spi, CC2520_FSCAL1_DEFAULT) ||
|
|
|
|
!write_reg_agcctrl1(&cc2520->spi, CC2520_AGCCTRL1_DEFAULT) ||
|
|
|
|
!write_reg_adctest0(&cc2520->spi, CC2520_ADCTEST0_DEFAULT) ||
|
|
|
|
!write_reg_adctest1(&cc2520->spi, CC2520_ADCTEST1_DEFAULT) ||
|
|
|
|
!write_reg_adctest2(&cc2520->spi, CC2520_ADCTEST2_DEFAULT)) {
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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(&cc2520->spi, 0) ||
|
|
|
|
!write_reg_frmctrl0(&cc2520->spi, CC2520_AUTOMATISM) ||
|
|
|
|
!write_reg_frmctrl1(&cc2520->spi, FRMCTRL1_IGNORE_TX_UNDERF |
|
|
|
|
FRMCTRL1_SET_RXENMASK_ON_TX) ||
|
2016-04-11 16:55:22 +02:00
|
|
|
!write_reg_frmfilt0(&cc2520->spi, FRMFILT0_FRAME_FILTER_EN |
|
2016-03-18 13:08:40 +01:00
|
|
|
FRMFILT0_MAX_FRAME_VERSION(3)) ||
|
2016-04-11 16:55:22 +02:00
|
|
|
!write_reg_frmfilt1(&cc2520->spi, FRMFILT1_ACCEPT_ALL) ||
|
|
|
|
!write_reg_srcmatch(&cc2520->spi, SRCMATCH_DEFAULTS) ||
|
2016-03-18 13:08:40 +01:00
|
|
|
!write_reg_fifopctrl(&cc2520->spi,
|
|
|
|
FIFOPCTRL_FIFOP_THR(CC2520_TX_THRESHOLD))) {
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleaning up TX fifo */
|
|
|
|
instruct_sflushtx(&cc2520->spi);
|
|
|
|
|
|
|
|
setup_gpio_callbacks(dev);
|
|
|
|
|
|
|
|
_cc2520_print_gpio_config(dev);
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int configure_spi(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
struct spi_config spi_conf = {
|
|
|
|
.config = SPI_WORD(8),
|
2017-02-06 11:49:16 +01:00
|
|
|
.max_sys_freq = CONFIG_IEEE802154_CC2520_SPI_FREQ,
|
2016-03-18 13:08:40 +01:00
|
|
|
};
|
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
cc2520->spi.dev = device_get_binding(
|
|
|
|
CONFIG_IEEE802154_CC2520_SPI_DRV_NAME);
|
2016-11-23 10:48:08 +01:00
|
|
|
if (!cc2520->spi.dev) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Unable to get SPI device");
|
2016-11-23 10:48:08 +01:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
cc2520->spi.slave = CONFIG_IEEE802154_CC2520_SPI_SLAVE;
|
2016-11-23 10:48:08 +01:00
|
|
|
|
|
|
|
if (spi_configure(cc2520->spi.dev, &spi_conf) != 0 ||
|
|
|
|
spi_slave_select(cc2520->spi.dev,
|
|
|
|
cc2520->spi.slave) != 0) {
|
|
|
|
cc2520->spi.dev = NULL;
|
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-10-26 06:54:53 +02:00
|
|
|
static int cc2520_init(struct device *dev)
|
2016-03-18 13:08:40 +01:00
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
|
|
|
|
atomic_set(&cc2520->tx, 0);
|
2016-11-09 16:53:16 +01:00
|
|
|
k_sem_init(&cc2520->rx_lock, 0, UINT_MAX);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2017-02-22 08:37:51 +01:00
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
k_sem_init(&cc2520->access_lock, 1, 1);
|
|
|
|
#endif
|
|
|
|
|
2016-03-18 13:08:40 +01:00
|
|
|
cc2520->gpios = cc2520_configure_gpios();
|
|
|
|
if (!cc2520->gpios) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Configuring GPIOS failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
if (configure_spi(dev) != 0) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Configuring SPI failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("GPIO and SPI configured");
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
if (power_on_and_setup(dev) != 0) {
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_ERR("Configuring CC2520 failed");
|
2016-04-26 16:43:02 +02:00
|
|
|
return -EIO;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-11-09 16:53:16 +01:00
|
|
|
k_thread_spawn(cc2520->cc2520_rx_stack,
|
2017-02-06 11:49:16 +01:00
|
|
|
CONFIG_IEEE802154_CC2520_RX_STACK_SIZE,
|
2016-11-09 16:53:16 +01:00
|
|
|
(k_thread_entry_t)cc2520_rx,
|
|
|
|
dev, NULL, NULL,
|
|
|
|
K_PRIO_COOP(2), 0, 0);
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_INF("CC2520 initialized");
|
|
|
|
|
2016-04-26 16:43:02 +02:00
|
|
|
return 0;
|
2016-03-18 13:08:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static void cc2520_iface_init(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct device *dev = net_if_get_device(iface);
|
|
|
|
struct cc2520_context *cc2520 = dev->driver_data;
|
|
|
|
uint8_t *mac = get_mac(dev);
|
|
|
|
|
2016-12-02 10:49:34 +01:00
|
|
|
SYS_LOG_DBG("");
|
2016-06-15 10:46:10 +02:00
|
|
|
|
2017-02-15 12:20:31 +01:00
|
|
|
net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);
|
2016-06-15 10:46:10 +02:00
|
|
|
|
|
|
|
cc2520->iface = iface;
|
|
|
|
|
|
|
|
ieee802154_init(iface);
|
|
|
|
}
|
|
|
|
|
2016-10-26 06:54:53 +02:00
|
|
|
static struct cc2520_context cc2520_context_data;
|
2016-03-18 13:08:40 +01:00
|
|
|
|
2016-06-15 10:46:10 +02:00
|
|
|
static struct ieee802154_radio_api cc2520_radio_api = {
|
|
|
|
.iface_api.init = cc2520_iface_init,
|
|
|
|
.iface_api.send = ieee802154_radio_send,
|
|
|
|
|
|
|
|
.cca = cc2520_cca,
|
|
|
|
.set_channel = cc2520_set_channel,
|
|
|
|
.set_pan_id = cc2520_set_pan_id,
|
|
|
|
.set_short_addr = cc2520_set_short_addr,
|
|
|
|
.set_ieee_addr = cc2520_set_ieee_addr,
|
|
|
|
.set_txpower = cc2520_set_txpower,
|
|
|
|
.start = cc2520_start,
|
|
|
|
.stop = cc2520_stop,
|
|
|
|
.tx = cc2520_tx,
|
2016-11-08 17:22:03 +01:00
|
|
|
.get_lqi = cc2520_get_lqi,
|
2016-06-15 10:46:10 +02:00
|
|
|
};
|
|
|
|
|
2017-02-06 11:49:16 +01:00
|
|
|
#if defined(CONFIG_IEEE802154_CC2520_RAW)
|
|
|
|
DEVICE_AND_API_INIT(cc2520, CONFIG_IEEE802154_CC2520_DRV_NAME,
|
2016-09-26 11:51:58 +02:00
|
|
|
cc2520_init, &cc2520_context_data, NULL,
|
2017-02-06 11:49:16 +01:00
|
|
|
POST_KERNEL, CONFIG_IEEE802154_CC2520_INIT_PRIO,
|
2016-09-26 11:51:58 +02:00
|
|
|
&cc2520_radio_api);
|
|
|
|
#else
|
2017-02-06 11:49:16 +01:00
|
|
|
NET_DEVICE_INIT(cc2520, CONFIG_IEEE802154_CC2520_DRV_NAME,
|
2016-06-15 10:46:10 +02:00
|
|
|
cc2520_init, &cc2520_context_data, NULL,
|
2017-02-06 11:49:16 +01:00
|
|
|
CONFIG_IEEE802154_CC2520_INIT_PRIO,
|
2016-06-15 10:46:10 +02:00
|
|
|
&cc2520_radio_api, IEEE802154_L2,
|
2016-11-16 11:48:40 +01:00
|
|
|
NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);
|
2016-11-18 21:58:17 +01:00
|
|
|
|
2016-11-28 14:05:02 +01:00
|
|
|
NET_STACK_INFO_ADDR(RX, cc2520,
|
2017-02-06 11:49:16 +01:00
|
|
|
CONFIG_IEEE802154_CC2520_RX_STACK_SIZE,
|
|
|
|
CONFIG_IEEE802154_CC2520_RX_STACK_SIZE,
|
2016-11-28 14:05:02 +01:00
|
|
|
((struct cc2520_context *)(&__device_cc2520))->
|
|
|
|
cc2520_rx_stack,
|
|
|
|
0);
|
2016-09-26 11:51:58 +02:00
|
|
|
#endif
|
2017-02-22 08:37:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_IEEE802154_CC2520_CRYPTO
|
|
|
|
|
|
|
|
static bool _cc2520_read_ram(struct cc2520_spi *spi, uint16_t addr,
|
|
|
|
uint8_t *data_buf, uint8_t len)
|
|
|
|
{
|
|
|
|
uint8_t cmd_buf[128];
|
|
|
|
|
|
|
|
cmd_buf[0] = CC2520_INS_MEMRD | (addr >> 8);
|
|
|
|
cmd_buf[1] = addr;
|
|
|
|
|
|
|
|
spi_slave_select(spi->dev, spi->slave);
|
|
|
|
|
|
|
|
if (spi_transceive(spi->dev, cmd_buf, len + 2,
|
|
|
|
cmd_buf, len + 2) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data_buf, &cmd_buf[2], len);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool instruct_ccm(struct cc2520_context *cc2520,
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
uint8_t cmd[9];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
SYS_LOG_DBG("CCM(P={01} K={%02x} C={%02x} N={%02x}"
|
|
|
|
" A={%03x} E={%03x} F{%02x} M={%02x})",
|
|
|
|
key_addr, auth_crypt, nonce_addr,
|
|
|
|
input_addr, output_addr, in_len, m);
|
|
|
|
|
|
|
|
cmd[0] = 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(&cc2520->access_lock, K_FOREVER);
|
|
|
|
|
|
|
|
ret = spi_write(cc2520->spi.dev, cmd, 9);
|
|
|
|
|
|
|
|
k_sem_give(&cc2520->access_lock);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
SYS_LOG_ERR("CCM Failed");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool instruct_uccm(struct cc2520_context *cc2520,
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
uint8_t cmd[9];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
SYS_LOG_DBG("UCCM(P={01} K={%02x} C={%02x} N={%02x}"
|
|
|
|
" A={%03x} E={%03x} F{%02x} M={%02x})",
|
|
|
|
key_addr, auth_crypt, nonce_addr,
|
|
|
|
input_addr, output_addr, in_len, m);
|
|
|
|
|
|
|
|
cmd[0] = CC2520_INS_UCCM | 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(&cc2520->access_lock, K_FOREVER);
|
|
|
|
|
|
|
|
ret = spi_write(cc2520->spi.dev, cmd, 9);
|
|
|
|
|
|
|
|
k_sem_give(&cc2520->access_lock);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
SYS_LOG_ERR("UCCM Failed");
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = ctx->device->driver_data;
|
|
|
|
uint8_t data[128];
|
|
|
|
uint8_t *in_buf;
|
|
|
|
uint8_t in_len;
|
|
|
|
uint8_t m = 0;
|
|
|
|
|
|
|
|
if (!apkt->pkt->out_buf || !apkt->pkt->out_buf_max) {
|
|
|
|
SYS_LOG_ERR("Out buffer needs to be set");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx->key.bit_stream || !ctx->keylen) {
|
|
|
|
SYS_LOG_ERR("No key installed");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ctx->flags & CAP_INPLACE_OPS)) {
|
|
|
|
SYS_LOG_ERR("It supports only in-place operation");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!apkt->ad || !apkt->ad_len) {
|
|
|
|
SYS_LOG_ERR("CCM needs associated data");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apkt->pkt->in_buf && apkt->pkt->in_buf - apkt->ad_len != apkt->ad) {
|
|
|
|
SYS_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) {
|
|
|
|
SYS_LOG_ERR("Auth only needs a tag length");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
in_buf = apkt->ad;
|
|
|
|
in_len = apkt->ad_len;
|
|
|
|
|
|
|
|
*auth_crypt = 0;
|
|
|
|
} 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 = 3;
|
|
|
|
} else {
|
|
|
|
m = ctx->mode_params.ccm_info.tag_len >> 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Writing the frame in RAM */
|
|
|
|
if (!_cc2520_write_ram(&cc2520->spi, CC2520_MEM_DATA, in_buf, in_len)) {
|
|
|
|
SYS_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->spi, CC2520_MEM_KEY, data, 16)) {
|
|
|
|
SYS_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->spi, CC2520_MEM_NONCE, data, 16)) {
|
|
|
|
SYS_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)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = ctx->device->driver_data;
|
|
|
|
uint8_t auth_crypt;
|
|
|
|
int m;
|
|
|
|
|
|
|
|
if (!apkt || !apkt->pkt) {
|
|
|
|
SYS_LOG_ERR("Invalid crypto packet to operate with");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (apkt->tag) {
|
|
|
|
SYS_LOG_ERR("CCM encryption does not take a tag");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = insert_crypto_parameters(ctx, apkt, ccm_nonce, &auth_crypt);
|
|
|
|
if (m < 0) {
|
|
|
|
SYS_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) {
|
|
|
|
SYS_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_ccm(cc2520, CC2520_MEM_KEY >> 4, auth_crypt,
|
|
|
|
CC2520_MEM_NONCE >> 4, CC2520_MEM_DATA,
|
|
|
|
0x000, apkt->ad_len, m) ||
|
|
|
|
!_cc2520_read_ram(&cc2520->spi, CC2520_MEM_DATA,
|
|
|
|
apkt->pkt->out_buf, apkt->pkt->out_len)) {
|
|
|
|
SYS_LOG_ERR("CCM or reading result from RAM failed");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
apkt->tag = apkt->pkt->out_buf + apkt->pkt->in_len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _cc2520_crypto_uccm(struct cipher_ctx *ctx,
|
|
|
|
struct cipher_aead_pkt *apkt,
|
|
|
|
uint8_t *ccm_nonce)
|
|
|
|
{
|
|
|
|
struct cc2520_context *cc2520 = ctx->device->driver_data;
|
|
|
|
uint8_t auth_crypt;
|
|
|
|
int m;
|
|
|
|
|
|
|
|
if (!apkt || !apkt->pkt) {
|
|
|
|
SYS_LOG_ERR("Invalid crypto packet to operate with");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->mode_params.ccm_info.tag_len && !apkt->tag) {
|
|
|
|
SYS_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(cc2520, CC2520_MEM_KEY >> 4, auth_crypt,
|
|
|
|
CC2520_MEM_NONCE >> 4, CC2520_MEM_DATA,
|
|
|
|
0x000, apkt->ad_len, m) ||
|
|
|
|
!_cc2520_read_ram(&cc2520->spi, CC2520_MEM_DATA,
|
|
|
|
apkt->pkt->out_buf, apkt->pkt->out_len)) {
|
|
|
|
SYS_LOG_ERR("UCCM or reading result from RAM failed");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m && (!(read_reg_dpustat(&cc2520->spi) & DPUSTAT_AUTHSTAT_H))) {
|
|
|
|
SYS_LOG_ERR("Authentication of the frame failed");
|
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cc2520_crypto_hw_caps(struct device *dev)
|
|
|
|
{
|
|
|
|
return CAP_RAW_KEY | CAP_INPLACE_OPS | CAP_SYNC_OPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cc2520_crypto_begin_session(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) {
|
|
|
|
SYS_LOG_ERR("Wrong algo (%u) or mode (%u)", algo, mode);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->mode_params.ccm_info.nonce_len != 13) {
|
|
|
|
SYS_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(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(struct device *dev)
|
|
|
|
{
|
|
|
|
SYS_LOG_INF("CC2520 crypto part initialized");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct crypto_driver_api cc2520_crypto_api = {
|
|
|
|
.query_hw_caps = cc2520_crypto_hw_caps,
|
|
|
|
.begin_session = cc2520_crypto_begin_session,
|
|
|
|
.free_session = cc2520_crypto_free_session,
|
|
|
|
.crypto_async_callback_set = NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
DEVICE_AND_API_INIT(cc2520_crypto, CONFIG_IEEE802154_CC2520_CRYPTO_DRV_NAME,
|
|
|
|
cc2520_crypto_init, &cc2520_context_data, NULL,
|
|
|
|
POST_KERNEL, CONFIG_IEEE802154_CC2520_CRYPTO_INIT_PRIO,
|
|
|
|
&cc2520_crypto_api);
|
|
|
|
|
|
|
|
#endif /* CONFIG_IEEE802154_CC2520_CRYPTO */
|