1603 lines
47 KiB
C
1603 lines
47 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 "at86rf23x.h"
|
|
|
|
/************************************************************************************
|
|
* Pre-processor Definitions
|
|
************************************************************************************/
|
|
|
|
#ifndef CONFIG_SCHED_HPWORK
|
|
# error High priority work queue required in this driver
|
|
#endif
|
|
|
|
#ifndef CONFIG_SPI_EXCHANGE
|
|
# error CONFIG_SPI_EXCHANGE required for 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_radio_s remains first. If not it will break the
|
|
* code
|
|
*/
|
|
|
|
struct at86rf23x_dev_s
|
|
{
|
|
struct ieee802154_radio_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, FAR uint8_t *frame,
|
|
uint8_t len);
|
|
static uint8_t at86rf23x_readframe(FAR struct spi_dev_s *spi,
|
|
FAR 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_radio_s *ieee,
|
|
uint8_t chan);
|
|
static int at86rf23x_getchannel(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint8_t *chan);
|
|
static int at86rf23x_setpanid(FAR struct ieee802154_radio_s *ieee,
|
|
uint16_t panid);
|
|
static int at86rf23x_getpanid(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint16_t *panid);
|
|
static int at86rf23x_setsaddr(FAR struct ieee802154_radio_s *ieee,
|
|
uint16_t saddr);
|
|
static int at86rf23x_getsaddr(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint16_t *saddr);
|
|
static int at86rf23x_seteaddr(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint8_t *eaddr);
|
|
static int at86rf23x_geteaddr(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint8_t *eaddr);
|
|
static int at86rf23x_setpromisc(FAR struct ieee802154_radio_s *ieee,
|
|
bool promisc);
|
|
static int at86rf23x_getpromisc(FAR struct ieee802154_radio_s *ieee,
|
|
FAR bool *promisc);
|
|
static int at86rf23x_setdevmode(FAR struct ieee802154_radio_s *ieee,
|
|
uint8_t mode);
|
|
static int at86rf23x_getdevmode(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint8_t *mode);
|
|
static int at86rf23x_settxpower(FAR struct ieee802154_radio_s *ieee,
|
|
int32_t txpwr);
|
|
static int at86rf23x_gettxpower(FAR struct ieee802154_radio_s *ieee,
|
|
FAR int32_t *txpwr);
|
|
static int at86rf23x_setcca(FAR struct ieee802154_radio_s *ieee,
|
|
FAR struct ieee802154_cca_s *cca);
|
|
static int at86rf23x_getcca(FAR struct ieee802154_radio_s *ieee,
|
|
FAR struct ieee802154_cca_s *cca);
|
|
static int at86rf23x_ioctl(FAR struct ieee802154_radio_s *ieee, int cmd,
|
|
unsigned long arg);
|
|
static int at86rf23x_energydetect(FAR struct ieee802154_radio_s *ieee,
|
|
FAR uint8_t *energy);
|
|
static int at86rf23x_rxenable(FAR struct ieee802154_radio_s *ieee,
|
|
bool state, FAR struct ieee802154_packet_s *packet);
|
|
static int at86rf23x_transmit(FAR struct ieee802154_radio_s *ieee,
|
|
FAR struct ieee802154_packet_s *packet);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* 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_radioops_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
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* 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);
|
|
|
|
wlinfo("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);
|
|
|
|
wlinfo("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, FAR 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, ®, 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,
|
|
FAR 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, ®, 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))
|
|
{
|
|
wlerr("ERROR: 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:
|
|
wlerr("ERRPR: %s \n", EINVAL_STR);
|
|
init_status = 0;/* Placed this here to keep compiler happy when not in debug */
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ret == ERROR)
|
|
{
|
|
wlerr("ERROR: State Transistion Error\n");
|
|
}
|
|
|
|
wlinfo("Radio state change state[0x%02x]->state[0x%02x]\n", init_status, status);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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_radio_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)
|
|
{
|
|
wlerr("ERROR: 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))
|
|
{
|
|
wlerr("ERROR: 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;
|
|
|
|
wlinfo("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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_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_radio_s *ieee,
|
|
FAR int32_t *txpwr)
|
|
{
|
|
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_radio_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_radio_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_radio_s *ieee, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR struct at86rf23x_dev_s *dev = (FAR struct at86rf23x_dev_s *)ieee;
|
|
|
|
switch (cmd)
|
|
{
|
|
case 1000:
|
|
return at86rf23x_regdump(dev);
|
|
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: at86rf23x_energydetect
|
|
*
|
|
* Description:
|
|
* Perform energy detection scan. TODO: Need to implement.
|
|
*
|
|
****************************************************************************/
|
|
static int at86rf23x_energydetect(FAR struct ieee802154_radio_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);
|
|
|
|
wlinfo("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)
|
|
{
|
|
wlerr("ERROR: 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_radio_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
|
|
* debug 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);
|
|
|
|
wlinfo("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
|
|
{
|
|
wlerr("ERROR: 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;
|
|
|
|
wlinfo("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)
|
|
{
|
|
wlinfo("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_radio_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
|
|
{
|
|
wlerr("ERROR: 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_radio_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;
|
|
}
|