cc2420.c 7.7 KB
/*
 * Copyright (C) 2015-2016 Freie Universität Berlin
 *               2016 Inria
 *
 * 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.
 */

/**
 * @ingroup     drivers_cc2420
 * @{
 *
 * @file
 * @brief       Implementation of public functions for cc2420 driver
 *
 * @author      Thomas Eichinger <thomas.eichinger@fu-berlin.de>
 * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
 *
 * @}
 */

#include "periph/cpuid.h"
#include "byteorder.h"
#include "net/ieee802154.h"
#include "net/gnrc.h"

#include "cc2420_internal.h"
#include "cc2420_netdev.h"
#include "cc2420_registers.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

/**
 * @todo    Move this function to a global module
 */
#if CPUID_ID_LEN
static void addr_from_cpuid(uint8_t *addr)
{
    /* option 1: generate addresses from CPUID */
    uint8_t cpuid[CPUID_ID_LEN];

    cpuid_get(cpuid);
    memcpy(addr, cpuid, 8);

#if CPUID_ID_LEN < 8
    /* in case CPUID_ID_LEN < 8, fill missing bytes with zeros */
    for (int i = CPUID_ID_LEN; i < 8; i++) {
        addr_long[i] = 0;
    }
#else
    /* in case CPUID_ID_LEN > 8, XOR those bytes on top of the first 8 */
    for (int i = 8; i < CPUID_ID_LEN; i++) {
        addr_long[i & 0x07] ^= cpuid[i];
    }
#endif

    /* make sure we mark the address as non-multicast and not globally unique */
    addr_long[0] &= ~(0x01);
    addr_long[0] |= 0x02;
}
#endif


void cc2420_setup(cc2420_t * dev, const cc2420_params_t *params)
{
    /* set pointer to the devices netdev functions */
    dev->netdev.netdev.driver = &cc2420_driver;
    /* pull in device configuration parameters */
    memcpy(&dev->params, params, sizeof(cc2420_params_t));
    dev->state = CC2420_STATE_IDLE;
    /* reset device descriptor fields */
    dev->options = 0;
    spi_init_master(dev->params.spi, SPI_CONF_FIRST_RISING, dev->params.spi_clk);
}

int cc2420_init(cc2420_t *dev)
{
    uint16_t reg;
    uint8_t addr[8] = CC2420_ADDR_FALLBACK;

    /* reset options and sequence number */
    dev->netdev.seq = 0;
    dev->netdev.flags = 0;

    /* set default address, channel, PAN ID, and TX power */
#if CPUID_ID_LEN
    addr_from_cpuid(addr);
#endif
    cc2420_set_addr_short(dev, &addr[6]);
    cc2420_set_addr_long(dev, addr);
    cc2420_set_pan(dev, CC2420_PANID_DEFAULT);
    cc2420_set_chan(dev, CC2420_CHAN_DEFAULT);
    cc2420_set_txpower(dev, CC2420_TXPOWER_DEFAULT);

    /* set default options */
    cc2420_set_option(dev, CC2420_OPT_AUTOACK, true);
    cc2420_set_option(dev, CC2420_OPT_CSMA, true);
    cc2420_set_option(dev, CC2420_OPT_TELL_TX_START, true);
    cc2420_set_option(dev, CC2420_OPT_TELL_RX_END, true);

#ifdef MODULE_NETSTATS_L2
    cc2420_set_option(dev, CC2420_OPT_TELL_RX_END, true);
#endif
    /* set default protocol*/
#ifdef MODULE_GNRC_SIXLOWPAN
    dev->netdev.proto = GNRC_NETTYPE_SIXLOWPAN;
#elif MODULE_GNRC
    dev->netdev.proto = GNRC_NETTYPE_UNDEF;
#endif

    /* change default RX bandpass filter to 1.3uA (as recommended) */
    reg = cc2420_reg_read(dev, CC2420_REG_RXCTRL1);
    reg |= CC2420_RXCTRL1_RXBPF_LOCUR;
    cc2420_reg_write(dev, CC2420_REG_RXCTRL1, reg);

    /* set the FIFOP threshold to maximum. */
    cc2420_reg_write(dev, CC2420_REG_IOCFG0, CC2420_PKT_MAXLEN);

    /* turn off "Security enable" (page 33). */
    reg = cc2420_reg_read(dev, CC2420_REG_SECCTRL0);
    reg &= ~CC2420_SECCTRL0_RXFIFO_PROT;
    cc2420_reg_write(dev, CC2420_REG_SECCTRL0, reg);

    /* set preamble length to 3 leading zero byte */
    /* and turn on hardware CRC generation */
    reg = cc2420_reg_read(dev, CC2420_REG_MDMCTRL0);
    reg &= ~(CC2420_MDMCTRL0_PREAMBLE_M);
    reg |= CC2420_MDMCTRL0_PREAMBLE_3B;
    reg |= CC2420_MDMCTRL0_AUTOCRC;
    cc2420_reg_write(dev, CC2420_REG_MDMCTRL0, reg);

    /* go into RX state */
    cc2420_set_state(dev, CC2420_GOTO_RX);

    return 0;
}


bool cc2420_cca(cc2420_t *dev)
{
    while (!(cc2420_status(dev) & CC2420_STATUS_RSSI_VALID)) {}
    return gpio_read(dev->params.pin_cca);
}

size_t cc2420_send(cc2420_t *dev, const struct iovec *data, unsigned count)
{
    size_t n = cc2420_tx_prepare(dev, data, count);

    if ((n > 0) && !(dev->options & CC2420_OPT_PRELOADING)) {
        cc2420_tx_exec(dev);
    }

    return n;
}

size_t cc2420_tx_prepare(cc2420_t *dev, const struct iovec *data, unsigned count)
{
    size_t pkt_len = 2;     /* include the FCS (frame check sequence) */

    /* wait for any ongoing transmissions to be finished */
    while (cc2420_get_state(dev) & NETOPT_STATE_TX) {}

    /* get and check the length of the packet */
    for (unsigned i = 0; i < count; i++) {
        pkt_len += data[i].iov_len;
    }
    if (pkt_len >= CC2420_PKT_MAXLEN) {
        DEBUG("cc2420: tx_prep: unable to send, pkt too large\n");
        return 0;
    }

    /* flush TX FIFO and write new packet to it */
    cc2420_strobe(dev, CC2420_STROBE_FLUSHTX);
    /* push packet length to TX FIFO */
    cc2420_fifo_write(dev, (uint8_t *)&pkt_len, 1);
    /* push packet to TX FIFO */
    for (int i = 0; i < count; i++) {
        cc2420_fifo_write(dev, (uint8_t *)data[i].iov_base, data[i].iov_len);
    }
    DEBUG("cc2420: tx_prep: loaded %i byte into the TX FIFO\n", (int)pkt_len);

    return pkt_len;
}

void cc2420_tx_exec(cc2420_t *dev)
{
    /* make sure, any ongoing transmission is finished */
    DEBUG("cc2420: tx_exec: waiting for any ongoing transmission\n");
    while (cc2420_get_state(dev) & NETOPT_STATE_TX) {}
    /* trigger the transmission */
    if (dev->options & CC2420_OPT_TELL_TX_START) {
        dev->netdev.netdev.event_callback(&dev->netdev.netdev,
                                          NETDEV2_EVENT_TX_STARTED);
    }
    DEBUG("cc2420: tx_exec: TX_START\n");
    if (dev->options & CC2420_OPT_CSMA) {
        DEBUG("cc2420: tx_exec: triggering TX with CCA\n");
        cc2420_strobe(dev, CC2420_STROBE_TXONCCA);
    }
    else {
        DEBUG("cc2420: tx_exec: triggering TX without CCA\n");
        cc2420_strobe(dev, CC2420_STROBE_TXON);
    }

    while (gpio_read(dev->params.pin_sfd)) {
        puts("\t...ongoing}");
    }
    if (dev->options & CC2420_OPT_TELL_TX_END) {
        dev->netdev.netdev.event_callback(&dev->netdev.netdev,
                                          NETDEV2_EVENT_TX_COMPLETE);
    }
    DEBUG("cc2420: tx_exec: TX_DONE\n");
}

int cc2420_rx(cc2420_t *dev, uint8_t *buf, size_t max_len, void *info)
{
    uint8_t len;
    uint8_t crc_corr;

    /* without a provided buffer, only readout the length and return it */
    if (buf == NULL) {
        /* get the packet length (without dropping it) (first byte in RX FIFO) */
        cc2420_ram_read(dev, CC2420_RAM_RXFIFO, &len, 1);
        len -= 2;   /* subtract RSSI and FCF */
        DEBUG("cc2420: recv: packet of length %i in RX FIFO\n", (int)len);
    }
    else {
        /* read length byte */
        cc2420_fifo_read(dev, &len, 1);
        len -= 2;   /* subtract RSSI and FCF */

        /* if a buffer is given, read (and drop) the packet */
        len = (len > max_len) ? max_len : len;

        /* read fifo contents */
        DEBUG("cc2420: recv: reading %i byte of the packet\n", (int)len);
        cc2420_fifo_read(dev, buf, len);

        uint8_t rssi;
        cc2420_fifo_read(dev, &rssi, 1);
        DEBUG("cc2420: recv: RSSI is %i\n", (int)rssi);

        /* fetch and check if CRC_OK bit (MSB) is set */
        cc2420_fifo_read(dev, &crc_corr, 1);
        if (!(crc_corr & 0x80)) {
            DEBUG("cc2420: recv: CRC_OK bit not set, dropping packet\n");
            /* drop the corrupted frame from the RXFIFO */
            len = 0;
        }

        /* finally flush the FIFO */
        cc2420_strobe(dev, CC2420_STROBE_FLUSHRX);
        cc2420_strobe(dev, CC2420_STROBE_FLUSHRX);
    }

    return (int)len;
}