1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-20 03:53:49 +01:00
RIOT/cpu/esp32/esp-ieee802154/esp_ieee802154_hal.c
2025-07-15 14:35:31 +02:00

471 lines
15 KiB
C

/*
* Copyright (C) 2025 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include "esp_ieee802154.h"
#include "esp_ieee802154_hal.h"
#include "iolist.h"
#include "log.h"
#include "net/ieee802154/radio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* ACK frame timeout in microseconds, should be a multiple of 16 */
#define ESP_IEEE802154_ACK_TIMEOUT_US (3456)
_Static_assert((3456 % 16 == 0), "ACK frame timeout should be a multiple of 16");
/* Although the device driver supports IEEE802154_CAP_IRQ_TX_START,
* IEEE802154_CAP_IRQ_RX_START and IEEE802154_CAP_IRQ_CCA_DONE, we are not
* using it for now to avoid unnecessary performance degradation as it is
* not used by any link layer driver */
#define _USE_RX_START 0
#define _USE_TX_START 0
#define _USE_CCA_DONE 0
#define _USE_SET_RX 1
#define _USE_SET_IDLE 1
static const ieee802154_radio_ops_t esp_ieee802154_driver;
static ieee802154_dev_t *esp_ieee802154_dev;
static uint8_t _tx_frame[IEEE802154_FRAME_LEN_MAX + 1]; /* additional byte 0 is used for length */
static uint8_t *_rx_frame;
static const uint8_t *_ack_frame;
static esp_ieee802154_frame_info_t *_rx_frame_info;
static esp_ieee802154_frame_info_t *_ack_frame_info;
static bool _channel_clear;
static esp_ieee802154_tx_error_t _tx_error; /* error on last transmit */
static int _write(ieee802154_dev_t *dev, const iolist_t *psdu)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
assert(psdu);
/* copy packet data into the _tx_frame buffer */
uint8_t *dst = &_tx_frame[1]; /* first byte is frame length */
for (; psdu; psdu = psdu->iol_next) {
if (psdu->iol_len) {
assert(((dst - &_tx_frame[1]) +
psdu->iol_len + IEEE802154_FCS_LEN) < ARRAY_SIZE(_tx_frame));
memcpy(dst, psdu->iol_base, psdu->iol_len);
dst += psdu->iol_len;
}
}
/* length of the package. */
_tx_frame[0] = (dst - &_tx_frame[1]) + IEEE802154_FCS_LEN;
DEBUG("[esp_ieee802154] %s: put %d bytes to _tx_frame\n", __func__, _tx_frame[0]);
return 0;
}
static int _len(ieee802154_dev_t *dev)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
return _rx_frame[0] - IEEE802154_FCS_LEN;
}
static int _read(ieee802154_dev_t *dev, void *buf, size_t size, ieee802154_rx_info_t *info)
{
(void)dev;
DEBUG("[esp_ieee802154] %s: buf=%p size=%u info=%p\n",
__func__, buf, size, info);
unsigned len = _rx_frame[0] - IEEE802154_FCS_LEN;
int res = 0;
if (size < len) {
DEBUG("[esp_ieee802154] %s: buffer to small (%u < %u)\n",
__func__, size, len);
res = -ENOBUFS;
}
else if (buf) {
DEBUG("[esp_ieee802154] %s: read packet of length %d\n", __func__, len);
if (info) {
info->rssi = ieee802154_dbm_to_rssi(_rx_frame[len]);
info->lqi = _rx_frame[len+1];
}
memcpy(buf, &_rx_frame[1], len);
res = len;
}
esp_ieee802154_receive_handle_done(_rx_frame);
return res;
}
static int _off(ieee802154_dev_t *dev)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
esp_ieee802154_disable();
return 0;
}
static int _request_on(ieee802154_dev_t *dev)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
esp_ieee802154_enable();
return 0;
}
static int _confirm_on(ieee802154_dev_t *dev)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
return 0;
}
static int _request_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx)
{
(void)dev;
(void)ctx;
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_TRANSMIT\n", __func__);
_tx_error = ESP_IEEE802154_TX_ERR_NONE;
_ack_frame = NULL;
_ack_frame_info = NULL;
return esp_ieee802154_transmit(_tx_frame, true) ? -EIO : 0;
case IEEE802154_HAL_OP_SET_RX:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_SET_RX\n", __func__);
return (IS_ACTIVE(_USE_SET_RX) && esp_ieee802154_receive()) ? -EIO : 0;
case IEEE802154_HAL_OP_SET_IDLE:
/* TODO: ctx = (bool *force) */
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_SET_IDLE\n", __func__);
return (IS_ACTIVE(_USE_SET_IDLE) && esp_ieee802154_sleep()) ? -EIO : 0;
return 0;
case IEEE802154_HAL_OP_CCA:
extern esp_err_t esp_ieee802154_cca(void);
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_CCA\n", __func__);
return esp_ieee802154_cca();
}
return -EINVAL;
}
static int _confirm_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx)
{
(void)dev;
esp_ieee802154_state_t state = esp_ieee802154_get_state();
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_TRANSMIT\n", __func__);
if (ctx) {
ieee802154_tx_info_t *info = ctx;
switch (_tx_error) {
case ESP_IEEE802154_TX_ERR_NONE:
info->status = TX_STATUS_SUCCESS;
if (_ack_frame_info && _ack_frame_info->pending) {
info->status = TX_STATUS_FRAME_PENDING;
}
break;
case ESP_IEEE802154_TX_ERR_CCA_BUSY:
info->status = TX_STATUS_MEDIUM_BUSY;
break;
case ESP_IEEE802154_TX_ERR_NO_ACK:
info->status = TX_STATUS_NO_ACK;
break;
default:
break;
}
}
esp_ieee802154_receive_handle_done(_ack_frame);
return 0;
case IEEE802154_HAL_OP_SET_RX:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_SET_RX\n", __func__);
return (!IS_ACTIVE(_USE_SET_RX) ||
(state == ESP_IEEE802154_RADIO_RECEIVE)) ? 0 : -EAGAIN;
case IEEE802154_HAL_OP_SET_IDLE:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_SET_IDLE\n", __func__);
return (!IS_ACTIVE(_USE_SET_IDLE) ||
((state == ESP_IEEE802154_RADIO_IDLE) ||
(state == ESP_IEEE802154_RADIO_SLEEP))) ? 0 : -EAGAIN;
case IEEE802154_HAL_OP_CCA:
DEBUG("[esp_ieee802154] %s: IEEE802154_HAL_OP_CCA\n", __func__);
assert(ctx);
*((bool *)ctx) = _channel_clear;
return 0;
}
return -EINVAL;
}
static int _set_cca_threshold(ieee802154_dev_t *dev, int8_t threshold)
{
(void)dev;
DEBUG("[esp_ieee802154] %s: threshold %i\n", __func__, threshold);
return esp_ieee802154_set_cca_threshold(threshold) ? -EIO : 0;
}
static int _set_cca_mode(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode)
{
(void)dev;
switch (mode) {
case IEEE802154_CCA_MODE_ED_THRESHOLD:
DEBUG("[esp_ieee802154] %s: set IEEE802154_CCA_MODE_ED_THRESHOLD\n", __func__);
return esp_ieee802154_set_cca_mode(ESP_IEEE802154_CCA_MODE_ED) ? -EIO : 0;
case IEEE802154_CCA_MODE_CARRIER_SENSING:
DEBUG("[esp_ieee802154] %s: set IEEE802154_CCA_MODE_CARRIER_SENSING\n", __func__);
return esp_ieee802154_set_cca_mode(ESP_IEEE802154_CCA_MODE_CARRIER) ? -EIO : 0;
case IEEE802154_CCA_MODE_ED_THRESH_AND_CS:
DEBUG("[esp_ieee802154] %s: set IEEE802154_CCA_MODE_ED_THRESH_AND_CS\n", __func__);
return esp_ieee802154_set_cca_mode(ESP_IEEE802154_CCA_MODE_CARRIER_AND_ED) ? -EIO : 0;
case IEEE802154_CCA_MODE_ED_THRESH_OR_CS:
DEBUG("[esp_ieee802154] %s: IEEE802154_CCA_MODE_ED_THRESH_OR_CS\n", __func__);
return esp_ieee802154_set_cca_mode(ESP_IEEE802154_CCA_MODE_CARRIER_OR_ED) ? -EIO : 0;
}
return -EINVAL;
}
static int _config_phy(ieee802154_dev_t *dev, const ieee802154_phy_conf_t *conf)
{
(void)dev;
assert(conf);
DEBUG("[esp_ieee802154] %s\n", __func__);
if (esp_ieee802154_set_txpower(conf->pow) ||
esp_ieee802154_set_channel(conf->channel)) {
return -EIO;
}
return 0;
}
static int _set_frame_retrans(ieee802154_dev_t *dev, uint8_t retrans)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
return -ENOTSUP;
}
static int _set_csma_params(ieee802154_dev_t *dev, const ieee802154_csma_be_t *bd,
int8_t retries)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
return -ENOTSUP;
}
static int _set_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_t mode)
{
(void)dev;
DEBUG("[esp_ieee802154] %s\n", __func__);
return -ENOTSUP;
}
static int _config_addr_filter(ieee802154_dev_t *dev, ieee802154_af_cmd_t cmd, const void *value)
{
(void)dev;
assert(value);
const uint8_t *addr = value;
switch (cmd) {
case IEEE802154_AF_SHORT_ADDR:
DEBUG("[esp_ieee802154] %s: set IEEE802154_AF_SHORT_ADDR %02x:%02x\n",
__func__, addr[0], addr[1]);
return esp_ieee802154_set_short_address(*((const uint16_t *)value)) ? -EIO : 0;
case IEEE802154_AF_EXT_ADDR:
DEBUG("[esp_ieee802154] %s: set IEEE802154_AF_EXT_ADDR "
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", __func__,
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]);
return esp_ieee802154_set_extended_address(addr) ? -EIO : 0;
case IEEE802154_AF_PANID:
DEBUG("[esp_ieee802154] %s: set IEEE802154_AF_PANID %02x:%02x\n",
__func__, addr[1], addr[0]);
return esp_ieee802154_set_panid(*((const uint16_t *)value)) ? -EIO : 0;
case IEEE802154_AF_PAN_COORD:
DEBUG("[esp_ieee802154] %s: set IEEE802154_AF_PAN_COORD not supported\n", __func__);
return -ENOTSUP;
}
return -EINVAL;
}
static int _config_src_addr_match(ieee802154_dev_t *dev, ieee802154_src_match_t cmd,
const void *value)
{
(void)dev;
bool enable = *((bool*)value);
switch (cmd) {
case IEEE802154_SRC_MATCH_EN:
DEBUG("[esp_ieee802154] %s: IEEE802154_SRC_MATCH_EN %d\n", __func__, enable);
return esp_ieee802154_set_pending_mode(enable
? ESP_IEEE802154_AUTO_PENDING_ENABLE
: ESP_IEEE802154_AUTO_PENDING_DISABLE) ? -EIO : 0;
case IEEE802154_SRC_MATCH_SHORT_ADD:
DEBUG("[esp_ieee802154] %s: set IEEE802154_SRC_MATCH_SHORT_ADD\n", __func__);
return esp_ieee802154_add_pending_addr(value, true) ? -ENOMEM : 0;
case IEEE802154_SRC_MATCH_SHORT_CLEAR:
DEBUG("[esp_ieee802154] %s: set IEEE802154_SRC_MATCH_SHORT_CLEAR\n", __func__);
return esp_ieee802154_clear_pending_addr(value, true) ? -ENOENT : 0;
case IEEE802154_SRC_MATCH_EXT_ADD:
DEBUG("[esp_ieee802154] %s: set IEEE802154_SRC_MATCH_EXT_ADD\n", __func__);
return esp_ieee802154_add_pending_addr(value, false) ? -ENOMEM : 0;
case IEEE802154_SRC_MATCH_EXT_CLEAR:
DEBUG("[esp_ieee802154] %s: set IEEE802154_SRC_MATCH_EXT_CLEAR\n", __func__);
return esp_ieee802154_clear_pending_addr(value, false) ? -ENOENT : 0;
}
return -EINVAL;
}
/* following functions are called back from ESP-IDF driver */
void esp_ieee802154_cca_done(bool channel_busy)
{
DEBUG("[esp_ieee802154] %s %d\n", __func__, channel_busy);
_channel_clear = !channel_busy;
if (IS_ACTIVE(_USE_CCA_DONE)) {
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_CONFIRM_CCA);
}
}
void esp_ieee802154_receive_sfd_done(void)
{
DEBUG("[esp_ieee802154] %s\n", __func__);
if (IS_ACTIVE(_USE_RX_START)) {
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_INDICATION_RX_START);
}
}
void esp_ieee802154_receive_done(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info)
{
assert(frame);
assert(frame_info);
DEBUG("[esp_ieee802154] %s: frame=%p frame_info=%p\n",
__func__, frame, frame_info);
_rx_frame = frame;
_rx_frame_info = frame_info;
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_INDICATION_RX_DONE);
}
void esp_ieee802154_transmit_sfd_done(uint8_t *frame)
{
DEBUG("[esp_ieee802154] %s: frame=%p\n", __func__, frame);
if (IS_ACTIVE(_USE_TX_START)) {
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_INDICATION_TX_START);
}
}
void esp_ieee802154_transmit_done(const uint8_t *frame, const uint8_t *ack,
esp_ieee802154_frame_info_t *ack_frame_info)
{
DEBUG("[esp_ieee802154] %s: frame=%p ack_frame=%p ack_frame_info=%p\n",
__func__, frame, ack, ack_frame_info);
_ack_frame = ack;
_ack_frame_info = ack_frame_info;
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_CONFIRM_TX_DONE);
}
void esp_ieee802154_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error)
{
DEBUG("[esp_ieee802154] %s: frame=%p error=%u\n", __func__, frame, error);
_tx_error = error;
esp_ieee802154_dev->cb(esp_ieee802154_dev, IEEE802154_RADIO_CONFIRM_TX_DONE);
}
void esp_ieee802154_energy_detect_done(int8_t power)
{
DEBUG("[esp_ieee802154] %s: power %i\n", __func__, power);
}
void esp_ieee802154_setup(ieee802154_dev_t *dev)
{
assert(dev);
DEBUG("[esp_ieee802154] %s: dev=%p\n", __func__, dev);
dev->driver = &esp_ieee802154_driver;
esp_ieee802154_dev = dev;
}
int esp_ieee802154_init(void)
{
DEBUG("[esp_ieee802154] %s\n", __func__);
esp_ieee802154_enable();
esp_ieee802154_reset_pending_table(true);
esp_ieee802154_reset_pending_table(false);
esp_ieee802154_set_rx_when_idle(true);
esp_ieee802154_receive();
esp_ieee802154_set_cca_mode(ESP_IEEE802154_CCA_MODE_ED);
esp_ieee802154_set_ack_timeout(ESP_IEEE802154_ACK_TIMEOUT_US);
esp_ieee802154_set_promiscuous(false);
return 0;
}
static const ieee802154_radio_ops_t esp_ieee802154_driver = {
.caps = IEEE802154_CAP_24_GHZ
| IEEE802154_CAP_PHY_OQPSK
| IEEE802154_CAP_AUTO_CSMA
| IEEE802154_CAP_SRC_ADDR_MATCH
#if _USE_CCA_DONE
| IEEE802154_CAP_IRQ_CCA_DONE
#endif
#if _USE_RX_START
| IEEE802154_CAP_IRQ_RX_START
#endif
#if _USE_TX_START
| IEEE802154_CAP_IRQ_TX_START
#endif
| IEEE802154_CAP_IRQ_TX_DONE
| IEEE802154_CAP_IRQ_ACK_TIMEOUT,
.write = _write,
.read = _read,
.request_on = _request_on,
.confirm_on = _confirm_on,
.len = _len,
.off = _off,
.request_op = _request_op,
.confirm_op = _confirm_op,
.set_cca_threshold = _set_cca_threshold,
.set_cca_mode = _set_cca_mode,
.config_phy = _config_phy,
.set_csma_params = _set_csma_params,
.config_addr_filter = _config_addr_filter,
.config_src_addr_match = _config_src_addr_match,
.set_frame_filter_mode = _set_frame_filter_mode,
.set_frame_retrans = _set_frame_retrans,
};