/* * 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; case NETOPT_TX_RETRIES_NEEDED: if (netdev_submac->retrans < 0) { return -ENOTSUP; } *((uint8_t*) value) = netdev_submac->retrans; return 1; case NETOPT_IEEE802154_PHY: *((uint8_t*) value) = ieee802154_get_phy_mode(submac); return 1; 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); } if (flags & NETDEV_SUBMAC_FLAGS_CRC_ERROR) { ieee802154_submac_crc_error_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; /* The Radio HAL uses the IEEE 802.15.4 definition for RSSI. * Netdev uses dBm. Therefore we need a translation here */ netdev_rx_info->rssi = ieee802154_rssi_to_dbm(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; netdev_ieee802154_submac_t *netdev_submac = container_of(submac, netdev_ieee802154_submac_t, submac); netdev_t *netdev = (netdev_t *)netdev_submac; if (info) { netdev_submac->retrans = info->retrans; } 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; break; case IEEE802154_RADIO_INDICATION_CRC_ERROR: netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_CRC_ERROR; break; 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; ieee802154_submac_t *submac = &netdev_submac->submac; netdev_ieee802154_t *netdev_ieee802154 = (netdev_ieee802154_t *)netdev; ieee802154_submac_init(submac, (network_uint16_t*) netdev_ieee802154->short_addr, (eui64_t*) netdev_ieee802154->long_addr); 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; 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; netopt_enable_t enable = NETOPT_ENABLE; netdev_ieee802154_setup(netdev_ieee802154); /* Initialise netdev_ieee802154_t struct */ netdev_ieee802154_set(netdev_ieee802154, NETOPT_CHANNEL, &chan, sizeof(chan)); netdev_ieee802154_set(netdev_ieee802154, NETOPT_ACK_REQ, &enable, sizeof(enable)); netdev_submac->dev.txpower = tx_power; return 0; } static const netdev_driver_t netdev_submac_driver = { .get = _get, .set = _set, .send = _send, .recv = _recv, .isr = _isr, .init = _init, }; /** @} */