diff --git a/Makefile.dep b/Makefile.dep index b48a10e402..fc4c8c22d8 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -108,6 +108,11 @@ ifneq (,$(filter netdev_ieee802154,$(USEMODULE))) USEMODULE += random endif +ifneq (,$(filter netdev_ieee802154_submac,$(USEMODULE))) + USEMODULE += ieee802154_radio_hal + USEMODULE += ieee802154_submac +endif + ifneq (,$(filter gnrc_dhcpv6_%, $(USEMODULE))) USEMODULE += gnrc_dhcpv6 endif @@ -191,6 +196,9 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE))) USEMODULE += netif USEMODULE += l2util USEMODULE += fmt + ifneq (,$(filter netdev_ieee802154_submac,$(USEMODULE))) + USEMODULE += gnrc_netif_pktq + endif ifneq (,$(filter netdev_ieee802154,$(USEMODULE))) USEMODULE += gnrc_netif_ieee802154 endif diff --git a/drivers/include/net/netdev/ieee802154_submac.h b/drivers/include/net/netdev/ieee802154_submac.h new file mode 100644 index 0000000000..c78862962d --- /dev/null +++ b/drivers/include/net/netdev/ieee802154_submac.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * 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. + */ + +/** + * @defgroup drivers_netdev_ieee802154_submac IEEE802.15.4 SubMAC netdev layer + * @ingroup drivers_netdev_api + * @experimental This API is experimental and in an early state - expect + * changes! + + * @brief This module defines implements the netdev API on top of the + * IEEE 802.15.4 radio HAL + * + * @{ + * + * @author José I. Alamos + */ +#ifndef NET_NETDEV_IEEE802154_SUBMAC_H +#define NET_NETDEV_IEEE802154_SUBMAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/netdev.h" +#include "net/netdev/ieee802154.h" +#include "net/ieee802154/submac.h" +#include "net/ieee802154/radio.h" +#include "xtimer.h" + +#include "od.h" +#include "event/thread.h" +#include "event/callback.h" +#include "xtimer.h" + +#define NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT (1 << 0) /**< Flag for ACK Timeout event */ +#define NETDEV_SUBMAC_FLAGS_TX_DONE (1 << 1) /**< Flag for TX Done event */ +#define NETDEV_SUBMAC_FLAGS_RX_DONE (1 << 2) /**< Flag for RX Done event */ + +/** + * @brief IEEE 802.15.4 SubMAC netdev descriptor + */ +typedef struct { + netdev_ieee802154_t dev; /**< IEEE 802.15.4 netdev descriptor */ + ieee802154_submac_t submac; /**< IEEE 802.15.4 SubMAC descriptor */ + xtimer_t ack_timer; /**< xtimer descriptor for the ACK timeout timer */ + int isr_flags; /**< netdev submac @ref NETDEV_EVENT_ISR flags */ +} netdev_ieee802154_submac_t; + +/** + * @brief Init the IEEE 802.15.4 SubMAC netdev adoption. + * + * @param[in] netdev_submac pointer to the netdev submac descriptor. + * @param[in] dev pointer to the device associated to @p netdev_submac. + * + * @return 0 on success. + * @return negative errno on failure. + */ +int netdev_ieee802154_submac_init(netdev_ieee802154_submac_t *netdev_submac, + ieee802154_dev_t *dev); +#ifdef __cplusplus +} +#endif + +#endif /* NET_NETDEV_IEEE802154_SUBMAC_H */ +/** @} */ diff --git a/drivers/netdev_ieee802154_submac/Makefile b/drivers/netdev_ieee802154_submac/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/netdev_ieee802154_submac/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c new file mode 100644 index 0000000000..5c231403e0 --- /dev/null +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * 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. + */ + +/** + * @{ + * + * @file + * @author José I. Alamos + */ + +#include "net/netdev/ieee802154_submac.h" +#include "event/thread.h" + +static const ieee802154_submac_cb_t _cb; + +static const netdev_driver_t netdev_submac_driver; + +static void _ack_timeout(void *arg) +{ + (void)arg; + netdev_ieee802154_submac_t *netdev_submac = arg; + netdev_t *netdev = arg; + + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT; + + netdev->event_callback(netdev, NETDEV_EVENT_ISR); +} + +static netopt_state_t _get_submac_state(ieee802154_submac_t *submac) +{ + ieee802154_submac_state_t state = ieee802154_get_state(submac); + + netopt_state_t netopt_state; + switch (state) { + case IEEE802154_STATE_OFF: + netopt_state = NETOPT_STATE_SLEEP; + break; + case IEEE802154_STATE_IDLE: + netopt_state = NETOPT_STATE_STANDBY; + break; + case IEEE802154_STATE_LISTEN: + default: + netopt_state = NETOPT_STATE_IDLE; + break; + } + + return netopt_state; +} + +static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len) +{ + netdev_ieee802154_submac_t *netdev_submac = (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + switch (opt) { + case NETOPT_STATE: + *((netopt_state_t*) value) = _get_submac_state(submac); + return 0; + default: + break; + } + + return netdev_ieee802154_get((netdev_ieee802154_t *)netdev, opt, + value, max_len); +} + +static int _set_submac_state(ieee802154_submac_t *submac, netopt_state_t state) +{ + switch (state) { + case NETOPT_STATE_STANDBY: + return ieee802154_set_state(submac, IEEE802154_STATE_IDLE); + case NETOPT_STATE_SLEEP: + return ieee802154_set_state(submac, IEEE802154_STATE_OFF); + case NETOPT_STATE_IDLE: + return ieee802154_set_state(submac, IEEE802154_STATE_LISTEN); + default: + return -ENOTSUP; + } +} + +static int _set(netdev_t *netdev, netopt_t opt, const void *value, + size_t value_len) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + int res; + int16_t tx_power; + + switch (opt) { + case NETOPT_ADDRESS: + ieee802154_set_short_addr(submac, value); + break; + case NETOPT_ADDRESS_LONG: + ieee802154_set_ext_addr(submac, value); + break; + case NETOPT_NID: + ieee802154_set_panid(submac, value); + break; + case NETOPT_CHANNEL: + ieee802154_set_channel_number(submac, *((uint16_t *)value)); + break; + case NETOPT_TX_POWER: + tx_power = *((int16_t *)value); + res = ieee802154_set_tx_power(submac, tx_power); + if (res >= 0) { + netdev_submac->dev.txpower = tx_power; + } + return res; + case NETOPT_STATE: + return _set_submac_state(submac, *((netopt_state_t*) value)); + default: + break; + } + + return netdev_ieee802154_set((netdev_ieee802154_t *)netdev, opt, + value, value_len); +} + +void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, uint16_t us) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + + xtimer_set(&netdev_submac->ack_timer, us); +} + +void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + + xtimer_remove(&netdev_submac->ack_timer); +} + +static int _send(netdev_t *netdev, const iolist_t *pkt) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + + return ieee802154_send(&netdev_submac->submac, pkt); +} + +static void _isr(netdev_t *netdev) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + do { + irq_disable(); + int flags = netdev_submac->isr_flags; + netdev_submac->isr_flags = 0; + irq_enable(); + + if (flags & NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT) { + ieee802154_submac_ack_timeout_fired(&netdev_submac->submac); + } + + if (flags & NETDEV_SUBMAC_FLAGS_TX_DONE) { + ieee802154_submac_tx_done_cb(&netdev_submac->submac); + } + + if (flags & NETDEV_SUBMAC_FLAGS_RX_DONE) { + ieee802154_submac_rx_done_cb(submac); + } + } while (netdev_submac->isr_flags != 0); +} + +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + ieee802154_rx_info_t rx_info; + + if (buf == NULL && len == 0) { + return ieee802154_get_frame_length(submac); + } + + int res = ieee802154_read_frame(submac, buf, len, &rx_info); + + if (info) { + netdev_ieee802154_rx_info_t *netdev_rx_info = info; + netdev_rx_info->rssi = rx_info.rssi; + netdev_rx_info->lqi = rx_info.lqi; + } + + return res; +} + +static void submac_tx_done(ieee802154_submac_t *submac, int status, + ieee802154_tx_info_t *info) +{ + (void)status; + (void)info; + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + switch (status) { + case TX_STATUS_SUCCESS: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + break; + case TX_STATUS_FRAME_PENDING: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE_DATA_PENDING); + break; + case TX_STATUS_MEDIUM_BUSY: + netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + break; + case TX_STATUS_NO_ACK: + netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK); + break; + default: + break; + } +} + +static void submac_rx_done(ieee802154_submac_t *submac) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); +} + +static const ieee802154_submac_cb_t _cb = { + .rx_done = submac_rx_done, + .tx_done = submac_tx_done, +}; + +/* Event Notification callback */ +static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status) +{ + ieee802154_submac_t *submac = dev->ctx; + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + switch (status) { + case IEEE802154_RADIO_CONFIRM_TX_DONE: + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_TX_DONE; + break; + case IEEE802154_RADIO_INDICATION_RX_DONE: + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_RX_DONE; + default: + break; + } + netdev->event_callback(netdev, NETDEV_EVENT_ISR); +} + +static int _init(netdev_t *netdev) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + /* Call the init function of the device (this will be handled by + * `auto_init`) */ + + ieee802154_submac_t *submac = &netdev_submac->submac; + + ieee802154_submac_init(submac); + + netdev_ieee802154_t *netdev_ieee802154 = (netdev_ieee802154_t *)netdev; + + /* This function already sets the PAN ID to the default one */ + netdev_ieee802154_reset(netdev_ieee802154); + + uint16_t chan = CONFIG_IEEE802154_DEFAULT_CHANNEL; + int16_t tx_power = CONFIG_IEEE802154_DEFAULT_TXPOWER; + + /* Initialise netdev_ieee802154_t struct */ + netdev_ieee802154_set(netdev_ieee802154, NETOPT_CHANNEL, + &chan, sizeof(chan)); + netdev_ieee802154_set(netdev_ieee802154, NETOPT_ADDRESS, + &submac->short_addr, sizeof(submac->short_addr)); + netdev_ieee802154_set(netdev_ieee802154, NETOPT_ADDRESS_LONG, + &submac->ext_addr, sizeof(submac->ext_addr)); + + netdev_submac->dev.txpower = tx_power; + + return 0; +} + +int netdev_ieee802154_submac_init(netdev_ieee802154_submac_t *netdev_submac, + ieee802154_dev_t *dev) +{ + netdev_t *netdev = (netdev_t *)netdev_submac; + + netdev->driver = &netdev_submac_driver; + ieee802154_submac_t *submac = &netdev_submac->submac; + + submac->dev = dev; + submac->cb = &_cb; + submac->dev->ctx = submac; + + /* Set the Event Notification */ + submac->dev->cb = _hal_radio_cb; + + netdev_submac->ack_timer.callback = _ack_timeout; + netdev_submac->ack_timer.arg = netdev_submac; + + return 0; +} + +static const netdev_driver_t netdev_submac_driver = { + .get = _get, + .set = _set, + .send = _send, + .recv = _recv, + .isr = _isr, + .init = _init, +}; + +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 42f09ab9e9..71942c9f60 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -128,6 +128,7 @@ PSEUDOMODULES += ztimer% # ztimer's main module is called "ztimer_core" NO_PSEUDOMODULES += ztimer_core +NO_PSEUDOMODULES += netdev_ieee802154_submac # print ascii representation in function od_hex_dump() PSEUDOMODULES += od_string