nuttx/drivers/wireless/ieee802154/at86rf23x.c
2016-06-01 12:12:21 +02:00

1480 lines
44 KiB
C

/****************************************************************************
* drivers/wireless/ieee802154/at86rf23x.c
*
* Copyright (C) 2016 Matt Poppe. All rights reserved.
* Author: Matt Poppe <matt@poppe.me>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <assert.h>
#include <debug.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <semaphore.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/fs/fs.h>
#include <nuttx/spi/spi.h>
#include <nuttx/wireless/ieee802154/at86rf23x.h>
#include <nuttx/wireless/ieee802154/ieee802154.h>
#include <nuttx/wireless/ieee802154/ieee802154_radio.h>
#include <nuttx/ieee802154/ieee802154_dev.h>
#include "at86rf23x.h"
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
#ifndef CONFIG_SCHED_HPWORK
#error High priority work queue required in this driver
#endif
#ifndef CONFIG_IEEE802154_at86rf23x_SPIMODE
# define CONFIG_IEEE802154_at86rf23x_SPIMODE SPIDEV_MODE0
#endif
#ifndef CONFIG_IEEE802154_at86rf23x_FREQUENCY
# define CONFIG_IEEE802154_at86rf23x_FREQUENCY 1000000
#endif
/* Definitions for the device structure */
#define AT86RF23X_RXMODE_NORMAL 0
#define AT86RF23X_RXMODE_PROMISC 1
#define AT86RF23X_RXMODE_NOCRC 2
#define AT86RF23X_RXMODE_AUTOACK 3
/* Definitions for PA control on high power modules */
#define AT86RF23X_PA_AUTO 1
#define AT86RF23X_PA_ED 2
#define AT86RF23X_PA_SLEEP 3
/************************************************************************************
* Private Types
************************************************************************************/
/*
* AT86RF23x device instance
*
* Make sure that struct ieee802154_dev_s remains first. If not it will break the
* code
*
*/
struct at86rf23x_dev_s
{
struct ieee802154_dev_s ieee; /* Public device instance */
FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
struct work_s irqwork; /* Interrupt continuation work queue support */
FAR const struct at86rf23x_lower_s *lower; /* Low-level MCU-specific support */
uint16_t panid; /* PAN identifier, FFFF = not set */
uint16_t saddr; /* short address, FFFF = not set */
uint8_t eaddr[8]; /* extended address, FFFFFFFFFFFFFFFF = not set */
uint8_t channel; /* 11 to 26 for the 2.4 GHz band */
uint8_t devmode; /* device mode: device, coord, pancoord */
uint8_t paenabled; /* enable usage of PA */
uint8_t rxmode; /* Reception mode: Main, no CRC, promiscuous */
int32_t txpower; /* TX power in mBm = dBm/100 */
struct ieee802154_cca_s cca; /* Clear channel assessement method */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Internal operations */
static void at86rf23x_lock(FAR struct spi_dev_s *spi);
static void at86rf23x_unlock(FAR struct spi_dev_s *spi);
static void at86rf23x_setreg(FAR struct spi_dev_s *spi, uint32_t addr, uint8_t val);
static uint8_t at86rf23x_getreg(FAR struct spi_dev_s *spi, uint32_t addr);
static int at86rf23x_writeframe(FAR struct spi_dev_s *spi, uint8_t *frame, uint8_t len);
static uint8_t at86rf23x_readframe(FAR struct spi_dev_s *spi, uint8_t *frame_rx);
static int at86rf23x_setTRXstate(FAR struct at86rf23x_dev_s *dev, uint8_t state, uint8_t force);
static uint8_t at86rf23x_getTRXstate(FAR struct at86rf23x_dev_s *dev);
static int at86rf23x_resetrf(FAR struct at86rf23x_dev_s *dev);
static int at86rf23x_initialize(FAR struct at86rf23x_dev_s *dev);
static int at86rf23x_regdump(FAR struct at86rf23x_dev_s *dev);
static void at86rf23x_irqwork_rx(FAR struct at86rf23x_dev_s *dev);
static void at86rf23x_irqwork_tx(FAR struct at86rf23x_dev_s *dev);
static void at86rf23x_irqworker(FAR void *arg);
static int at86rf23x_interrupt(int irq, FAR void *context);
/* Driver operations */
static int at86rf23x_setchannel (FAR struct ieee802154_dev_s *ieee, uint8_t chan);
static int at86rf23x_getchannel (FAR struct ieee802154_dev_s *ieee, FAR uint8_t *chan);
static int at86rf23x_setpanid (FAR struct ieee802154_dev_s *ieee, uint16_t panid);
static int at86rf23x_getpanid (FAR struct ieee802154_dev_s *ieee, FAR uint16_t *panid);
static int at86rf23x_setsaddr (FAR struct ieee802154_dev_s *ieee, uint16_t saddr);
static int at86rf23x_getsaddr (FAR struct ieee802154_dev_s *ieee, FAR uint16_t *saddr);
static int at86rf23x_seteaddr (FAR struct ieee802154_dev_s *ieee, FAR uint8_t *eaddr);
static int at86rf23x_geteaddr (FAR struct ieee802154_dev_s *ieee, FAR uint8_t *eaddr);
static int at86rf23x_setpromisc (FAR struct ieee802154_dev_s *ieee, bool promisc);
static int at86rf23x_getpromisc (FAR struct ieee802154_dev_s *ieee, FAR bool *promisc);
static int at86rf23x_setdevmode (FAR struct ieee802154_dev_s *ieee, uint8_t mode);
static int at86rf23x_getdevmode (FAR struct ieee802154_dev_s *ieee, FAR uint8_t *mode);
static int at86rf23x_settxpower (FAR struct ieee802154_dev_s *ieee, int32_t txpwr);
static int at86rf23x_gettxpower (FAR struct ieee802154_dev_s *ieee, FAR int32_t *txpwr);
static int at86rf23x_setcca (FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_cca_s *cca);
static int at86rf23x_getcca (FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_cca_s *cca);
static int at86rf23x_ioctl (FAR struct ieee802154_dev_s *ieee, int cmd, unsigned long arg);
static int at86rf23x_energydetect(FAR struct ieee802154_dev_s *ieee, FAR uint8_t *energy);
static int at86rf23x_rxenable (FAR struct ieee802154_dev_s *ieee, bool state, FAR struct ieee802154_packet_s *packet);
static int at86rf23x_transmit (FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_packet_s *packet);
/* These are pointers to ALL registered at86rf23x devices.
* This table is used during irqs to find the context
* Only one device is supported for now.
* More devices can be supported in the future by lookup them up
* using the IRQ number. See the ENC28J60 or CC3000 drivers for reference.
*/
static struct at86rf23x_dev_s g_at86rf23x_devices[1];
static const struct ieee802154_devops_s at86rf23x_devops =
{
.setchannel = at86rf23x_setchannel,
.getchannel = at86rf23x_getchannel,
.setpanid = at86rf23x_setpanid,
.getpanid = at86rf23x_getpanid,
.setsaddr = at86rf23x_setsaddr,
.getsaddr = at86rf23x_getsaddr,
.seteaddr = at86rf23x_seteaddr,
.geteaddr = at86rf23x_geteaddr,
.setpromisc = at86rf23x_setpromisc,
.getpromisc = at86rf23x_getpromisc,
.setdevmode = at86rf23x_setdevmode,
.getdevmode = at86rf23x_getdevmode,
.settxpower = at86rf23x_settxpower,
.gettxpower = at86rf23x_gettxpower,
.setcca = at86rf23x_setcca,
.getcca = at86rf23x_getcca,
.ioctl = at86rf23x_ioctl,
.energydetect = at86rf23x_energydetect,
.rxenable = at86rf23x_rxenable,
.transmit = at86rf23x_transmit
};
/****************************************************************************
* Private Functions
****************************************************************************/
#ifndef CONFIG_SPI_EXCHANGE
#error CONFIG_SPI_EXCHANGE required for this driver
#endif
/* hardware access routines */
/****************************************************************************
* Name: at86rf23x_lock
*
* Description:
* Acquire exclusive access to the shared SPI bus.
*
****************************************************************************/
static void at86rf23x_lock(FAR struct spi_dev_s *spi)
{
SPI_LOCK (spi, 1);
SPI_SETBITS (spi, 8);
SPI_SETMODE (spi, CONFIG_IEEE802154_at86rf23x_SPIMODE );
SPI_SETFREQUENCY(spi, CONFIG_IEEE802154_at86rf23x_FREQUENCY );
}
/****************************************************************************
* Name: at86rf23x_unlock
*
* Description:
* Release exclusive access to the shared SPI bus.
*
****************************************************************************/
static void at86rf23x_unlock(FAR struct spi_dev_s *spi)
{
SPI_LOCK(spi,0);
}
/****************************************************************************
* Name: at86rf23x_setreg
*
* Description:
* Define the value of an at86rf23x device register
*
****************************************************************************/
static void at86rf23x_setreg(FAR struct spi_dev_s *spi, uint32_t addr, uint8_t val)
{
uint8_t reg[2];
reg[0] = addr;
reg[0] |= RF23X_SPI_REG_WRITE;
reg[1] = val;
at86rf23x_lock(spi);
SPI_SELECT(spi, SPIDEV_IEEE802154, true);
SPI_SNDBLOCK(spi, reg, 2);
SPI_SELECT(spi, SPIDEV_IEEE802154, false);
at86rf23x_unlock(spi);
vdbg("0x%02X->r[0x%02X]\n", val, addr);
}
/****************************************************************************
* Name: at86rf23x_getreg
*
* Description:
* Return the value of an at86rf23x device register
*
****************************************************************************/
static uint8_t at86rf23x_getreg(FAR struct spi_dev_s *spi, uint32_t addr)
{
uint8_t reg[2];
uint8_t val[2];
/* Add Read mask to desired register */
reg[0] = addr | RF23X_SPI_REG_READ;
at86rf23x_lock (spi);
SPI_SELECT (spi, SPIDEV_IEEE802154, true);
SPI_EXCHANGE (spi, reg, val, 2);
SPI_SELECT (spi, SPIDEV_IEEE802154, false);
at86rf23x_unlock(spi);
vdbg("r[0x%02X]=0x%02X\n",addr,val[1]);
return val[1];
}
/****************************************************************************
* Name: at86rf23x_setregbits
*
* Description:
* Read the current value of the register. Change only the portion
* of the register we need to change and write the value back to the
* register.
*
****************************************************************************/
static void at86rf23x_setregbits(FAR struct spi_dev_s *spi,
uint8_t addr,
uint8_t pos,
uint8_t mask,
uint8_t val)
{
uint8_t reg;
reg = at86rf23x_getreg(spi, addr);
reg = (reg & ~(mask << pos)) | (val << pos);
at86rf23x_setreg(spi, addr, reg);
}
/****************************************************************************
* Name: at86rf23x_getregbits
*
* Description:
* Return the value of an section of the at86rf23x device register.
*
****************************************************************************/
static uint8_t at86rf23x_getregbits(FAR struct spi_dev_s *spi,
uint8_t addr,
uint8_t pos,
uint8_t mask)
{
uint8_t val;
val = at86rf23x_getreg(spi, addr);
val = (val >> pos) & mask;
return val;
}
/****************************************************************************
* Name: at86rf23x_writebuffer
*
* Description:
* Write frame to the transmit buffer of the radio. This does not
* initiate the transfer, just write to the buffer.
*
****************************************************************************/
static int at86rf23x_writeframe(FAR struct spi_dev_s *spi, uint8_t *frame, uint8_t len)
{
// report_packet(frame_wr, len);
uint8_t reg = RF23X_SPI_FRAME_WRITE;
at86rf23x_lock(spi);
SPI_SELECT(spi, SPIDEV_IEEE802154, true);
SPI_SNDBLOCK(spi, &reg, 1);
SPI_SNDBLOCK(spi, &frame, len);
SPI_SELECT(spi, SPIDEV_IEEE802154, false);
at86rf23x_unlock(spi);
return len;
}
/****************************************************************************
* Name: at86rf23x_readframe
*
* Description:
* Read the buffer memory of the radio. This is used when the radio
* indicates a frame has been received.
*
****************************************************************************/
static uint8_t at86rf23x_readframe(FAR struct spi_dev_s *spi, uint8_t *frame_rx)
{
uint8_t len, reg;
reg = RF23X_SPI_FRAME_READ;
at86rf23x_lock(spi);
SPI_SELECT(spi, SPIDEV_IEEE802154, true);
SPI_SNDBLOCK(spi, &reg, 1);
SPI_RECVBLOCK(spi, &len, 1);
SPI_RECVBLOCK(spi, frame_rx, len+3);
SPI_SELECT(spi, SPIDEV_IEEE802154, false);
at86rf23x_unlock(spi);
return len;
}
/****************************************************************************
* Name: at86rf23x_getTRXstate
*
* Description:
* Return the current status of the TRX state machine.
*
****************************************************************************/
static uint8_t at86rf23x_getTRXstate(FAR struct at86rf23x_dev_s *dev)
{
return at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
}
/****************************************************************************
* Name: at86rf23x_setTRXstate
*
* Description:
* Set the TRX state machine to the desired state. If the state machine
* cannot move to the desired state an ERROR is returned. If the transistion
* is successful an OK is returned. This is a long running function due to
* waiting for the transistion delay between states. This can be as long as
* 510us.
*
****************************************************************************/
static int at86rf23x_setTRXstate(FAR struct at86rf23x_dev_s *dev, uint8_t state, uint8_t force)
{
/* Get the current state of the transceiver */
uint8_t status = at86rf23x_getTRXstate(dev);
int ret = OK;
/* TODO I don't have every state included verify this will work with SLEEP */
if((status != TRX_STATUS_TRXOFF) &&
(status != TRX_STATUS_RXON) &&
(status != TRX_STATUS_PLLON) &&
(status != TRX_STATUS_RXAACKON) &&
(status != TRX_STATUS_TXARETON) &&
(status != TRX_STATUS_PON))
{
dbg("Invalid State\n");
return ERROR;
}
int8_t init_status = status;
/* Start the state change */
switch(state)
{
case TRX_CMD_TRXOFF:
if(status == TRX_STATUS_TRXOFF)
{
break;
}
/* verify in a state that will transfer to TRX_OFF if not check if force is required */
if( (status == TRX_STATUS_RXON) ||
(status == TRX_STATUS_PLLON) ||
(status == TRX_STATUS_RXAACKON) ||
(status == TRX_STATUS_TXARETON))
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_TRXOFF);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
else if(status == TRX_STATUS_PON)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_TRXOFF);
up_udelay(RF23X_TIME_P_ON_TO_TRXOFF);
}
else if(force)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_FORCETRXOFF);
up_udelay(RF23X_TIME_FORCE_TRXOFF);
}
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
if(status != TRX_STATUS_TRXOFF)
{
ret = ERROR;
}
break;
case TRX_CMD_RX_ON:
if(status == TRX_STATUS_RXON)
{
break;
}
if (status == TRX_STATUS_TRXOFF)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_ON);
up_udelay(RF23X_TIME_TRXOFF_TO_PLL);
}
else if((status == TRX_STATUS_RXAACKON) ||
(status == TRX_STATUS_RXON) ||
(status == TRX_STATUS_PLLON) ||
(status == TRX_STATUS_TXARETON))
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
if(status != TRX_STATUS_RXON)
{
ret = ERROR;
}
break;
/* transition to PLL_ON */
case TRX_CMD_PLL_ON:
if(status == TRX_STATUS_PLLON)
{
break;
}
if (status == TRX_STATUS_TRXOFF)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_PLL_ON);
up_udelay(RF23X_TIME_TRXOFF_TO_PLL);
}
else
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_PLL_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
if(status != TRX_STATUS_PLLON)
{
ret = ERROR;
}
break;
case TRX_CMD_RX_AACK_ON:
if(status == TRX_STATUS_RXAACKON)
{
break;
}
if (status == TRX_STATUS_TRXOFF || status == TRX_STATUS_TXARETON)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_ON);
(status == TRX_STATUS_TRXOFF) ? up_udelay(RF23X_TIME_TRXOFF_TO_PLL) :
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
status = at86rf23x_getTRXstate(dev);
if ((status == TRX_STATUS_RXON) || (status == TRX_STATUS_PLLON))
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_AACK_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
if(status != TRX_STATUS_RXAACKON)
{
ret = ERROR;
}
break;
case TRX_STATUS_TXARETON:
if(status == TRX_STATUS_TXARETON)
{
break;
}
if (status == TRX_STATUS_TRXOFF)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_ON);
up_udelay(RF23X_TIME_TRXOFF_TO_PLL);
}
if ((status == TRX_STATUS_RXON) || (status == TRX_STATUS_PLLON))
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_TX_ARET_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
else if (status == TRX_STATUS_RXAACKON)
{
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_RX_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_TX_ARET_ON);
up_udelay(RF23X_TIME_TRANSITION_PLL_ACTIVE);
}
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
if(status != TRX_STATUS_TXARETON)
{
ret = ERROR;
}
break;
case TRX_STATUS_SLEEP:
at86rf23x_setregbits(dev->spi, RF23X_TRXCMD_STATE, TRX_CMD_FORCETRXOFF);
up_udelay(RF23X_TIME_CMD_FORCE_TRX_OFF);
dev->lower->slptr(dev->lower, true);
up_udelay(RF23X_TIME_TRXOFF_TO_SLEEP);
status = at86rf23x_getregbits(dev->spi, RF23X_TRXSTATUS_STATUS);
break;
default:
dbg("%s \n", EINVAL_STR);
init_status = 0;/* Placed this here to keep compiler happy when not in debug */
return -EINVAL;
}
if(ret == ERROR)
{
dbg("State Transistion Error\n");
}
vdbg("Radio state change state[0x%02x]->state[0x%02x]\n", init_status, status);
return ret;
}
/* Publicized driver routines */
/****************************************************************************
* Name: at86rf23x_setchannel
*
* Description:
* Define the current radio channel the device is operating on.
* In the 2.4 GHz, there are 16 channels, each 2 MHz wide, 5 MHz spacing:
* Chan MHz Chan MHz Chan MHz Chan MHz
* 11 2405 15 2425 19 2445 23 2465
* 12 2410 16 2430 20 2450 24 2470
* 13 2415 17 2435 21 2455 25 2475
* 14 2420 18 2440 22 2460 26 2480
*
*
****************************************************************************/
static int at86rf23x_setchannel(FAR struct ieee802154_dev_s *ieee, uint8_t chan)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
uint8_t state;
/* Validate if chan is a valid channel */
if(chan<11 || chan>26)
{
dbg("Invalid requested chan: %d\n",chan);
return -EINVAL;
}
/* Validate we are in an acceptable mode to change the channel */
state = at86rf23x_getTRXstate(dev);
if((TRX_STATUS_SLEEP == state) || (TRX_STATUS_PON == state)) {
dbg("Radio in invalid mode [%02x] to set the channel\n", state);
return ERROR;
}
/* Set the Channel on the transceiver */
at86rf23x_setregbits(dev->spi, RF23X_CCA_BITS_CHANNEL, chan);
/* Set the channel within local device */
dev->channel = chan;
vdbg("CHANNEL Changed to %d\n", chan);
return OK;
}
/****************************************************************************
* Name: at86rf23x_getchannel
*
* Description:
* Define the current radio channel the device is operating on.
* In the 2.4 GHz, there are 16 channels, each 2 MHz wide, 5 MHz spacing:
* Chan MHz Chan MHz Chan MHz Chan MHz
* 11 2405 15 2425 19 2445 23 2465
* 12 2410 16 2430 20 2450 24 2470
* 13 2415 17 2435 21 2455 25 2475
* 14 2420 18 2440 22 2460 26 2480
*
* This section assumes that the transceiver is not in SLEEP or P_ON state.
*
*
****************************************************************************/
static int at86rf23x_getchannel(FAR struct ieee802154_dev_s *ieee, FAR uint8_t *chan)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
/* Set the Channel on the transceiver */
*chan = at86rf23x_getregbits(dev->spi, RF23X_CCA_BITS_CHANNEL);
/* Set the channel within local device */
dev->channel = *chan;
return OK;
}
/****************************************************************************
* Name: at86rf23x_setpanid
*
* Description:
* Define the PAN ID for the network.
*
****************************************************************************/
static int at86rf23x_setpanid(FAR struct ieee802154_dev_s *ieee, uint16_t panid)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
uint8_t *pan = (uint8_t *)&panid;
at86rf23x_setreg(dev->spi, RF23X_REG_PANID0, pan[0]);
at86rf23x_setreg(dev->spi, RF23X_REG_PANID1, pan[1]);
return OK;
}
/****************************************************************************
* Name: at86rf23x_getpanid
*
* Description:
* Define the PAN ID for the network.
*
****************************************************************************/
static int at86rf23x_getpanid(FAR struct ieee802154_dev_s *ieee, FAR uint16_t *panid)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
uint8_t *pan = (uint8_t *)panid;
/* TODO: Check if we need to pay attention to endianness */
pan[0] = at86rf23x_getreg(dev->spi, RF23X_REG_PANID0);
pan[1] = at86rf23x_getreg(dev->spi, RF23X_REG_PANID1);
return OK;
}
/****************************************************************************
* Name: at86rf23x_setsaddr
*
* Description:
* Define the Short Address for the device.
*
****************************************************************************/
static int at86rf23x_setsaddr(FAR struct ieee802154_dev_s *ieee, uint16_t saddr)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
uint8_t *addr = (uint8_t *)&saddr;
at86rf23x_setreg(dev->spi, RF23X_REG_SADDR0, addr[0]);
at86rf23x_setreg(dev->spi, RF23X_REG_SADDR1, addr[1]);
return OK;
}
/****************************************************************************
* Name: at86rf23x_getsaddr
*
* Description:
* Get the short address of the device.
*
****************************************************************************/
static int at86rf23x_getsaddr(FAR struct ieee802154_dev_s *ieee, FAR uint16_t *saddr)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
uint8_t *addr = (uint8_t *)saddr;
/* TODO: Check if we need to pay attention to endianness */
addr[0] = at86rf23x_getreg(dev->spi, RF23X_REG_SADDR0);
addr[1] = at86rf23x_getreg(dev->spi, RF23X_REG_SADDR1);
return OK;
}
/****************************************************************************
* Name: at86rf23x_seteaddr
*
* Description:
* Set the IEEE address of the device.
*
****************************************************************************/
static int at86rf23x_seteaddr(FAR struct ieee802154_dev_s *ieee, FAR uint8_t *eaddr)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* TODO: Check if we need to pay attention to endianness */
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR0, eaddr[0]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR1, eaddr[1]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR2, eaddr[2]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR3, eaddr[3]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR4, eaddr[4]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR5, eaddr[5]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR6, eaddr[6]);
at86rf23x_setreg(dev->spi, RF23X_REG_IEEEADDR7, eaddr[7]);
return OK;
}
/****************************************************************************
* Name: at86rf23x_geteaddr
*
* Description:
* Get the IEEE address of the device.
*
****************************************************************************/
static int at86rf23x_geteaddr(FAR struct ieee802154_dev_s *ieee, FAR uint8_t *eaddr)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* TODO: Check if we need to pay attention to endianness */
eaddr[0] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR0);
eaddr[1] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR1);
eaddr[2] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR2);
eaddr[3] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR3);
eaddr[4] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR4);
eaddr[5] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR5);
eaddr[6] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR6);
eaddr[7] = at86rf23x_getreg(dev->spi, RF23X_REG_IEEEADDR7);
return OK;
}
/****************************************************************************
* Name: at86rf23x_setpromisc
*
* Description:
* enable/disable promiscuous mode.
*
****************************************************************************/
static int at86rf23x_setpromisc(FAR struct ieee802154_dev_s *ieee, bool promisc)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* TODO: Check what mode I should be in to activate promiscuous mode:
* This is way to simple of an implementation. Many other things should be set
* and/or checked before we set the device into promiscuous mode
* */
at86rf23x_setregbits(dev->spi, RF23X_XAHCTRL1_BITS_PROM_MODE, promisc);
return OK;
}
/****************************************************************************
* Name: at86rf23x_getpromisc
*
* Description:
* Check if the device is in promiscuous mode.
*
****************************************************************************/
static int at86rf23x_getpromisc(FAR struct ieee802154_dev_s *ieee, FAR bool *promisc)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
*promisc = at86rf23x_getregbits(dev->spi, RF23X_XAHCTRL1_BITS_PROM_MODE);
return OK;
}
/****************************************************************************
* Name: at86rf23x_setdevmode
*
* Description:
* Check if the device is in promiscuous mode.
*
****************************************************************************/
static int at86rf23x_setdevmode(FAR struct ieee802154_dev_s *ieee, uint8_t mode)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* define dev mode */
if(mode == IEEE802154_MODE_PANCOORD)
{
at86rf23x_setregbits(dev->spi, RF23X_CSMASEED1_IAMCOORD_BITS, 0x01);
}
else if(mode == IEEE802154_MODE_COORD)
{
/* ????? */
}
else if(mode == IEEE802154_MODE_DEVICE)
{
at86rf23x_setregbits(dev->spi, RF23X_CSMASEED1_IAMCOORD_BITS, 0x00);
}
else
{
return -EINVAL;
}
dev->devmode = mode;
return OK;
}
/****************************************************************************
* Name: at86rf23x_getdevmode
*
* Description:
* get the device mode type of the radio.
*
****************************************************************************/
static int at86rf23x_getdevmode (FAR struct ieee802154_dev_s *ieee, FAR uint8_t *mode)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
int val;
val = at86rf23x_getregbits(dev->spi, RF23X_CSMASEED1_IAMCOORD_BITS);
if(val == 1)
{
mode = IEEE802154_MODE_PANCOORD;
}
else
{
mode = IEEE802154_MODE_DEVICE;
}
return OK;
}
/****************************************************************************
* Name: at86rf23x_settxpower
*
* Description:
* set the tx power attenuation or amplification
*
****************************************************************************/
static int at86rf23x_settxpower(FAR struct ieee802154_dev_s *ieee, int32_t txpwr)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* TODO: this needs alot of work to make sure all chips can share this function */
/* Right now we only set tx power to 0 */
at86rf23x_setreg(dev->spi, RF23X_REG_TXPWR, RF23X_TXPWR_0);
return OK;
}
/****************************************************************************
* Name: at86rf23x_gettxpower
*
* Description:
* get the tx power attenuation or amplification.
*
****************************************************************************/
static int at86rf23x_gettxpower(FAR struct ieee802154_dev_s *ieee, FAR int32_t *txpwr)
{
/* TODO: this needs alot of work to make sure all chips can share this function */
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
uint8_t reg;
/* TODO: this needs alot of work to make sure all chips can share this function */
/* Right now we only get negative values */
reg = at86rf23x_getreg(dev->spi, RF23X_REG_TXPWR);
switch(reg)
{
case RF23X_TXPWR_POS_4:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_3_7:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_3_4:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_3:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_2_5:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_2:
*txpwr = 0;
break;
case RF23X_TXPWR_POS_1:
*txpwr = 0;
break;
case RF23X_TXPWR_0:
*txpwr = 0;
break;
case RF23X_TXPWR_NEG_1:
*txpwr = 1000;
break;
case RF23X_TXPWR_NEG_2:
*txpwr = 2000;
break;
case RF23X_TXPWR_NEG_3:
*txpwr = 3000;
break;
case RF23X_TXPWR_NEG_4:
*txpwr = 4000;
break;
case RF23X_TXPWR_NEG_6:
*txpwr = 6000;
break;
case RF23X_TXPWR_NEG_8:
*txpwr = 8000;
break;
case RF23X_TXPWR_NEG_12:
*txpwr = 12000;
break;
case RF23X_TXPWR_NEG_17:
*txpwr = 17000;
break;
}
return OK;
}
/****************************************************************************
* Name: at86rf23x_setcca
*
* Description:
* Configures if energy detection is used or carrier sense. The base
* measurement is configured here as well
*
*
****************************************************************************/
static int at86rf23x_setcca(FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_cca_s *cca)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
/* TODO: This doesn't fit the RF233 completely come back to this */
if (!cca->use_ed && !cca->use_cs)
{
return -EINVAL;
}
if (cca->use_cs && cca->csth > 0x0f)
{
return -EINVAL;
}
if (cca->use_ed)
{
at86rf23x_setregbits(dev->spi, RF23X_CCA_BITS_MODE, RF23X_CCA_MODE_ED);
}
if (cca->use_cs)
{
at86rf23x_setregbits(dev->spi, RF23X_CCA_BITS_MODE, RF23X_CCA_MODE_CS);
}
memcpy(&dev->cca, cca, sizeof(struct ieee802154_cca_s));
return OK;
}
/****************************************************************************
* Name: at86rf23x_getcca
*
* Description:
* Get CCA for ???: TODO: need to implement
*
****************************************************************************/
static int at86rf23x_getcca(FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_cca_s *cca)
{
FAR struct at86rf23x_dev_s *dev = (struct at86rf23x_dev_s *)ieee;
#warning at86rf23x_getcca not implemented.
UNUSED(dev);
UNUSED(cca);
return ERROR;
}
/****************************************************************************
* Name: at86rf23x_ioctl
*
* Description:
* Control operations for the radio.
*
****************************************************************************/
static int at86rf23x_ioctl(FAR struct ieee802154_dev_s *ieee, int cmd, unsigned long arg)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
switch(cmd)
{
case MAC854IORDUMP: return at86rf23x_regdump(dev);
default: return -EINVAL;
}
}
/****************************************************************************
* Name: at86rf23x_energydetect
*
* Description:
* Perform energy detection scan. TODO: Need to implement.
*
****************************************************************************/
static int at86rf23x_energydetect(FAR struct ieee802154_dev_s *ieee, FAR uint8_t *energy)
{
#warning at86rf23x_energydetect not implemented.
/* Not yet implemented */
return ERROR;
}
/****************************************************************************
* Name: at86rf23x_initialize
*
* Description:
* Initialize the radio.
*
****************************************************************************/
int at86rf23x_initialize(FAR struct at86rf23x_dev_s *dev)
{
uint8_t part;
uint8_t version;
at86rf23x_resetrf(dev);
part = at86rf23x_getreg(dev->spi, RF23X_REG_PART);
version = at86rf23x_getreg(dev->spi, RF23X_REG_VERSION);
dbg("Radio part: 0x%02x version: 0x%02x found\n", part, version);
return OK;
}
/****************************************************************************
* Name: at86rf23x_resetrf
*
* Description:
* Hard Reset of the radio. The reset also brings the radio into the
* TRX_OFF state.
*
****************************************************************************/
static int at86rf23x_resetrf(FAR struct at86rf23x_dev_s *dev)
{
FAR const struct at86rf23x_lower_s *lower = dev->lower;
uint8_t trx_status, retry_cnt = 0;
/* Reset the radio */
lower->reset(lower, 0);
lower->slptr(lower, 0);
up_udelay(RF23X_TIME_RESET);
lower->reset(lower, 1);
/* Dummy read of IRQ register */
at86rf23x_getreg(dev->spi, RF23X_REG_IRQ_STATUS);
do
{
trx_status = at86rf23x_setTRXstate(dev, TRX_CMD_TRXOFF, true);
if (retry_cnt == RF23X_MAX_RETRY_RESET_TO_TRX_OFF)
{
dbg("Reset of transceiver failed\n");
return ERROR;
}
retry_cnt++;
} while (trx_status != OK);
return OK;
}
/****************************************************************************
* Name: at86rf23x_rxenable
*
* Description:
* puts the radio into RX mode and brings in the buffer to handle
* RX messages.
*
****************************************************************************/
static int at86rf23x_rxenable(FAR struct ieee802154_dev_s *ieee, bool state,
FAR struct ieee802154_packet_s *packet)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
/* Set the radio to the receive state */
return at86rf23x_setTRXstate(dev, TRX_CMD_RX_ON, false);
/* Enable the RX IRQ */
/* TODO:
* I am not sure what to do here since the at86rf23x shares the
* irq with the tx finished. Better planning needs to be done on
* my end.
*/
/* Set buffer to receive next packet */
ieee->rxbuf = packet;
}
/****************************************************************************
* Name: at86rf23x_interrupt
*
* Description:
* Actual interrupt handler ran inside privileged space.
*
****************************************************************************/
static int at86rf23x_interrupt(int irq, FAR void *context)
{
/* To support multiple devices,
* retrieve the priv structure using the irq number */
register FAR struct at86rf23x_dev_s *dev = &g_at86rf23x_devices[0];
/* In complex environments, we cannot do SPI transfers from the interrupt
* handler because semaphores are probably used to lock the SPI bus. In
* this case, we will defer processing to the worker thread. This is also
* much kinder in the use of system resources and is, therefore, probably
* a good thing to do in any event.
*/
DEBUGASSERT(work_available(&dev->irqwork));
/* Notice that further GPIO interrupts are disabled until the work is
* actually performed. This is to prevent overrun of the worker thread.
* Interrupts are re-enabled in enc_irqworker() when the work is completed.
*/
dev->lower->irq(dev->lower, NULL, FALSE);
// dev->lower->enable(dev->lower, FALSE);
return work_queue(HPWORK, &dev->irqwork, at86rf23x_irqworker, (FAR void *)dev, 0);
}
/****************************************************************************
* Name: at86rf23x_regdump
*
* Description:
* Dumps all the RF23X radios registers from 00 - 2F there are a few other
* registers that don't get dumped but just fore the ease of code I left
* them out.
*
****************************************************************************/
static int at86rf23x_regdump(FAR struct at86rf23x_dev_s *dev)
{
uint32_t i;
char buf[4+16*3+2+1];
int len=0;
printf("RF23X regs:\n");
for (i=0;i<0x30;i++)
{
/* First row and every 15 regs */
if ((i & 0x0f) == 0)
{
len = sprintf(buf, "%02x: ",i&0xFF);
}
/* print the register value */
len += sprintf(buf+len, "%02x ", at86rf23x_getreg(dev->spi, i));
/* at the end of each 15 regs or end of rf233s regs and actually print dbg message*/
if ((i&15)==15 || i == 0x2f)
{
sprintf(buf+len, "\n");
printf("%s",buf);
}
}
/* TODO: I have a few more regs that are not consecutive. Will print later */
return 0;
}
/****************************************************************************
* Name: at86rf23x_irqworker
*
* Description:
* Actual thread to handle the irq outside of privaleged mode.
*
****************************************************************************/
static void at86rf23x_irqworker(FAR void *arg)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)arg;
uint8_t irq_status = at86rf23x_getreg(dev->spi, RF23X_REG_IRQ_STATUS);
vdbg("IRQ: 0x%02X\n", irq_status);
if((irq_status & (1<<3)) != 0)
{
if((irq_status & (1<<2)) != 0)
{
at86rf23x_irqwork_rx(dev);
}
else
{
at86rf23x_irqwork_tx(dev);
}
}
else
{
dbg("Unknown IRQ Status: %d\n", irq_status);
/* Re enable the IRQ even if we don't know how to handle previous status*/
dev->lower->irq(dev->lower, NULL, true);
}
}
/****************************************************************************
* Name: at86rf23x_irqwork_rx
*
* Description:
* Misc/unofficial device controls.
*
****************************************************************************/
static void at86rf23x_irqwork_rx(FAR struct at86rf23x_dev_s *dev)
{
uint8_t rx_len;
vdbg("6LOWPAN:Rx IRQ\n");
rx_len = at86rf23x_readframe(dev->spi, dev->ieee.rxbuf->data);
dev->ieee.rxbuf->len = rx_len;
dev->ieee.rxbuf->lqi = 0;
dev->ieee.rxbuf->rssi = 0;
sem_post(&dev->ieee.rxsem);
/*
* TODO:
* Not to sure yet what I should do here. I will something
* soon.
*/
/* Re enable the IRQ */
dev->lower->irq(dev->lower, NULL, true);
}
/****************************************************************************
* Name: at86rf23x_irqwork_tx
*
* Description:
* Misc/unofficial device controls.
*
****************************************************************************/
static void at86rf23x_irqwork_tx(FAR struct at86rf23x_dev_s *dev)
{
vdbg("6LOWPAN:Tx IRQ\n");
/*
* TODO:
* There needs to be more here but for now just alert the waiting
* thread. Maybe put it back into Rx mode
*
*/
/* Re enable the IRQ */
dev->lower->irq(dev->lower, NULL, true);
sem_post(&dev->ieee.txsem);
}
/****************************************************************************
* Name: at86rf23x_transmit
*
* Description:
* transmission the packet. Send the packet to the radio and initiate the
* transmit process. Then block the function till we have a successful
* transmission
*
****************************************************************************/
static int at86rf23x_transmit(FAR struct ieee802154_dev_s *ieee, FAR struct ieee802154_packet_s *packet)
{
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
/*
* TODO:
* A plan needs to be made on when we declare the transmission successful.
* 1. If the packet is sent
* 2. If we receive an ACK.
* 3. Where do we control the retry process?
*/
if(at86rf23x_setTRXstate(dev, TRX_CMD_PLL_ON, false))
{
at86rf23x_writeframe(dev->spi, packet->data, packet->len);
}
else
{
dbg("Transmit could not put the radio in a Tx state\n");
return ERROR;
}
/* put the thread that requested transfer to a waiting state */
sem_wait(&dev->ieee.txsem);
/*TODO Verify that I want to stay in the PLL state or if I want to roll back to RX_ON */
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: at86rf23x_init
*
* Description:
* Return an at86rf23x device for use by other drivers.
*
****************************************************************************/
FAR struct ieee802154_dev_s *at86rf23x_init(FAR struct spi_dev_s *spi, FAR const struct at86rf23x_lower_s *lower)
{
FAR struct at86rf23x_dev_s *dev;
struct ieee802154_cca_s cca;
dev = &g_at86rf23x_devices[0];
/* Attach the interface, lower driver, and devops */
dev->spi = spi;
dev->lower = lower;
dev->ieee.ops = &at86rf23x_devops;
/* attach irq */
if (lower->irq(lower, at86rf23x_interrupt, false) != OK)
{
return NULL;
}
sem_init(&dev->ieee.rxsem, 0, 0);
sem_init(&dev->ieee.txsem, 0, 0);
/*Initialize device */
at86rf23x_initialize(dev);
/* Configure the desired IRQs of the devices */
at86rf23x_setreg(dev->spi, RF23X_REG_IRQ_MASK, RF23X_IRQ_MASK_DEFAULT);
/* Turn the PLL to the on state */
at86rf23x_setTRXstate(dev, TRX_CMD_PLL_ON, false);
/*SEED value of the CSMA backoff algorithm. */
#ifdef RF23X_ANTENNA_DIVERSITY
/* Use antenna diversity */
trx_bit_write(SR_ANT_CTRL, ANTENNA_DEFAULT);
trx_bit_write(SR_PDT_THRES, THRES_ANT_DIV_ENABLE);
trx_bit_write(SR_ANT_DIV_EN, ANT_DIV_ENABLE);
trx_bit_write(SR_ANT_EXT_SW_EN, ANT_EXT_SW_ENABLE);
#endif
#ifdef RF23X_TIMESTAMP
/* Enable timestamp init code goes here */
#endif
#ifdef RF23X_RF_FRONTEND_CTRL
/* Init front end control code goes here */
#endif
/* Set the channel of the radio */
at86rf23x_setchannel(&dev->ieee, 12);
/* Configure the Pan id */
// at86rf23x_setpanid (&dev->ieee, IEEE802154_PAN_DEFAULT);
/* Configure the Short Addr */
// at86rf23x_setsaddr (&dev->ieee, IEEE802154_SADDR_UNSPEC);
/* Configure the IEEE Addr */
// at86rf23x_seteaddr (&dev->ieee, IEEE802154_EADDR_UNSPEC);
/* Default device params at86rf23x defaults to energy detect only */
cca.use_ed = 1;
cca.use_cs = 0;
cca.edth = 0x60; /* CCA mode ED, no carrier sense, recommenced ED threshold -69 dBm */
at86rf23x_setcca(&dev->ieee, &cca);
/* Put the Device to RX ON Mode */
// at86rf23x_setTRXstate(dev, TRX_CMD_RX_ON, false );
/* Enable Radio IRQ */
lower->irq(lower, at86rf23x_interrupt, true);
return &dev->ieee;
}