diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index ee01562838..532ad0fe3f 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -70,6 +70,11 @@ ifneq (,$(filter at86rf215%,$(USEMODULE))) DEFAULT_MODULE += auto_init_at86rf215 DEFAULT_MODULE += at86rf215_subghz + DEFAULT_MODULE += netdev_ieee802154_multimode + + DEFAULT_MODULE += netdev_ieee802154_oqpsk + DEFAULT_MODULE += netdev_ieee802154_mr_oqpsk + ifeq (,$(filter at86rf215m,$(USEMODULE))) DEFAULT_MODULE += at86rf215_24ghz endif diff --git a/drivers/at86rf215/at86rf215.c b/drivers/at86rf215/at86rf215.c index 2cfbe72f45..ae6428dd48 100644 --- a/drivers/at86rf215/at86rf215.c +++ b/drivers/at86rf215/at86rf215.c @@ -17,7 +17,6 @@ * @} */ - #include "luid.h" #include "byteorder.h" #include "net/ieee802154.h" @@ -145,8 +144,13 @@ if (!IS_ACTIVE(CONFIG_AT86RF215_USE_CLOCK_OUTPUT)){ at86rf215_reg_write(dev, dev->BBC->RG_AMCS, reg); - /* set compatibility with first-gen 802.15.4 devices */ - at86rf215_configure_legacy_OQPSK(dev, 0); + if (AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_OQPSK) { + at86rf215_configure_legacy_OQPSK(dev, 0); + } + if (AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_OQPSK) { + at86rf215_configure_OQPSK(dev, AT86RF215_DEFAULT_MR_OQPSK_CHIPS, + AT86RF215_DEFAULT_MR_OQPSK_RATE); + } /* set default channel */ at86rf215_set_chan(dev, dev->netdev.chan); diff --git a/drivers/at86rf215/at86rf215_getset.c b/drivers/at86rf215/at86rf215_getset.c index 9016340916..940149267b 100644 --- a/drivers/at86rf215/at86rf215_getset.c +++ b/drivers/at86rf215/at86rf215_getset.c @@ -114,6 +114,18 @@ uint16_t at86rf215_get_channel_spacing(at86rf215_t *dev) { return 25 * at86rf215_reg_read(dev, dev->RF->RG_CS); } +uint8_t at86rf215_get_phy_mode(at86rf215_t *dev) +{ + switch (at86rf215_reg_read(dev, dev->BBC->RG_PC) & PC_PT_MASK) { + case 0x1: return IEEE802154_PHY_MR_FSK; + case 0x2: return IEEE802154_PHY_MR_OFDM; + case 0x3: return at86rf215_OQPSK_is_legacy(dev) + ? IEEE802154_PHY_OQPSK + : IEEE802154_PHY_MR_OQPSK; + default: return IEEE802154_PHY_DISABLED; + } +} + uint16_t at86rf215_get_pan(const at86rf215_t *dev, uint8_t filter) { if (filter > 3) { diff --git a/drivers/at86rf215/at86rf215_netdev.c b/drivers/at86rf215/at86rf215_netdev.c index c42b5a7816..8d34160608 100644 --- a/drivers/at86rf215/at86rf215_netdev.c +++ b/drivers/at86rf215/at86rf215_netdev.c @@ -327,6 +327,10 @@ static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) case NETOPT_CHANNEL_PAGE: assert(max_len >= sizeof(uint16_t)); + if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) { + return -ENOTSUP; + } + ((uint8_t *)val)[1] = 0; ((uint8_t *)val)[0] = is_subGHz(dev) ? 2 : 0; return sizeof(uint16_t); @@ -384,6 +388,35 @@ static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) res = max_len; break; + case NETOPT_IEEE802154_PHY: + assert(max_len >= sizeof(int8_t)); + *((int8_t *)val) = at86rf215_get_phy_mode(dev); + res = max_len; + break; + + case NETOPT_MR_OQPSK_CHIPS: + assert(max_len >= sizeof(int16_t)); + switch (at86rf215_OQPSK_get_chips(dev)) { + case 0: *((int16_t *)val) = 100; break; + case 1: *((int16_t *)val) = 200; break; + case 2: *((int16_t *)val) = 1000; break; + case 3: *((int16_t *)val) = 2000; break; + } + res = max_len; + break; + + case NETOPT_MR_OQPSK_RATE: + assert(max_len >= sizeof(int8_t)); + *((int8_t *)val) = at86rf215_OQPSK_get_mode(dev); + res = max_len; + break; + + case NETOPT_OQPSK_RATE: + assert(max_len >= sizeof(int8_t)); + *((int8_t *)val) = at86rf215_OQPSK_get_mode_legacy(dev); + res = max_len; + break; + default: res = -ENOTSUP; break; @@ -528,6 +561,77 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len) res = sizeof(int8_t); break; + case NETOPT_IEEE802154_PHY: + assert(len <= sizeof(uint8_t)); + switch (*(uint8_t *)val) { + case IEEE802154_PHY_OQPSK: + at86rf215_configure_legacy_OQPSK(dev, at86rf215_OQPSK_get_mode_legacy(dev)); + res = sizeof(uint8_t); + break; + case IEEE802154_PHY_MR_OQPSK: + at86rf215_configure_OQPSK(dev, + at86rf215_OQPSK_get_chips(dev), + at86rf215_OQPSK_get_mode(dev)); + res = sizeof(uint8_t); + break; + default: + return -ENOTSUP; + } + break; + + case NETOPT_MR_OQPSK_CHIPS: + if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) { + return -ENOTSUP; + } + + uint8_t chips; + assert(len <= sizeof(uint16_t)); + if (*((const uint16_t *)val) == 100) { + chips = 0; + } else if (*((const uint16_t *)val) == 200) { + chips = 1; + } else if (*((const uint16_t *)val) == 1000) { + chips = 2; + } else if (*((const uint16_t *)val) == 2000) { + chips = 3; + } else { + res = -EINVAL; + break; + } + + if (at86rf215_OQPSK_set_chips(dev, chips) == 0) { + res = sizeof(uint8_t); + } else { + res = -ERANGE; + } + break; + + case NETOPT_MR_OQPSK_RATE: + if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) { + return -ENOTSUP; + } + + assert(len <= sizeof(uint8_t)); + if (at86rf215_OQPSK_set_mode(dev, *(uint8_t *)val) == 0) { + res = sizeof(uint8_t); + } else { + res = -ERANGE; + } + break; + + case NETOPT_OQPSK_RATE: + if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) { + return -ENOTSUP; + } + + assert(len <= sizeof(uint8_t)); + if (at86rf215_OQPSK_set_mode_legacy(dev, *(uint8_t *)val) == 0) { + res = sizeof(uint8_t); + } else { + res = -ERANGE; + } + break; + default: break; } diff --git a/drivers/at86rf215/at86rf215_o-qpsk.c b/drivers/at86rf215/at86rf215_o-qpsk.c index c978eaabf3..782a558706 100644 --- a/drivers/at86rf215/at86rf215_o-qpsk.c +++ b/drivers/at86rf215/at86rf215_o-qpsk.c @@ -34,6 +34,8 @@ #define QPSK_CHANNEL_SPACING_24GHZ (5000U) /* kHz */ #define QPSK_CENTER_FREQUENCY_24GHZ (2350000U - CCF0_24G_OFFSET) /* Hz */ +#define LEGACY_QPSK_SYMBOL_TIME_US (16) + /* Table 6-103. O-QPSK Transmitter Frontend Configuration */ static uint8_t _TXCUTC_PARAMP(uint8_t chips) { @@ -148,20 +150,88 @@ static inline uint8_t _AGCC(uint8_t chips) } } -/* Table 6-100. MR-O-QPSK Modes */ -static uint32_t _get_bitrate(uint8_t chips, uint8_t mode) +static inline uint16_t _get_symbol_duration_us(uint8_t chips) { + /* 802.15.4g, Table 183 / Table 165 */ switch (chips) { case BB_FCHIP100: - return 6250 * (1 << mode); + return 320; case BB_FCHIP200: - return 12500 * (1 << mode); + return 160; case BB_FCHIP1000: case BB_FCHIP2000: - return mode ? 125000 * (1 << (mode - 1)) : 31250; + default: + return 64; + } +} + +static inline uint8_t _get_cca_duration_syms(uint8_t chips) +{ + /* 802.15.4g, Table 188 */ + return (chips < BB_FCHIP1000) ? 4 : 8; +} + +static inline uint8_t _get_shr_duration_syms(uint8_t chips) +{ + /* 802.15.4g, Table 184 / Table 165 */ + return (chips < BB_FCHIP1000) ? 48 : 72; +} + +static uint8_t _get_spreading(uint8_t chips, uint8_t mode) +{ + if (mode == 4) { + return 1; } - return 0; + uint8_t spread = 1 << (3 - mode); + + if (chips == BB_FCHIP1000) { + return 2 * spread; + } + + if (chips == BB_FCHIP2000) { + return 4 * spread; + } + + return spread; +} + +static inline uint8_t _get_ack_psdu_duration_syms(uint8_t chips, uint8_t mode) +{ + /* pg. 119, section 18.3.2.14 */ + static const uint8_t sym_len[] = { 32, 32, 64, 128 }; + const uint8_t Ns = sym_len[chips]; + const uint8_t Rspread = _get_spreading(chips, mode); + /* Nd == 63, since ACK length is 5 or 7 octects only */ + const uint16_t Npsdu = Rspread * 2 * 63; + + /* phyPSDUDuration = ceiling(Npsdu / Ns) + ceiling(Npsdu / Mp) */ + /* with Mp = Np * 16, see Table 182 */ + return (Npsdu + Ns/2) / Ns + (Npsdu + 8 * Ns) / (16 * Ns); +} + +static uint8_t _set_mode(at86rf215_t *dev, uint8_t mode) +{ + mode = AT86RF215_MR_OQPSK_MODE(mode); + + /* TX with selected rate mode */ + at86rf215_reg_write(dev, dev->BBC->RG_OQPSKPHRTX, mode); + + /* power save mode only works when not listening to legacy frames */ + /* listening to both uses ~1mA more that just listening to legacy */ + /* TODO: make this configurable */ + uint8_t rxm = RXM_MR_OQPSK; + + if (dev->flags & AT86RF215_OPT_RPC) { + rxm |= OQPSKC2_RPC_MASK; /* enable Reduced Power Consumption */ + } + + at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC2, + rxm /* receive mode, MR-O-QPSK */ + | OQPSKC2_FCSTLEG_MASK /* 16 bit frame checksum */ + | OQPSKC2_ENPROP_MASK); /* enable RX of proprietary modes */ + + return mode; } static void _set_chips(at86rf215_t *dev, uint8_t chips) @@ -222,18 +292,38 @@ static void _set_legacy(at86rf215_t *dev, bool high_rate) | OQPSKC2_ENPROP_MASK); /* enable RX of proprietary modes */ } +static inline void _set_ack_timeout_legacy(at86rf215_t *dev) +{ + dev->ack_timeout_usec = AT86RF215_ACK_PERIOD_IN_SYMBOLS * LEGACY_QPSK_SYMBOL_TIME_US; + DEBUG("[%s] ACK timeout: %"PRIu32" µs\n", "legacy O-QPSK", dev->ack_timeout_usec); +} + static void _set_ack_timeout(at86rf215_t *dev, uint8_t chips, uint8_t mode) { - dev->ack_timeout_usec = AT86RF215_ACK_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode); + /* see 802.15.4g-2012, p. 30 */ + uint16_t symbols = _get_cca_duration_syms(chips) + + _get_shr_duration_syms(chips) + + 15 /* PHR duration */ + + _get_ack_psdu_duration_syms(chips, mode); + + dev->ack_timeout_usec = _get_symbol_duration_us(chips) * symbols + + IEEE802154G_ATURNAROUNDTIME_US; + DEBUG("[%s] ACK timeout: %"PRIu32" µs\n", "O-QPSK", dev->ack_timeout_usec); } -static void _set_csma_backoff_period(at86rf215_t *dev, uint8_t chips, uint8_t mode) +static inline void _set_csma_backoff_period(at86rf215_t *dev, uint8_t chips) { - dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode); + dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * _get_symbol_duration_us(chips); DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "O-QPSK", dev->csma_backoff_period); } +static inline void _set_csma_backoff_period_legacy(at86rf215_t *dev) +{ + dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * LEGACY_QPSK_SYMBOL_TIME_US; + DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "legacy O-QPSK", dev->csma_backoff_period); +} + void _end_configure_OQPSK(at86rf215_t *dev) { /* set channel spacing with 25 kHz resolution */ @@ -256,22 +346,127 @@ void _end_configure_OQPSK(at86rf215_t *dev) at86rf215_enable_radio(dev, BB_MROQPSK); } -int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate) +int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t mode) { - /* select 'mode' that would result in the approprate MR-O-QPSK data rate */ - uint8_t mode = high_rate ? 3 : 2; - uint8_t chips = is_subGHz(dev) ? BB_FCHIP1000 : BB_FCHIP2000; + if (chips > BB_FCHIP2000) { + DEBUG("[%s] invalid chips: %d\n", __func__, chips); + return -EINVAL; + } + + if (mode > 4) { + DEBUG("[%s] invalid mode: %d\n", __func__, mode); + return -EINVAL; + } + + /* mode 4 only supports 2000 kchip/s */ + if (mode == 4 && chips != BB_FCHIP2000) { + DEBUG("[%s] mode 4 only supports 2000 kChip/s\n", __func__); + return -EINVAL; + } at86rf215_await_state_end(dev, RF_STATE_TX); /* disable radio */ at86rf215_reg_write(dev, dev->BBC->RG_PC, 0); - _set_legacy(dev, high_rate); - _set_csma_backoff_period(dev, chips, mode); + _set_mode(dev, mode); + _set_chips(dev, chips); + _set_csma_backoff_period(dev, chips); _set_ack_timeout(dev, chips, mode); _end_configure_OQPSK(dev); return 0; } + +int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate) +{ + at86rf215_await_state_end(dev, RF_STATE_TX); + + /* disable radio */ + at86rf215_reg_write(dev, dev->BBC->RG_PC, 0); + + _set_legacy(dev, high_rate); + _set_csma_backoff_period_legacy(dev); + _set_ack_timeout_legacy(dev); + + _end_configure_OQPSK(dev); + + return 0; +} + +int at86rf215_OQPSK_set_chips(at86rf215_t *dev, uint8_t chips) +{ + uint8_t mode; + + mode = at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX); + + if (mode & AT86RF215_OQPSK_MODE_LEGACY) { + DEBUG("[%s] can't set chip rate in legacy mode\n", __func__); + return -1; + } + + at86rf215_await_state_end(dev, RF_STATE_TX); + + _set_chips(dev, chips); + _set_csma_backoff_period(dev, chips); + _set_ack_timeout(dev, chips, mode >> OQPSKPHRTX_MOD_SHIFT); + + return 0; +} + +uint8_t at86rf215_OQPSK_get_chips(at86rf215_t *dev) +{ + return at86rf215_reg_read(dev, dev->BBC->RG_OQPSKC0) & OQPSKC0_FCHIP_MASK; +} + +int at86rf215_OQPSK_set_mode(at86rf215_t *dev, uint8_t mode) +{ + if (mode > 4) { + return -1; + } + + uint8_t chips = at86rf215_OQPSK_get_chips(dev); + + at86rf215_await_state_end(dev, RF_STATE_TX); + + if (mode == 4 && chips != BB_FCHIP2000) { + _set_chips(dev, BB_FCHIP2000); + } + + _set_mode(dev, mode); + _set_csma_backoff_period(dev, chips); + _set_ack_timeout(dev, chips, mode); + + return 0; +} + +uint8_t at86rf215_OQPSK_get_mode(at86rf215_t *dev) +{ + uint8_t mode = at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX); + return (mode & OQPSKPHRTX_MOD_MASK) >> OQPSKPHRTX_MOD_SHIFT; +} + +int at86rf215_OQPSK_set_mode_legacy(at86rf215_t *dev, bool high_rate) +{ + /* enable/disable legacy high data rate */ + if (high_rate) { + at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, OQPSKC3_HRLEG_MASK); + } else { + at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, 0); + } + + _set_csma_backoff_period_legacy(dev); + _set_ack_timeout_legacy(dev); + + return 0; +} + +uint8_t at86rf215_OQPSK_get_mode_legacy(at86rf215_t *dev) +{ + if (at86rf215_reg_read(dev, dev->BBC->RG_OQPSKC3) & OQPSKC3_HRLEG_MASK) { + return 1; + } + + return 0; +} diff --git a/drivers/at86rf215/include/at86rf215_internal.h b/drivers/at86rf215/include/at86rf215_internal.h index 80f1817e69..514f63f439 100644 --- a/drivers/at86rf215/include/at86rf215_internal.h +++ b/drivers/at86rf215/include/at86rf215_internal.h @@ -46,8 +46,6 @@ extern "C" { */ /** 20 symbols is the std period length */ #define AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS (20U) -/** in 802.15.4 oqpsk each symble is 4 bits, not about the others */ -#define AT86RF215_BACKOFF_PERIOD_IN_BITS (AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * 4) /** * Default Parameters for 802.15.4 retransmissions & CSMA @@ -59,10 +57,6 @@ extern "C" { #define AT86RF215_CSMA_MAX_BE_DEFAULT (5) /** @} */ -/** For the SUN PHYs, the value is 1 ms expressed in symbol periods, rounded up to the next - integer number of symbol periods using the ceiling() function */ -#define AT86RF215_TURNAROUND_TIME_US (1 * US_PER_MS) - /** An ACK consists of 5 payload bytes */ #define AT86RF215_ACK_PSDU_BYTES (5) @@ -71,8 +65,6 @@ extern "C" { * AT86RF233 uses an ACK timeout of 54 symbol periods, or 864 µs @ 250 kbit/s * -> 864µs * 250kbit/s = 216 bit */ #define AT86RF215_ACK_PERIOD_IN_SYMBOLS (54U) -/** in 802.15.4 oqpsk each symble is 4 bits, not about the others */ -#define AT86RF215_ACK_PERIOD_IN_BITS (AT86RF215_ACK_PERIOD_IN_SYMBOLS * 4) #define AT86RF215_OQPSK_MODE_LEGACY (0x1) /**< legacy mode, 250 kbit/s */ #define AT86RF215_OQPSK_MODE_LEGACY_HDR (0x3) /**< legacy mode, high data rate */ @@ -158,6 +150,105 @@ void at86rf215_get_random(at86rf215_t *dev, void *data, size_t len); */ int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate); +/** + * @brief Configure the radio to make use of O-QPSK modulation. + * The chip rate is the number of bits per second (chips per second) used in the spreading signal. + * The rate mode may be + * - @ref AT86RF215_OQPSK_MODE_LEGACY for compatibility with first-gen 802.15.4 devices (250 kbit/s) + * - @ref AT86RF215_OQPSK_MODE_LEGACY_HDR for compatibility with the proprietary high-data rate mode + * of the at86rf233 (1000 kbit/s, 2.4 GHz) and at86rf212b (500 kbit/s, sub-GHz) + * - @ref AT86RF215_MR_OQPSK_MODE for the rate modes specified in 802.15.4g-2012 + * + * @param[in] dev device to configure + * @param[in] chips chip rate, `BB_FCHIP100` … `BB_FCHIP2000` + * @param[in] rate rate mode, may be @ref AT86RF215_OQPSK_MODE_LEGACY or @ref AT86RF215_MR_OQPSK_MODE + * + * @return 0 on success, error otherwise + */ +int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t rate); + +/** + * @brief Get the current O-QPSK chip rate + * + * @param[in] dev device to read from + * + * @return the current chip rate + */ +uint8_t at86rf215_OQPSK_get_chips(at86rf215_t *dev); + +/** + * @brief Set the current O-QPSK chip rate + * + * @param[in] dev device to configure + * @param[in] chips chip rate in chip/s + * + * @return 0 on success, error otherwise + */ +int at86rf215_OQPSK_set_chips(at86rf215_t *dev, uint8_t chips); + +/** + * @brief Get the current O-QPSK rate mode + * + * @param[in] dev device to read from + * + * @return the current rate mode + */ +uint8_t at86rf215_OQPSK_get_mode(at86rf215_t *dev); + +/** + * @brief Set the current O-QPSK rate mode + * + * @param[in] dev device to configure + * @param[in] mode rate mode + * + * @return 0 on success, error otherwise + */ +int at86rf215_OQPSK_set_mode(at86rf215_t *dev, uint8_t mode); + +/** + * @brief Get the current legacy O-QPSK mode + * + * @param[in] dev device to read from + * + * @return 0 for IEEE 802.15.4 mode, 1 for high data rate + */ +uint8_t at86rf215_OQPSK_get_mode_legacy(at86rf215_t *dev); + +/** + * @brief Set the current legacy O-QPSK rate mode + * + * @param[in] dev device to configure + * @param[in] high_rate set to use proprietary high data rate + * + * @return 0 on success, error otherwise + */ +int at86rf215_OQPSK_set_mode_legacy(at86rf215_t *dev, bool high_rate); + +/** + * @brief Test if O-QPSK PHY operates in legacy mode + * + * @param[in] dev device to test + * + * @return true if device operates in legacy mode + */ +static inline bool at86rf215_OQPSK_is_legacy(at86rf215_t *dev) { + return at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX) & AT86RF215_OQPSK_MODE_LEGACY; +} + +/** @} */ + +/** + * @brief Get the current PHY modulation. + * May be @ref IEEE802154_PHY_MR_FSK, @ref IEEE802154_PHY_MR_OFDM, + * @ref IEEE802154_PHY_MR_OQPSK, @ref IEEE802154_PHY_OQPSK + * or @ref IEEE802154_PHY_DISABLED. + * + * @param[in] dev device to read from + * + * @return the current PHY mode the device is operating with + */ +uint8_t at86rf215_get_phy_mode(at86rf215_t *dev); + /** * @brief Check if a channel number is valid. * The function takes the current frequency band and modulation into diff --git a/drivers/include/at86rf215.h b/drivers/include/at86rf215.h index 8211c5c156..786e9fb17a 100644 --- a/drivers/include/at86rf215.h +++ b/drivers/include/at86rf215.h @@ -56,6 +56,16 @@ typedef struct at86rf215_BBC_regs at86rf215_BBC_regs_t; */ typedef void (*at86rf215_batmon_cb_t)(void *arg); +/** + * @brief MR-O-QPSK chip rates (kChip/s) + */ +enum { + AT86RF215_FCHIP_100, + AT86RF215_FCHIP_200, + AT86RF215_FCHIP_1000, + AT86RF215_FCHIP_2000, +}; + /** * @brief Maximum possible packet size in byte */ @@ -100,6 +110,33 @@ typedef void (*at86rf215_batmon_cb_t)(void *arg); #define AT86RF215_DEFAULT_SUBGHZ_CHANNEL (CONFIG_IEEE802154_DEFAULT_SUBGHZ_CHANNEL) /** @} */ +/** + * @name Default PHY Mode + * @{ + */ +#ifndef AT86RF215_DEFAULT_PHY_MODE +#define AT86RF215_DEFAULT_PHY_MODE (IEEE802154_PHY_OQPSK) +#endif +/** @} */ + +/** + * @name Default MR-O-QPSK Chip Rate + * @{ + */ +#ifndef AT86RF215_DEFAULT_MR_OQPSK_CHIPS +#define AT86RF215_DEFAULT_MR_OQPSK_CHIPS (AT86RF215_FCHIP_1000) +#endif +/** @} */ + +/** + * @name Default MR-O-QPSK Rate Mode + * @{ + */ +#ifndef AT86RF215_DEFAULT_MR_OQPSK_RATE +#define AT86RF215_DEFAULT_MR_OQPSK_RATE (2) +#endif +/** @} */ + /** * @brief Default TX power (0dBm) */ @@ -119,6 +156,13 @@ typedef enum { } at86rf215_state_t; /** @} */ +enum { + AT86RF215_MODE_LEGACY_OQPSK, + AT86RF215_MODE_MR_OQPSK, + AT86RF215_MODE_MR_OFDM, + AT86RF215_MODE_MR_FSK +}; + /** * @name Internal device option flags * @{ diff --git a/drivers/netdev_ieee802154/netdev_ieee802154.c b/drivers/netdev_ieee802154/netdev_ieee802154.c index 4a168d876c..3a973a1a6c 100644 --- a/drivers/netdev_ieee802154/netdev_ieee802154.c +++ b/drivers/netdev_ieee802154/netdev_ieee802154.c @@ -71,6 +71,36 @@ void netdev_ieee802154_reset(netdev_ieee802154_t *dev) dev->netdev.driver->set(&dev->netdev, NETOPT_NID, &dev->pan, sizeof(dev->pan)); } +static inline uint16_t _get_ieee802154_pdu(netdev_ieee802154_t *dev) +{ +#if defined(MODULE_NETDEV_IEEE802154_MR_OQPSK) || \ + defined(MODULE_NETDEV_IEEE802154_MR_OFDM) || \ + defined(MODULE_NETDEV_IEEE802154_MR_FSK) + uint8_t type = IEEE802154_PHY_DISABLED; + dev->netdev.driver->get(&dev->netdev, NETOPT_IEEE802154_PHY, &type, sizeof(type)); +#else + (void) dev; +#endif + +#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK + if (type == IEEE802154_PHY_MR_OQPSK) { + return IEEE802154G_FRAME_LEN_MAX; + } +#endif +#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM + if (type == IEEE802154_PHY_MR_OFDM) { + return IEEE802154G_FRAME_LEN_MAX; + } +#endif +#ifdef MODULE_NETDEV_IEEE802154_MR_FSK + if (type == IEEE802154_PHY_MR_FSK) { + return IEEE802154G_FRAME_LEN_MAX; + } +#endif + + return IEEE802154_FRAME_LEN_MAX; +} + int netdev_ieee802154_get(netdev_ieee802154_t *dev, netopt_t opt, void *value, size_t max_len) { @@ -152,9 +182,10 @@ int netdev_ieee802154_get(netdev_ieee802154_t *dev, netopt_t opt, void *value, #endif case NETOPT_MAX_PDU_SIZE: assert(max_len >= sizeof(int16_t)); - *((uint16_t *)value) = (IEEE802154_FRAME_LEN_MAX - - IEEE802154_MAX_HDR_LEN) - - IEEE802154_FCS_LEN; + + *((uint16_t *)value) = (_get_ieee802154_pdu(dev) + - IEEE802154_MAX_HDR_LEN) + - IEEE802154_FCS_LEN; res = sizeof(uint16_t); break; default: diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 9209c0a8f2..09608c6a01 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -62,6 +62,7 @@ PSEUDOMODULES += mpu_stack_guard PSEUDOMODULES += mpu_noexec_ram PSEUDOMODULES += nanocoap_% PSEUDOMODULES += netdev_default +PSEUDOMODULES += netdev_ieee802154_% PSEUDOMODULES += netstats PSEUDOMODULES += netstats_l2 PSEUDOMODULES += netstats_ipv6 diff --git a/sys/include/net/ieee802154.h b/sys/include/net/ieee802154.h index 661eed1f68..43b392a0a1 100644 --- a/sys/include/net/ieee802154.h +++ b/sys/include/net/ieee802154.h @@ -94,7 +94,38 @@ extern "C" { #define IEEE802154_CHANNEL_MAX (26U) /**< Maximum channel for 2.4 GHz band */ /** @} */ -#define IEEE802154_FRAME_LEN_MAX (127U) /**< maximum frame length */ +#define IEEE802154_FRAME_LEN_MAX (127U) /**< maximum 802.15.4 frame length */ +#define IEEE802154G_FRAME_LEN_MAX (2047U) /**< maximum 802.15.4g-2012 frame length */ + +/** + * For the SUN PHYs, the value is 1 ms expressed in symbol periods, rounded + * up to the next integer number of symbol periods using the ceiling() function. + * + * 802.15.4g, Table 70 (p. 43) + */ +#define IEEE802154G_ATURNAROUNDTIME_US (1 * US_PER_MS) + +/** + * @brief 802.15.4 PHY modes + */ +enum { + IEEE802154_PHY_DISABLED, /**< PHY disabled, no mode selected */ + IEEE802154_PHY_BPSK, /**< Binary Phase Shift Keying */ + IEEE802154_PHY_ASK, /**< Amplitude-Shift Keying */ + IEEE802154_PHY_OQPSK, /**< Offset Quadrature Phase-Shift Keying */ + IEEE802154_PHY_MR_OQPSK, /**< Multi-Rate Offset Quadrature Phase-Shift Keying */ + IEEE802154_PHY_MR_OFDM, /**< Multi-Rate Orthogonal Frequency-Division Multiplexing */ + IEEE802154_PHY_MR_FSK /**< Multi-Rate Frequency Shift Keying */ +}; + +/** + * @brief 802.15.4 forward error correction schemes + */ +enum { + IEEE802154_FEC_NONE, /**< no forward error correction */ + IEEE802154_FEC_NRNSC, /**< non-recursive and non-systematic code */ + IEEE802154_FEC_RSC /**< recursive and systematic code */ +}; /** * @brief Special address definitions diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index b1739646c3..54398370db 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -651,6 +651,31 @@ typedef enum { */ NETOPT_LORAWAN_MIN_RX_SYMBOL, + /** + * @brief (uint8_t) 802.15.4 PHY mode + */ + NETOPT_IEEE802154_PHY, + + /** + * @brief (uint8_t) legacy O-QPSK Rate Mode + */ + NETOPT_OQPSK_RATE, + + /** + * @brief (uint8_t) MR-O-QPSK Chip Rate (kchip/s) + */ + NETOPT_MR_OQPSK_CHIPS, + + /** + * @brief (uint8_t) MR-O-QPSK Rate Mode + */ + NETOPT_MR_OQPSK_RATE, + + /** + * @brief (uint8_t) PHY Channel Spacing (kHz) + */ + NETOPT_CHANNEL_SPACING, + /** * @brief (uint8_t*) phy layer syncword */ diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index e9f420f1bb..683ab72732 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -106,6 +106,11 @@ static const char *_netopt_strmap[] = { [NETOPT_LORAWAN_RX2_FREQ] = "NETOPT_LORAWAN_RX2_FREQ", [NETOPT_LORAWAN_MAX_RX_ERROR] = "NETOPT_LORAWAN_MAX_RX_ERROR", [NETOPT_LORAWAN_MIN_RX_SYMBOL] = "NETOPT_LORAWAN_MIN_RX_SYMBOL", + [NETOPT_IEEE802154_PHY] = "NETOPT_IEEE802154_PHY", + [NETOPT_OQPSK_RATE] = "NETOPT_O-QPSK_RATE", + [NETOPT_MR_OQPSK_CHIPS] = "NETOPT_MR-O-QPSK_CHIPS", + [NETOPT_MR_OQPSK_RATE] = "NETOPT_MR-O-QPSK_RATE", + [NETOPT_CHANNEL_SPACING] = "NETOPT_CHANNEL_SPACING", [NETOPT_SYNCWORD] = "NETOPT_SYNCWORD", [NETOPT_RANDOM] = "NETOPT_RANDOM", [NETOPT_RX_SYMBOL_TIMEOUT] = "NETOPT_RX_SYMBOL_TIMEOUT", diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index 6cdaad5e85..eac2b7462e 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -374,6 +374,9 @@ int gnrc_netif_set_from_netdev(gnrc_netif_t *netif, case NETOPT_SRC_LEN: _update_l2addr_from_dev(netif); break; + case NETOPT_IEEE802154_PHY: + gnrc_netif_ipv6_init_mtu(netif); + break; case NETOPT_STATE: if (*((netopt_state_t *)opt->data) == NETOPT_STATE_RESET) { _configure_netdev(netif->dev); diff --git a/sys/shell/commands/sc_gnrc_netif.c b/sys/shell/commands/sc_gnrc_netif.c index f8e21722c6..3441d06b1f 100644 --- a/sys/shell/commands/sc_gnrc_netif.c +++ b/sys/shell/commands/sc_gnrc_netif.c @@ -18,6 +18,7 @@ * @author Oliver Hahm */ +#include #include #include @@ -86,6 +87,15 @@ static void _print_iface_name(netif_t *iface) printf("%s", name); } +__attribute__ ((unused)) +static void str_toupper(char *str) +{ + while (*str) { + *str = toupper((unsigned) *str); + ++str; + } +} + #ifdef MODULE_NETSTATS static const char *_netstats_module_to_str(uint8_t module) { @@ -173,6 +183,13 @@ static void _set_usage(char *cmd_name) " * \"dr\" - sets datarate\n" " * \"rx2_dr\" - sets datarate of RX2 (lorawan)\n" " * \"nwkskey\" - sets Network Session Key\n" +#endif +#ifdef MODULE_NETDEV_IEEE802154_MULTIMODE + " * \"phy_mode\" - select PHY mode\n" +#endif +#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK + " * \"chip_rate\" - BPSK/QPSK chip rate in kChip/s\n" + " * \"rate_mode\" - BPSK/QPSK rate mode\n" #endif " * \"power\" - TX power in dBm\n" " * \"retrans\" - max. number of retransmissions\n" @@ -303,7 +320,33 @@ static void _print_netopt(netopt_t opt) case NETOPT_CODING_RATE: printf("coding rate"); break; -#endif +#endif /* MODULE_GNRC_NETIF_CMD_LORA */ +#ifdef MODULE_NETDEV_IEEE802154_MULTIMODE + + case NETOPT_IEEE802154_PHY: + printf("PHY mode"); + break; + +#endif /* MODULE_NETDEV_IEEE802154_MULTIMODE */ +#ifdef MODULE_NETDEV_IEEE802154_OQPSK + + case NETOPT_OQPSK_RATE: + printf("high rate"); + break; + +#endif /* MODULE_NETDEV_IEEE802154_OQPSK */ +#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK + + case NETOPT_MR_OQPSK_CHIPS: + printf("chip rate"); + break; + + case NETOPT_MR_OQPSK_RATE: + printf("rate mode"); + break; + +#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */ + case NETOPT_CHECKSUM: printf("checksum"); break; @@ -359,6 +402,18 @@ static const char *_netopt_coding_rate_str[] = { }; #endif +#ifdef MODULE_NETDEV_IEEE802154 +static const char *_netopt_ieee802154_phy_str[] = { + [IEEE802154_PHY_DISABLED] = "DISABLED", + [IEEE802154_PHY_BPSK] = "BPSK", + [IEEE802154_PHY_ASK] = "ASK", + [IEEE802154_PHY_OQPSK] = "O-QPSK", + [IEEE802154_PHY_MR_OQPSK] = "MR-O-QPSK", + [IEEE802154_PHY_MR_OFDM] = "MR-OFDM", + [IEEE802154_PHY_MR_FSK] = "MR-FSK" +}; +#endif + /* for some lines threshold might just be 0, so we can't use _LINE_THRESHOLD * here */ static unsigned _newline(unsigned threshold, unsigned line_thresh) @@ -370,7 +425,6 @@ static unsigned _newline(unsigned threshold, unsigned line_thresh) return line_thresh; } - static unsigned _netif_list_flag(netif_t *iface, netopt_t opt, char *str, unsigned line_thresh) { @@ -478,7 +532,7 @@ static void _netif_list(netif_t *iface) } res = netif_get_opt(iface, NETOPT_NID, 0, &u16, sizeof(u16)); if (res >= 0) { - printf(" NID: 0x%" PRIx16, u16); + printf(" NID: 0x%" PRIx16 " ", u16); } #ifdef MODULE_GNRC_NETIF_CMD_LORA res = netif_get_opt(iface, NETOPT_BANDWIDTH, 0, &u8, sizeof(u8)); @@ -493,7 +547,42 @@ static void _netif_list(netif_t *iface) if (res >= 0) { printf(" CR: %s ", _netopt_coding_rate_str[u8]); } -#endif +#endif /* MODULE_GNRC_NETIF_CMD_LORA */ +#ifdef MODULE_NETDEV_IEEE802154 + res = netif_get_opt(iface, NETOPT_IEEE802154_PHY, 0, &u8, sizeof(u8)); + if (res >= 0) { + printf(" PHY: %s ", _netopt_ieee802154_phy_str[u8]); + switch (u8) { + +#ifdef MODULE_NETDEV_IEEE802154_OQPSK + case IEEE802154_PHY_OQPSK: + printf("\n "); + res = netif_get_opt(iface, NETOPT_OQPSK_RATE, 0, &u8, sizeof(u8)); + if (res >= 0 && u8) { + printf(" high data rate: %d ", u8); + } + + break; + +#endif /* MODULE_NETDEV_IEEE802154_OQPSK */ +#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK + case IEEE802154_PHY_MR_OQPSK: + printf("\n "); + res = netif_get_opt(iface, NETOPT_MR_OQPSK_CHIPS, 0, &u16, sizeof(u16)); + if (res >= 0) { + printf(" chip rate: %u ", u16); + } + res = netif_get_opt(iface, NETOPT_MR_OQPSK_RATE, 0, &u8, sizeof(u8)); + if (res >= 0) { + printf(" rate mode: %d ", u8); + } + + break; + +#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */ + } + } +#endif /* MODULE_NETDEV_IEEE802154 */ netopt_enable_t link; res = netif_get_opt(iface, NETOPT_LINK, 0, &link, sizeof(netopt_enable_t)); if (res >= 0) { @@ -603,7 +692,7 @@ static void _netif_list(netif_t *iface) #endif res = netif_get_opt(iface, NETOPT_SRC_LEN, 0, &u16, sizeof(u16)); if (res >= 0) { - printf("Source address length: %" PRIu16 , u16); + printf("Source address length: %" PRIu16, u16); line_thresh++; } line_thresh = _newline(0U, line_thresh); @@ -781,6 +870,36 @@ static int _netif_set_coding_rate(netif_t *iface, char *value) } #endif /* MODULE_GNRC_NETIF_CMD_LORA */ +#ifdef MODULE_NETDEV_IEEE802154_MULTIMODE +static int _netif_set_ieee802154_phy_mode(netif_t *iface, char *value) +{ + /* ignore case */ + str_toupper(value); + + for (uint8_t i = 0; i < ARRAY_SIZE(_netopt_ieee802154_phy_str); ++i) { + + if (strcmp(_netopt_ieee802154_phy_str[i], value)) { + continue; + } + + if (netif_set_opt(iface, NETOPT_IEEE802154_PHY, 0, &i, sizeof(uint8_t)) < 0) { + printf("error: unable to set PHY mode to %s\n", value); + return 1; + } + + printf("success: set PHY mode %s\n", value); + return 0; + } + + printf("usage: ifconfig set phy "); + for (unsigned i = 0; i < ARRAY_SIZE(_netopt_ieee802154_phy_str); ++i) { + printf("%c%s", i ? '|' : '[', _netopt_ieee802154_phy_str[i]); + } + puts("]"); + return 1; +} +#endif /* MODULE_NETDEV_IEEE802154_MULTIMODE */ + static int _netif_set_u16(netif_t *iface, netopt_t opt, uint16_t context, char *u16_str) { @@ -894,7 +1013,7 @@ static int _netif_set_lw_key(netif_t *iface, netopt_t opt, char *key_str) size_t key_len = fmt_hex_bytes(key, key_str); size_t expected_len; - switch(opt) { + switch (opt) { case NETOPT_LORAWAN_APPKEY: case NETOPT_LORAWAN_APPSKEY: case NETOPT_LORAWAN_NWKSKEY: @@ -1162,7 +1281,25 @@ static int _netif_set(char *cmd_name, netif_t *iface, char *key, char *value) else if (strcmp("rx2_dr", key) == 0) { return _netif_set_u8(iface, NETOPT_LORAWAN_RX2_DR, 0, value); } -#endif +#endif /* MODULE_GNRC_NETIF_CMD_LORA */ +#ifdef MODULE_NETDEV_IEEE802154_MULTIMODE + else if ((strcmp("phy_mode", key) == 0) || (strcmp("phy", key) == 0)) { + return _netif_set_ieee802154_phy_mode(iface, value); + } +#endif /* MODULE_NETDEV_IEEE802154_MULTIMODE */ +#ifdef MODULE_NETDEV_IEEE802154_OQPSK + else if (strcmp("high_rate", key) == 0) { + return _netif_set_u8(iface, NETOPT_OQPSK_RATE, 0, value); + } +#endif /* MODULE_NETDEV_IEEE802154_OQPSK */ +#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK + else if ((strcmp("chip_rate", key) == 0) || (strcmp("chips", key) == 0)) { + return _netif_set_u16(iface, NETOPT_MR_OQPSK_CHIPS, 0, value); + } + else if (strcmp("rate_mode", key) == 0) { + return _netif_set_u8(iface, NETOPT_MR_OQPSK_RATE, 0, value); + } +#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */ else if ((strcmp("channel", key) == 0) || (strcmp("chan", key) == 0)) { return _netif_set_u16(iface, NETOPT_CHANNEL, 0, value); } @@ -1237,7 +1374,7 @@ static uint8_t _get_prefix_len(char *addr) static int _netif_link(netif_t *iface, netopt_enable_t en) { - if(netif_set_opt(iface, NETOPT_LINK, 0, &en, sizeof(en)) < 0) { + if (netif_set_opt(iface, NETOPT_LINK, 0, &en, sizeof(en)) < 0) { printf("error: unable to set link %s\n", en == NETOPT_ENABLE ? "up" : "down"); return 1; }