diff --git a/drivers/at86rf2xx/at86rf2xx_getset.c b/drivers/at86rf2xx/at86rf2xx_getset.c index b4daa98a0b21..5955295bb8ef 100644 --- a/drivers/at86rf2xx/at86rf2xx_getset.c +++ b/drivers/at86rf2xx/at86rf2xx_getset.c @@ -16,6 +16,7 @@ * @author Thomas Eichinger * @author Hauke Petersen * @author Baptiste Clenet + * @author Daniel Krebs * * @} */ @@ -254,6 +255,56 @@ void at86rf2xx_set_max_retries(at86rf2xx_t *dev, uint8_t max) at86rf2xx_reg_write(dev, AT86RF2XX_REG__XAH_CTRL_0, tmp); } +uint8_t at86rf2xx_get_csma_max_retries(at86rf2xx_t *dev) +{ + uint8_t tmp; + tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__XAH_CTRL_0); + tmp &= AT86RF2XX_XAH_CTRL_0__MAX_CSMA_RETRIES; + tmp >>= 1; + return tmp; +} + +void at86rf2xx_set_csma_max_retries(at86rf2xx_t *dev, int8_t retries) +{ + retries = (retries > 5) ? 5 : retries; /* valid values: 0-5 */ + retries = (retries < 0) ? 7 : retries; /* max < 0 => disable CSMA (set to 7) */ + DEBUG("[at86rf2xx] opt: Set CSMA retries to %u\n", retries); + + uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__XAH_CTRL_0); + tmp &= ~(AT86RF2XX_XAH_CTRL_0__MAX_CSMA_RETRIES); + tmp |= (retries << 1); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__XAH_CTRL_0, tmp); +} + +void at86rf2xx_set_csma_backoff_exp(at86rf2xx_t *dev, uint8_t min, uint8_t max) +{ + max = (max > 8) ? 8 : max; + min = (min > max) ? max : min; + DEBUG("[at86rf2xx] opt: Set min BE=%u, max BE=%u\n", min, max); + + at86rf2xx_reg_write(dev, + AT86RF2XX_REG__CSMA_BE, + (max << 4) | (min)); +} + +void at86rf2xx_set_csma_seed(at86rf2xx_t *dev, uint8_t entropy[2]) +{ + if(entropy == NULL) { + DEBUG("[at86rf2xx] opt: CSMA seed entropy is nullpointer\n"); + return; + } + DEBUG("[at86rf2xx] opt: Set CSMA seed to 0x%x 0x%x\n", entropy[0], entropy[1]); + + at86rf2xx_reg_write(dev, + AT86RF2XX_REG__CSMA_SEED_0, + entropy[0]); + + uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__CSMA_SEED_1); + tmp &= ~(AT86RF2XX_CSMA_SEED_1__CSMA_SEED_1); + tmp |= entropy[1] & AT86RF2XX_CSMA_SEED_1__CSMA_SEED_1; + at86rf2xx_reg_write(dev, AT86RF2XX_REG__CSMA_SEED_1, tmp); +} + void at86rf2xx_set_option(at86rf2xx_t *dev, uint16_t option, bool state) { uint8_t tmp; @@ -266,8 +317,12 @@ void at86rf2xx_set_option(at86rf2xx_t *dev, uint16_t option, bool state) /* trigger option specific actions */ switch (option) { case AT86RF2XX_OPT_CSMA: - DEBUG("[at86rf2xx] opt: enabling CSMA mode (NOT IMPLEMENTED)\n"); - /* TODO: en/disable csma */ + DEBUG("[at86rf2xx] opt: enabling CSMA mode" \ + "(4 retries, min BE: 3 max BE: 5)\n"); + /* Initialize CSMA seed with hardware address */ + at86rf2xx_set_csma_seed(dev, dev->addr_long); + at86rf2xx_set_csma_max_retries(dev, 4); + at86rf2xx_set_csma_backoff_exp(dev, 3, 5); break; case AT86RF2XX_OPT_PROMISCUOUS: DEBUG("[at86rf2xx] opt: enabling PROMISCUOUS mode\n"); @@ -302,8 +357,9 @@ void at86rf2xx_set_option(at86rf2xx_t *dev, uint16_t option, bool state) /* trigger option specific actions */ switch (option) { case AT86RF2XX_OPT_CSMA: - DEBUG("[at86rf2xx] opt: disabling CSMA mode (NOT IMPLEMENTED)\n"); - /* TODO: en/disable csma */ + DEBUG("[at86rf2xx] opt: disabling CSMA mode\n"); + /* setting retries to -1 means CSMA disabled */ + at86rf2xx_set_csma_max_retries(dev, -1); break; case AT86RF2XX_OPT_PROMISCUOUS: DEBUG("[at86rf2xx] opt: disabling PROMISCUOUS mode\n"); diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c index 1b591a5b0915..c00a4e67ee97 100644 --- a/drivers/at86rf2xx/at86rf2xx_netdev.c +++ b/drivers/at86rf2xx/at86rf2xx_netdev.c @@ -528,6 +528,18 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) !!(dev->options & AT86RF2XX_OPT_TELL_TX_END); return sizeof(netopt_enable_t); + case NETOPT_CSMA: + *((netopt_enable_t *)val) = + !!(dev->options & AT86RF2XX_OPT_CSMA); + return sizeof(netopt_enable_t); + + case NETOPT_CSMA_RETRIES: + if (max_len < sizeof(uint8_t)) { + return -EOVERFLOW; + } + *((uint8_t *)val) = at86rf2xx_get_csma_max_retries(dev); + return sizeof(uint8_t); + default: return -ENOTSUP; } @@ -654,6 +666,23 @@ static int _set(gnrc_netdev_t *device, netopt_t opt, void *val, size_t len) ((bool *)val)[0]); return sizeof(netopt_enable_t); + case NETOPT_CSMA: + at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, + ((bool *)val)[0]); + return sizeof(netopt_enable_t); + + case NETOPT_CSMA_RETRIES: + if( (len > sizeof(uint8_t)) || + (*((uint8_t *)val) > 5) ) { + return -EOVERFLOW; + } + /* If CSMA is disabled, don't allow setting retries */ + if( !(dev->options & AT86RF2XX_OPT_CSMA) ) { + return -ENOTSUP; + } + at86rf2xx_set_csma_max_retries(dev, *((uint8_t *)val)); + return sizeof(uint8_t); + default: return -ENOTSUP; } diff --git a/drivers/at86rf2xx/include/at86rf2xx_registers.h b/drivers/at86rf2xx/include/at86rf2xx_registers.h index 9f98df1414fe..f54121bdf495 100644 --- a/drivers/at86rf2xx/include/at86rf2xx_registers.h +++ b/drivers/at86rf2xx/include/at86rf2xx_registers.h @@ -317,6 +317,7 @@ extern "C" { #define AT86RF2XX_CSMA_SEED_1__AACK_SET_PD (0x20) #define AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK (0x10) #define AT86RF2XX_CSMA_SEED_1__AACK_I_AM_COORD (0x08) +#define AT86RF2XX_CSMA_SEED_1__CSMA_SEED_1 (0x07) /** @} */ /** diff --git a/drivers/include/at86rf2xx.h b/drivers/include/at86rf2xx.h index 6166a047b9d9..0d226931369d 100644 --- a/drivers/include/at86rf2xx.h +++ b/drivers/include/at86rf2xx.h @@ -21,6 +21,7 @@ * @author Thomas Eichinger * @author Hauke Petersen * @author Kaspar Schleiser + * @author Daniel Krebs */ #ifndef AT86RF2XX_H_ @@ -334,6 +335,48 @@ uint8_t at86rf2xx_get_max_retries(at86rf2xx_t *dev); */ void at86rf2xx_set_max_retries(at86rf2xx_t *dev, uint8_t max); +/** + * @brief Get the maximum number of channel access attempts per frame (CSMA) + * + * @param[in] dev device to read from + * + * @return configured number of retries + */ +uint8_t at86rf2xx_get_csma_max_retries(at86rf2xx_t *dev); + +/** + * @brief Set the maximum number of channel access attempts per frame (CSMA) + * + * This setting specifies the number of attempts to access the channel to + * transmit a frame. If the channel is busy @p retries times, then frame + * transmission fails. + * Valid values: 0 to 5, -1 means CSMA disabled + * + * @param[in] dev device to write to + * @param[in] max the maximum number of retries + */ +void at86rf2xx_set_csma_max_retries(at86rf2xx_t *dev, int8_t retries); + +/** + * @brief Set the min and max backoff exponent for CSMA/CA + * + * - Maximum BE: 0 - 8 + * - Minimum BE: 0 - [max] + * + * @param[in] dev device to write to + * @param[in] min the minimum BE + * @param[in] max the maximum BE + */ +void at86rf2xx_set_csma_backoff_exp(at86rf2xx_t *dev, uint8_t min, uint8_t max); + +/** + * @brief Set seed for CSMA random backoff + * + * @param[in] dev device to write to + * @param[in] entropy 11 bit of entropy as seed for random backoff + */ +void at86rf2xx_set_csma_seed(at86rf2xx_t *dev, uint8_t entropy[2]); + /** * @brief Enable or disable driver specific options * diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index af334bfbf783..21ba41dab954 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -137,9 +137,31 @@ typedef enum { * @note not all transceivers may support this interrupt */ NETOPT_TX_END_IRQ, - NETOPT_AUTOCCA, /**< en/disable to check automatically - * before sending the channel is clear. */ + /** + * @brief Check automatically before sending if the channel is clear. + * + * This may be a hardware feature of the given transceiver, or might be + * otherwise implemented in software. If the device supports CSMA this + * option will enable CSMA with a certain set of parameters to emulate the + * desired behaviour. + * + * @note Be sure not to set NETCONF_OPT_CSMA simultaneously. + * + * TODO: How to get feedback? + */ + NETOPT_AUTOCCA, + + /** + * @brief en/disable CSMA/CA support + * + * If the device supports CSMA in hardware, this option enables it with + * default parameters. For further configuration refer to the other + * NETCONF_OPT_CSMA_* options. + */ + NETOPT_CSMA, + NETOPT_CSMA_RETRIES, /**< get/set the number of retries when + when channel is busy */ /** * @brief read-only check for a wired interface. * diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index 84505c6ef6ce..0841fe4a95ba 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -46,6 +46,8 @@ static const char *_netopt_strmap[] = { [NETOPT_TX_START_IRQ] = "NETOPT_TX_START_IRQ", [NETOPT_TX_END_IRQ] = "NETOPT_TX_END_IRQ", [NETOPT_AUTOCCA] = "NETOPT_AUTOCCA", + [NETOPT_CSMA] = "NETOPT_CSMA", + [NETOPT_CSMA_RETRIES] = "NETOPT_CSMA_RETRIES", [NETOPT_IS_WIRED] = "NETOPT_IS_WIRED", [NETOPT_NUMOF] = "NETOPT_NUMOF", }; diff --git a/sys/shell/commands/sc_netif.c b/sys/shell/commands/sc_netif.c index 101925a21029..4c51d585ae55 100644 --- a/sys/shell/commands/sc_netif.c +++ b/sys/shell/commands/sc_netif.c @@ -80,7 +80,8 @@ static void _set_usage(char *cmd_name) " * \"addr_long\" - sets long address\n" " * \"addr_short\" - alias for \"addr\"\n" " * \"channel\" - sets the frequency channel\n" - " * \"chan\" - alias for \"channel\"" + " * \"chan\" - alias for \"channel\"\n" + " * \"csma_retries\" - set max. number of channel access attempts\n" " * \"nid\" - sets the network identifier (or the PAN ID)\n" " * \"pan\" - alias for \"nid\"\n" " * \"pan_id\" - alias for \"nid\"\n" @@ -96,7 +97,7 @@ static void _mtu_usage(char *cmd_name) static void _flag_usage(char *cmd_name) { - printf("usage: %s [-]{promisc|autoack|preload|6lo|iphc}\n", cmd_name); + printf("usage: %s [-]{promisc|autoack|csma|autocca|preload|6lo|iphc}\n", cmd_name); } static void _add_usage(char *cmd_name) @@ -138,6 +139,10 @@ static void _print_netopt(netopt_t opt) printf("TX power [in dBm]"); break; + case NETOPT_CSMA_RETRIES: + printf("CSMA retries"); + break; + default: /* we don't serve these options here */ break; @@ -176,6 +181,7 @@ static void _netif_list(kernel_pid_t dev) uint8_t hwaddr[MAX_ADDR_LEN]; uint16_t u16; int16_t i16; + uint8_t u8; int res; netopt_state_t state; netopt_enable_t enable; @@ -230,11 +236,21 @@ static void _netif_list(kernel_pid_t dev) if (res >= 0) { char hwaddr_str[res * 3]; printf("Long HWaddr: "); - printf("%s", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), + printf("%s ", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), hwaddr, res)); - printf("\n "); } + res = gnrc_netapi_get(dev, NETOPT_CSMA_RETRIES, 0, &u8, sizeof(u8)); + + if (res >= 0) { + res = gnrc_netapi_get(dev, NETOPT_CSMA, 0, &enable, sizeof(enable)); + if ((res >= 0) && (enable == NETOPT_ENABLE)) { + printf(" CSMA Retries: %" PRIu8 " ", *((uint8_t*) &u8)); + } + } + + printf("\n "); + res = gnrc_netapi_get(dev, NETOPT_PROMISCUOUSMODE, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { @@ -263,6 +279,20 @@ static void _netif_list(kernel_pid_t dev) linebreak = true; } + res = gnrc_netapi_get(dev, NETOPT_CSMA, 0, &enable, sizeof(enable)); + + if ((res >= 0) && (enable == NETOPT_ENABLE)) { + printf("CSMA "); + linebreak = true; + } + + res = gnrc_netapi_get(dev, NETOPT_AUTOCCA, 0, &enable, sizeof(enable)); + + if ((res >= 0) && (enable == NETOPT_ENABLE)) { + printf("AUTOCCA "); + linebreak = true; + } + #ifdef MODULE_GNRC_IPV6_NETIF if (entry != NULL) { printf("MTU:%" PRIu16 " ", entry->mtu); @@ -401,6 +431,24 @@ static int _netif_set_i16(kernel_pid_t dev, netopt_t opt, char *i16_str) return 0; } +static int _netif_set_u8(kernel_pid_t dev, netopt_t opt, char *u8_str) +{ + uint8_t val = (uint8_t)atoi(u8_str); + + if (gnrc_netapi_set(dev, opt, 0, (uint8_t *)&val, sizeof(uint8_t)) < 0) { + printf("error: unable to set "); + _print_netopt(opt); + puts(""); + return 1; + } + + printf("success: set "); + _print_netopt(opt); + printf(" on interface %" PRIkernel_pid " to %i\n", dev, val); + + return 0; +} + static int _netif_set_flag(kernel_pid_t dev, netopt_t opt, netopt_enable_t set) { @@ -498,6 +546,9 @@ static int _netif_set(char *cmd_name, kernel_pid_t dev, char *key, char *value) else if (strcmp("state", key) == 0) { return _netif_set_state(dev, value); } + else if (strcmp("csma_retries", key) == 0) { + return _netif_set_u8(dev, NETOPT_CSMA_RETRIES, value); + } _set_usage(cmd_name); return 1; @@ -524,6 +575,12 @@ static int _netif_flag(char *cmd, kernel_pid_t dev, char *flag) else if (strcmp(flag, "raw") == 0) { return _netif_set_flag(dev, NETOPT_RAWMODE, set); } + else if (strcmp(flag, "csma") == 0) { + return _netif_set_flag(dev, NETOPT_CSMA, set); + } + else if (strcmp(flag, "autocca") == 0) { + return _netif_set_flag(dev, NETOPT_AUTOCCA, set); + } else if (strcmp(flag, "6lo") == 0) { #ifdef MODULE_GNRC_IPV6_NETIF gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(dev);