Browse Source
Initial commit of the IEEE 802.15.4 driver using Espressif HAL. Signed-off-by: Martin Jäger <martin@libre.solar>pull/91646/head
7 changed files with 779 additions and 3 deletions
@ -0,0 +1,185 @@ |
|||||||
|
# Espressif ESP32 802.15.4 configuration options |
||||||
|
|
||||||
|
# Copyright (c) 2024 A Labs GmbH |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
menuconfig IEEE802154_ESP32 |
||||||
|
bool "ESP32 series IEEE 802.15.4 Driver" |
||||||
|
default y |
||||||
|
depends on DT_HAS_ESPRESSIF_ESP32_IEEE802154_ENABLED |
||||||
|
|
||||||
|
if IEEE802154_ESP32 |
||||||
|
|
||||||
|
config IEEE802154_ESP32_INIT_PRIO |
||||||
|
int "ESP32 IEEE 802.15.4 initialization priority" |
||||||
|
default 80 |
||||||
|
help |
||||||
|
Set the initialization priority number. Do not mess with it unless |
||||||
|
you know what you are doing. |
||||||
|
|
||||||
|
# Kconfigs copied from Espressif HAL module (ESP-IDF) below |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RX_BUFFER_SIZE |
||||||
|
int "Number of 802.15.4 receive buffers" |
||||||
|
default 20 |
||||||
|
range 2 100 |
||||||
|
help |
||||||
|
The number of 802.15.4 receive buffers. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
choice IEEE802154_ESP32_CCA_MODE |
||||||
|
prompt "Clear Channel Assessment (CCA) mode" |
||||||
|
default IEEE802154_ESP32_CCA_ED |
||||||
|
help |
||||||
|
Configure the CCA mode |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_CARRIER |
||||||
|
bool "Carrier sense only" |
||||||
|
help |
||||||
|
Configure the CCA mode to Carrier sense only |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_ED |
||||||
|
bool "Energy above threshold" |
||||||
|
help |
||||||
|
Configure the CCA mode to Energy above threshold |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_CARRIER_OR_ED |
||||||
|
bool "Carrier sense OR energy above threshold" |
||||||
|
help |
||||||
|
Configure the CCA mode to Carrier sense OR energy above threshold |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_CARRIER_AND_ED |
||||||
|
bool "Carrier sense AND energy above threshold" |
||||||
|
help |
||||||
|
Configure the CCA mode to Carrier sense AND energy above threshold |
||||||
|
|
||||||
|
endchoice # IEEE802154_CCA_MODE |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_MODE |
||||||
|
int |
||||||
|
default 0 if IEEE802154_ESP32_CCA_CARRIER |
||||||
|
default 1 if IEEE802154_ESP32_CCA_ED |
||||||
|
default 2 if IEEE802154_ESP32_CCA_CARRIER_OR_ED |
||||||
|
default 3 if IEEE802154_ESP32_CCA_CARRIER_AND_ED |
||||||
|
|
||||||
|
config IEEE802154_ESP32_CCA_THRESHOLD |
||||||
|
int "CCA detection threshold" |
||||||
|
range -120 0 |
||||||
|
default -60 |
||||||
|
help |
||||||
|
Set the CCA threshold, in dB. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_PENDING_TABLE_SIZE |
||||||
|
int "Pending table size" |
||||||
|
range 1 100 |
||||||
|
default 20 |
||||||
|
help |
||||||
|
set the pending table size |
||||||
|
|
||||||
|
config IEEE802154_ESP32_MULTI_PAN_ENABLE |
||||||
|
bool "Multi-pan feature for frame filter" |
||||||
|
help |
||||||
|
Enable IEEE802154 multi-pan |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
menuconfig IEEE802154_ESP32_DEBUG |
||||||
|
bool "IEEE802154 Debug" |
||||||
|
help |
||||||
|
Enabling this option allows different kinds of IEEE802154 debug output. |
||||||
|
All IEEE802154 debug features increase the size of the final binary. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_ASSERT |
||||||
|
bool "Enrich the assert information with IEEE802154 state and event" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
default n |
||||||
|
help |
||||||
|
Enabling this option to add some probe codes in the driver, and this information |
||||||
|
will be printed when assert. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_EVENT |
||||||
|
bool "Record event information for debugging" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
help |
||||||
|
Enabling this option to record event, when assert, the recorded event will be printed. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_EVENT_SIZE |
||||||
|
int "Record event table size" |
||||||
|
depends on IEEE802154_ESP32_RECORD_EVENT |
||||||
|
range 1 50 |
||||||
|
default 30 |
||||||
|
help |
||||||
|
Set the record event table size |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_STATE |
||||||
|
bool "Record state information for debugging" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
help |
||||||
|
Enabling this option to record state, when assert, the recorded state will be printed. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_STATE_SIZE |
||||||
|
int "Record state table size" |
||||||
|
depends on IEEE802154_ESP32_RECORD_STATE |
||||||
|
range 1 50 |
||||||
|
default 10 |
||||||
|
help |
||||||
|
Set the record state table size. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_CMD |
||||||
|
bool "Record command information for debugging" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
help |
||||||
|
Enable this option to record the command information. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_CMD_SIZE |
||||||
|
int "Record command table size" |
||||||
|
depends on IEEE802154_ESP32_RECORD_CMD |
||||||
|
range 1 50 |
||||||
|
default 10 |
||||||
|
help |
||||||
|
Set the record command table size. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_ABORT |
||||||
|
bool "Record abort information for debugging" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
help |
||||||
|
Enable this option to record abort information. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_RECORD_ABORT_SIZE |
||||||
|
int "Record abort table size" |
||||||
|
depends on IEEE802154_ESP32_RECORD_ABORT |
||||||
|
range 1 50 |
||||||
|
default 10 |
||||||
|
help |
||||||
|
Set the record abort table size. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
config IEEE802154_ESP32_TXRX_STATISTIC |
||||||
|
bool "Record tx/rx packet information for debugging" |
||||||
|
depends on IEEE802154_ESP32_DEBUG |
||||||
|
help |
||||||
|
Enable this option to record tx and rx packet information. |
||||||
|
|
||||||
|
This config is used in the Espressif HAL module. |
||||||
|
|
||||||
|
endif # IEEE802154_ESP32 |
@ -0,0 +1,524 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 A Labs GmbH |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT espressif_esp32_ieee802154 |
||||||
|
|
||||||
|
#define LOG_MODULE_NAME ieee802154_esp32 |
||||||
|
#if defined(CONFIG_IEEE802154_DRIVER_LOG_LEVEL) |
||||||
|
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL |
||||||
|
#else |
||||||
|
#define LOG_LEVEL LOG_LEVEL_NONE |
||||||
|
#endif |
||||||
|
|
||||||
|
#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 <soc.h> |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/init.h> |
||||||
|
#include <zephyr/debug/stack.h> |
||||||
|
#include <zephyr/net/net_if.h> |
||||||
|
#include <zephyr/net/net_pkt.h> |
||||||
|
|
||||||
|
#if defined(CONFIG_NET_L2_OPENTHREAD) |
||||||
|
#include <zephyr/net/openthread.h> |
||||||
|
#include <zephyr/net/ieee802154_radio_openthread.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <zephyr/sys/byteorder.h> |
||||||
|
#include <string.h> |
||||||
|
#include <zephyr/random/random.h> |
||||||
|
|
||||||
|
#include <zephyr/net/ieee802154_radio.h> |
||||||
|
#include <zephyr/irq.h> |
||||||
|
|
||||||
|
#include "ieee802154_esp32.h" |
||||||
|
#include <esp_ieee802154.h> |
||||||
|
#include <esp_ieee802154_dev.h> |
||||||
|
#include <esp_mac.h> |
||||||
|
|
||||||
|
#define IEEE802154_ESP32_TX_TIMEOUT_MS (100) |
||||||
|
|
||||||
|
static struct ieee802154_esp32_data esp32_data; |
||||||
|
|
||||||
|
/* override weak function in components/ieee802154/esp_ieee802154.c of ESP-IDF */ |
||||||
|
void esp_ieee802154_receive_done(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info) |
||||||
|
{ |
||||||
|
struct net_pkt *pkt; |
||||||
|
uint8_t *payload; |
||||||
|
uint8_t len; |
||||||
|
int err; |
||||||
|
|
||||||
|
/* The ESP-IDF HAL handles FCS already and drops frames with bad checksum. The checksum at
|
||||||
|
* the end of a valid frame is replaced with RSSI and LQI values. |
||||||
|
* |
||||||
|
* ToDo: Check if L2 needs a valid checksum in the frame. |
||||||
|
*/ |
||||||
|
if (IS_ENABLED(CONFIG_IEEE802154_L2_PKT_INCL_FCS)) { |
||||||
|
len = frame[0]; |
||||||
|
} else { |
||||||
|
len = frame[0] - IEEE802154_FCS_LENGTH; |
||||||
|
} |
||||||
|
|
||||||
|
#if defined(CONFIG_NET_BUF_DATA_SIZE) |
||||||
|
__ASSERT_NO_MSG(len <= CONFIG_NET_BUF_DATA_SIZE); |
||||||
|
#endif |
||||||
|
|
||||||
|
payload = frame + 1; |
||||||
|
|
||||||
|
LOG_HEXDUMP_DBG(payload, len, "RX buffer:"); |
||||||
|
|
||||||
|
pkt = net_pkt_rx_alloc_with_buffer(esp32_data.iface, len, AF_UNSPEC, 0, K_NO_WAIT); |
||||||
|
if (!pkt) { |
||||||
|
LOG_ERR("No pkt available"); |
||||||
|
goto exit; |
||||||
|
} |
||||||
|
|
||||||
|
err = net_pkt_write(pkt, payload, len); |
||||||
|
if (err != 0) { |
||||||
|
LOG_ERR("Failed to write to a packet: %d", err); |
||||||
|
net_pkt_unref(pkt); |
||||||
|
goto exit; |
||||||
|
} |
||||||
|
|
||||||
|
net_pkt_set_ieee802154_lqi(pkt, frame_info->lqi); |
||||||
|
net_pkt_set_ieee802154_rssi_dbm(pkt, frame_info->rssi); |
||||||
|
net_pkt_set_ieee802154_ack_fpb(pkt, frame_info->pending); |
||||||
|
|
||||||
|
err = net_recv_data(esp32_data.iface, pkt); |
||||||
|
if (err != 0) { |
||||||
|
LOG_ERR("RCV Packet dropped by NET stack: %d", err); |
||||||
|
net_pkt_unref(pkt); |
||||||
|
} |
||||||
|
|
||||||
|
exit: |
||||||
|
esp_ieee802154_receive_handle_done(frame); |
||||||
|
} |
||||||
|
|
||||||
|
static enum ieee802154_hw_caps esp32_get_capabilities(const struct device *dev) |
||||||
|
{ |
||||||
|
ARG_UNUSED(dev); |
||||||
|
|
||||||
|
/*
|
||||||
|
* ESP32-C6 Datasheet: |
||||||
|
* - CSMA/CA |
||||||
|
* - active scan and energy detect |
||||||
|
* - HW frame filter |
||||||
|
* - HW auto acknowledge |
||||||
|
* - HW auto frame pending |
||||||
|
* - coordinated sampled listening (CSL) |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ToDo: Double-check and extend */ |
||||||
|
return IEEE802154_HW_ENERGY_SCAN | IEEE802154_HW_FILTER | IEEE802154_HW_TX_RX_ACK | |
||||||
|
IEEE802154_HW_CSMA; |
||||||
|
} |
||||||
|
|
||||||
|
/* override weak function in components/ieee802154/esp_ieee802154.c of ESP-IDF */ |
||||||
|
void IRAM_ATTR esp_ieee802154_cca_done(bool channel_free) |
||||||
|
{ |
||||||
|
esp32_data.channel_free = channel_free; |
||||||
|
k_sem_give(&esp32_data.cca_wait); |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_cca(const struct device *dev) |
||||||
|
{ |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
int err; |
||||||
|
|
||||||
|
if (ieee802154_cca() != 0) { |
||||||
|
LOG_DBG("CCA failed"); |
||||||
|
return -EBUSY; |
||||||
|
} |
||||||
|
|
||||||
|
err = k_sem_take(&data->cca_wait, K_MSEC(1000)); |
||||||
|
if (err == -EAGAIN) { |
||||||
|
LOG_DBG("CCA timed out"); |
||||||
|
return -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
LOG_DBG("Channel free? %d", data->channel_free); |
||||||
|
|
||||||
|
return data->channel_free ? 0 : -EBUSY; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_set_channel(const struct device *dev, uint16_t channel) |
||||||
|
{ |
||||||
|
int err; |
||||||
|
|
||||||
|
ARG_UNUSED(dev); |
||||||
|
|
||||||
|
LOG_DBG("Channel: %u", channel); |
||||||
|
|
||||||
|
if (channel > 26) { |
||||||
|
return -EINVAL; |
||||||
|
} else if (channel < 11) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
err = esp_ieee802154_set_channel(channel); |
||||||
|
|
||||||
|
return err == 0 ? 0 : -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_filter(const struct device *dev, bool set, enum ieee802154_filter_type type, |
||||||
|
const struct ieee802154_filter *filter) |
||||||
|
{ |
||||||
|
int err; |
||||||
|
|
||||||
|
LOG_DBG("Applying filter %u", type); |
||||||
|
|
||||||
|
if (!set) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
switch (type) { |
||||||
|
case IEEE802154_FILTER_TYPE_IEEE_ADDR: |
||||||
|
err = esp_ieee802154_set_extended_address(filter->ieee_addr); |
||||||
|
break; |
||||||
|
case IEEE802154_FILTER_TYPE_SHORT_ADDR: |
||||||
|
err = esp_ieee802154_set_short_address(filter->short_addr); |
||||||
|
break; |
||||||
|
case IEEE802154_FILTER_TYPE_PAN_ID: |
||||||
|
err = esp_ieee802154_set_panid(filter->pan_id); |
||||||
|
break; |
||||||
|
default: |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
return err == 0 ? 0 : -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_set_txpower(const struct device *dev, int16_t dbm) |
||||||
|
{ |
||||||
|
int err; |
||||||
|
|
||||||
|
ARG_UNUSED(dev); |
||||||
|
|
||||||
|
LOG_DBG("TX power: %u dBm", dbm); |
||||||
|
|
||||||
|
if (dbm > CONFIG_ESP32_PHY_MAX_TX_POWER) { |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
err = esp_ieee802154_set_txpower(dbm); |
||||||
|
|
||||||
|
return err == 0 ? 0 : -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
static int handle_ack(struct ieee802154_esp32_data *data) |
||||||
|
{ |
||||||
|
uint8_t ack_len; |
||||||
|
struct net_pkt *ack_pkt; |
||||||
|
int err = 0; |
||||||
|
|
||||||
|
if (data->ack_frame == NULL || data->ack_frame_info == NULL) { |
||||||
|
/* no ACK received, nothing to do */ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_IEEE802154_L2_PKT_INCL_FCS)) { |
||||||
|
ack_len = data->ack_frame[0]; |
||||||
|
} else { |
||||||
|
ack_len = data->ack_frame[0] - IEEE802154_FCS_LENGTH; |
||||||
|
} |
||||||
|
|
||||||
|
ack_pkt = net_pkt_rx_alloc_with_buffer(data->iface, ack_len, AF_UNSPEC, 0, K_NO_WAIT); |
||||||
|
if (!ack_pkt) { |
||||||
|
LOG_ERR("No free packet available."); |
||||||
|
err = -ENOMEM; |
||||||
|
goto free_esp_ack; |
||||||
|
} |
||||||
|
|
||||||
|
/* Upper layers expect the frame to start at the MAC header, skip the
|
||||||
|
* PHY header (PHR byte containing the length). |
||||||
|
*/ |
||||||
|
if (net_pkt_write(ack_pkt, data->ack_frame + 1, ack_len) < 0) { |
||||||
|
LOG_ERR("Failed to write to a packet."); |
||||||
|
err = -ENOMEM; |
||||||
|
goto free_net_ack; |
||||||
|
} |
||||||
|
|
||||||
|
net_pkt_set_ieee802154_lqi(ack_pkt, data->ack_frame_info->lqi); |
||||||
|
net_pkt_set_ieee802154_rssi_dbm(ack_pkt, data->ack_frame_info->rssi); |
||||||
|
|
||||||
|
#if defined(CONFIG_NET_PKT_TIMESTAMP) |
||||||
|
net_pkt_set_timestamp_ns(ack_pkt, data->ack_frame_info->time * NSEC_PER_USEC); |
||||||
|
#endif |
||||||
|
|
||||||
|
net_pkt_cursor_init(ack_pkt); |
||||||
|
|
||||||
|
if (ieee802154_handle_ack(data->iface, ack_pkt) != NET_OK) { |
||||||
|
LOG_INF("ACK packet not handled - releasing."); |
||||||
|
} |
||||||
|
|
||||||
|
free_net_ack: |
||||||
|
net_pkt_unref(ack_pkt); |
||||||
|
|
||||||
|
free_esp_ack: |
||||||
|
esp_ieee802154_receive_handle_done(data->ack_frame); |
||||||
|
data->ack_frame = NULL; |
||||||
|
|
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
/* override weak function in components/ieee802154/esp_ieee802154.c of ESP-IDF */ |
||||||
|
void IRAM_ATTR esp_ieee802154_transmit_done(const uint8_t *tx_frame, const uint8_t *ack_frame, |
||||||
|
esp_ieee802154_frame_info_t *ack_frame_info) |
||||||
|
{ |
||||||
|
esp32_data.ack_frame = ack_frame; |
||||||
|
esp32_data.ack_frame_info = ack_frame_info; |
||||||
|
|
||||||
|
k_sem_give(&esp32_data.tx_wait); |
||||||
|
} |
||||||
|
|
||||||
|
/* override weak function in components/ieee802154/esp_ieee802154.c of ESP-IDF */ |
||||||
|
void IRAM_ATTR esp_ieee802154_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error) |
||||||
|
{ |
||||||
|
k_sem_give(&esp32_data.tx_wait); |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_tx(const struct device *dev, enum ieee802154_tx_mode tx_mode, struct net_pkt *pkt, |
||||||
|
struct net_buf *frag) |
||||||
|
{ |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
uint8_t payload_len = frag->len; |
||||||
|
uint8_t *payload = frag->data; |
||||||
|
uint64_t net_time_us; |
||||||
|
int err; |
||||||
|
|
||||||
|
if (payload_len > IEEE802154_MTU) { |
||||||
|
LOG_ERR("Payload too large: %d", payload_len); |
||||||
|
return -EMSGSIZE; |
||||||
|
} |
||||||
|
|
||||||
|
LOG_HEXDUMP_DBG(payload, payload_len, "TX buffer:"); |
||||||
|
|
||||||
|
data->tx_psdu[0] = payload_len + IEEE802154_FCS_LENGTH; |
||||||
|
memcpy(data->tx_psdu + 1, payload, payload_len); |
||||||
|
|
||||||
|
k_sem_reset(&data->tx_wait); |
||||||
|
|
||||||
|
switch (tx_mode) { |
||||||
|
case IEEE802154_TX_MODE_DIRECT: |
||||||
|
err = esp_ieee802154_transmit(data->tx_psdu, false); |
||||||
|
break; |
||||||
|
case IEEE802154_TX_MODE_CSMA_CA: |
||||||
|
/*
|
||||||
|
* The second parameter of esp_ieee802154_transmit is called CCA, but actually |
||||||
|
* means CSMA/CA (see also ESP-IDF implementation of OpenThread interface). |
||||||
|
*/ |
||||||
|
err = esp_ieee802154_transmit(data->tx_psdu, true); |
||||||
|
break; |
||||||
|
case IEEE802154_TX_MODE_TXTIME: |
||||||
|
case IEEE802154_TX_MODE_TXTIME_CCA: |
||||||
|
/**
|
||||||
|
* The Espressif HAL functions seem to expect a system uptime in us stored as |
||||||
|
* uint32_t, which would overflow already after 1.2 hours. In addition to that, the |
||||||
|
* network time from PTP, which is returned by net_pkt_timestamp_ns, will most |
||||||
|
* probably have a different basis. Anyway, time-based transfers are required for |
||||||
|
* some Thread features, so this will have to be fixed in the future. |
||||||
|
* |
||||||
|
* See also: |
||||||
|
* - include/zephyr/net/net_time.h |
||||||
|
* - ../modules/hal/espressif/components/ieee802154/driver/esp_ieee802154_dev.c |
||||||
|
*/ |
||||||
|
net_time_us = net_pkt_timestamp_ns(pkt) / NSEC_PER_USEC; |
||||||
|
err = esp_ieee802154_transmit_at(data->tx_psdu, |
||||||
|
tx_mode == IEEE802154_TX_MODE_TXTIME_CCA, |
||||||
|
(uint32_t)net_time_us); |
||||||
|
break; |
||||||
|
default: |
||||||
|
LOG_ERR("TX mode %d not supported", tx_mode); |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
err = k_sem_take(&data->tx_wait, K_MSEC(IEEE802154_ESP32_TX_TIMEOUT_MS)); |
||||||
|
|
||||||
|
/* set the radio back to RX mode as quickly as possible */ |
||||||
|
ieee802154_receive(); |
||||||
|
|
||||||
|
if (err != 0) { |
||||||
|
LOG_ERR("TX timeout"); |
||||||
|
} else { |
||||||
|
handle_ack(data); |
||||||
|
} |
||||||
|
|
||||||
|
return err == 0 ? 0 : -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_start(const struct device *dev) |
||||||
|
{ |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
|
||||||
|
if (data->is_started) { |
||||||
|
return 0; |
||||||
|
} else if (esp_ieee802154_enable() == 0) { |
||||||
|
esp_ieee802154_set_promiscuous(false); |
||||||
|
esp_ieee802154_set_rx_when_idle(true); |
||||||
|
|
||||||
|
/* ToDo: check if this is necessary */ |
||||||
|
esp_ieee802154_receive(); |
||||||
|
|
||||||
|
data->is_started = true; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_stop(const struct device *dev) |
||||||
|
{ |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
|
||||||
|
if (!data->is_started) { |
||||||
|
return 0; |
||||||
|
} else if (esp_ieee802154_disable() == 0) { |
||||||
|
data->is_started = false; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return -EIO; |
||||||
|
} |
||||||
|
|
||||||
|
/* override weak function in components/ieee802154/esp_ieee802154.c of ESP-IDF */ |
||||||
|
void IRAM_ATTR esp_ieee802154_energy_detect_done(int8_t power) |
||||||
|
{ |
||||||
|
energy_scan_done_cb_t callback; |
||||||
|
const struct device *dev; |
||||||
|
|
||||||
|
if (esp32_data.energy_scan_done == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
callback = esp32_data.energy_scan_done; |
||||||
|
esp32_data.energy_scan_done = NULL; |
||||||
|
dev = net_if_get_device(esp32_data.iface); |
||||||
|
callback(dev, power); /* TODO: check scaling */ |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_ed_scan(const struct device *dev, uint16_t duration, energy_scan_done_cb_t done_cb) |
||||||
|
{ |
||||||
|
ARG_UNUSED(dev); |
||||||
|
|
||||||
|
int err = 0; |
||||||
|
|
||||||
|
if (esp32_data.energy_scan_done == NULL) { |
||||||
|
esp32_data.energy_scan_done = done_cb; |
||||||
|
|
||||||
|
/* The duration of energy detection, in symbol unit (16 us). TODO: check scaling */ |
||||||
|
if (esp_ieee802154_energy_detect(duration * USEC_PER_MSEC / US_PER_SYMBLE) != 0) { |
||||||
|
esp32_data.energy_scan_done = NULL; |
||||||
|
err = -EBUSY; |
||||||
|
} |
||||||
|
} else { |
||||||
|
err = -EALREADY; |
||||||
|
} |
||||||
|
|
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_configure(const struct device *dev, enum ieee802154_config_type type, |
||||||
|
const struct ieee802154_config *config) |
||||||
|
{ |
||||||
|
ARG_UNUSED(dev); |
||||||
|
ARG_UNUSED(config); |
||||||
|
|
||||||
|
switch (type) { |
||||||
|
/* IEEE802154_CONFIG_ACK_FPB */ |
||||||
|
/* IEEE802154_CONFIG_EVENT_HANDLER */ |
||||||
|
case IEEE802154_CONFIG_PROMISCUOUS: |
||||||
|
esp_ieee802154_set_promiscuous(config->promiscuous); |
||||||
|
break; |
||||||
|
case IEEE802154_CONFIG_RX_ON_WHEN_IDLE: |
||||||
|
esp_ieee802154_set_rx_when_idle(config->rx_on_when_idle); |
||||||
|
break; |
||||||
|
default: |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* driver-allocated attribute memory - constant across all driver instances */ |
||||||
|
IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 11, 26); |
||||||
|
|
||||||
|
static int esp32_attr_get(const struct device *dev, enum ieee802154_attr attr, |
||||||
|
struct ieee802154_attr_value *value) |
||||||
|
{ |
||||||
|
ARG_UNUSED(dev); |
||||||
|
|
||||||
|
return ieee802154_attr_get_channel_page_and_range( |
||||||
|
attr, IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915, |
||||||
|
&drv_attr.phy_supported_channels, value); |
||||||
|
} |
||||||
|
|
||||||
|
static int esp32_init(const struct device *dev) |
||||||
|
{ |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
|
||||||
|
k_sem_init(&data->cca_wait, 0, 1); |
||||||
|
k_sem_init(&data->tx_wait, 0, 1); |
||||||
|
|
||||||
|
LOG_INF("IEEE 802154 radio initialized"); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void esp32_iface_init(struct net_if *iface) |
||||||
|
{ |
||||||
|
const struct device *dev = net_if_get_device(iface); |
||||||
|
struct ieee802154_esp32_data *data = dev->data; |
||||||
|
|
||||||
|
esp_efuse_mac_get_default(data->mac); |
||||||
|
net_if_set_link_addr(iface, data->mac, sizeof(data->mac), NET_LINK_IEEE802154); |
||||||
|
|
||||||
|
data->iface = iface; |
||||||
|
|
||||||
|
ieee802154_init(iface); |
||||||
|
} |
||||||
|
|
||||||
|
static const struct ieee802154_radio_api esp32_radio_api = { |
||||||
|
.iface_api.init = esp32_iface_init, |
||||||
|
|
||||||
|
.get_capabilities = esp32_get_capabilities, |
||||||
|
.cca = esp32_cca, |
||||||
|
.set_channel = esp32_set_channel, |
||||||
|
.filter = esp32_filter, |
||||||
|
.set_txpower = esp32_set_txpower, |
||||||
|
.tx = esp32_tx, |
||||||
|
.start = esp32_start, |
||||||
|
.stop = esp32_stop, |
||||||
|
.ed_scan = esp32_ed_scan, |
||||||
|
.configure = esp32_configure, |
||||||
|
.attr_get = esp32_attr_get, |
||||||
|
}; |
||||||
|
|
||||||
|
#if defined(CONFIG_NET_L2_IEEE802154) |
||||||
|
#define L2 IEEE802154_L2 |
||||||
|
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2) |
||||||
|
#define MTU IEEE802154_MTU |
||||||
|
#elif defined(CONFIG_NET_L2_OPENTHREAD) |
||||||
|
#define L2 OPENTHREAD_L2 |
||||||
|
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2) |
||||||
|
#define MTU 1280 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(CONFIG_NET_L2_PHY_IEEE802154) |
||||||
|
NET_DEVICE_DT_INST_DEFINE(0, esp32_init, NULL, &esp32_data, NULL, |
||||||
|
CONFIG_IEEE802154_ESP32_INIT_PRIO, &esp32_radio_api, L2, |
||||||
|
L2_CTX_TYPE, MTU); |
||||||
|
#else |
||||||
|
DEVICE_DT_INST_DEFINE(0, esp32_init, NULL, &esp32_data, NULL, POST_KERNEL, |
||||||
|
CONFIG_IEEE802154_ESP32_INIT_PRIO, &esp32_radio_api); |
||||||
|
#endif |
@ -0,0 +1,56 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 A Labs GmbH |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_IEEE802154_IEEE802154_ESP32_H_ |
||||||
|
#define ZEPHYR_DRIVERS_IEEE802154_IEEE802154_ESP32_H_ |
||||||
|
|
||||||
|
#include <esp_ieee802154_types.h> |
||||||
|
|
||||||
|
#include <zephyr/net/ieee802154_radio.h> |
||||||
|
|
||||||
|
struct ieee802154_esp32_data { |
||||||
|
/* Pointer to the network interface. */ |
||||||
|
struct net_if *iface; |
||||||
|
|
||||||
|
/* 802.15.4 HW address. */ |
||||||
|
uint8_t mac[8]; |
||||||
|
|
||||||
|
/* CCA complete semaphore. Unlocked when CCA is complete. */ |
||||||
|
struct k_sem cca_wait; |
||||||
|
|
||||||
|
/* CCA result. Holds information whether channel is free or not. */ |
||||||
|
bool channel_free; |
||||||
|
|
||||||
|
bool is_started; |
||||||
|
|
||||||
|
/* TX synchronization semaphore. Unlocked when frame has been
|
||||||
|
* sent or send procedure failed. |
||||||
|
*/ |
||||||
|
struct k_sem tx_wait; |
||||||
|
|
||||||
|
/* TX buffer. First byte is PHR (length), remaining bytes are
|
||||||
|
* MPDU data. |
||||||
|
*/ |
||||||
|
uint8_t tx_psdu[1 + IEEE802154_MAX_PHY_PACKET_SIZE]; |
||||||
|
|
||||||
|
/*
|
||||||
|
* ACK frame (stored until esp_ieee802154_receive_handle_done is called) |
||||||
|
* First byte is frame length (PHR), followed by payload (PSDU) |
||||||
|
*/ |
||||||
|
const uint8_t *ack_frame; |
||||||
|
|
||||||
|
/* A buffer for the received ACK frame. psdu pointer be NULL if no
|
||||||
|
* ACK was requested/received. |
||||||
|
*/ |
||||||
|
esp_ieee802154_frame_info_t *ack_frame_info; |
||||||
|
|
||||||
|
/* Callback handler of the currently ongoing energy scan.
|
||||||
|
* It shall be NULL if energy scan is not in progress. |
||||||
|
*/ |
||||||
|
energy_scan_done_cb_t energy_scan_done; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif /* ZEPHYR_DRIVERS_IEEE802154_IEEE802154_ESP32_H_ */ |
@ -0,0 +1,8 @@ |
|||||||
|
# Copyright (c) 2024 A Labs GmbH |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
description: Espressif ESP32 IEEE 802.15.4 node |
||||||
|
|
||||||
|
compatible: "espressif,esp32-ieee802154" |
||||||
|
|
||||||
|
include: base.yaml |
Loading…
Reference in new issue