Skip to content
si4463.c 78.5 KiB
Newer Older
/****************************************************************************
 * drivers/wireless/generic/si4463.c
 *   Copyright (C) 2017-2018 Sebastien Lorquet. All rights reserved.
 *   Author: Sebastien Lorquet <sebastien@lorquet.fr>
 *
 * 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 <stdint.h>
Sebastien Lorquet's avatar
Sebastien Lorquet committed
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <nuttx/semaphore.h>

#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
#include <nuttx/semaphore.h>
#include <nuttx/fs/fs.h>
#include <nuttx/spi/spi.h>
#include <nuttx/wireless/generic/si4463.h>
Sebastien Lorquet's avatar
Sebastien Lorquet committed
#include <nuttx/wireless/generic/genradio.h>
Sebastien Lorquet's avatar
Sebastien Lorquet committed
#include "si4463.h"

/****************************************************************************
 * Pre-Processor Definitions
 ****************************************************************************/

#ifndef CONFIG_SCHED_HPWORK
#error High priority work queue required in this driver
#endif

#ifndef CONFIG_GENERICRADIO_SI4463_SPIMODE
#  define CONFIG_GENERICRADIO_SI4463_SPIMODE SPIDEV_MODE0
#ifndef CONFIG_GENERICRADIO_SI4463_FREQUENCY
Sebastien Lorquet's avatar
Sebastien Lorquet committed
#  define CONFIG_GENERICRADIO_SI4463_FREQUENCY 10000000 /* max 10 MHz */
/* Default to a 32-bit preamble length */
#ifndef CONFIG_GENERICRADIO_SI4463_DEFAULT_PREAMBLELEN
#define CONFIG_GENERICRADIO_SI4463_DEFAULT_PREAMBLELEN 32
#warning preamble length not defined, using 32
#elif CONFIG_GENERICRADIO_SI4463_DEFAULT_PREAMBLELEN == 0
#define CONFIG_GENERICRADIO_SI4463_DEFAULT_PREAMBLELEN 32
#warning preamble length was 0, using 32
#endif

/* Default to no preset base frequency */
#ifndef CONFIG_GENERICRADIO_SI4463_DEFAULT_FREQ
#define CONFIG_GENERICRADIO_SI4463_DEFAULT_FREQ 0
#endif

#ifndef CONFIG_GENERICRADIO_SI4463_DEFAULT_CHSPC
#define CONFIG_GENERICRADIO_SI4463_DEFAULT_CHSPC 0
#endif

#ifndef CONFIG_SPI_EXCHANGE
#  error CONFIG_SPI_EXCHANGE required for this driver
#endif

Sebastien Lorquet's avatar
Sebastien Lorquet committed
/* Values used to know what RF registers should be reprogrammed */

#define SI4463_APPLY_MODTYPE     (1 << 0)
#define SI4463_APPLY_BASEFREQ    (1 << 1)
#define SI4463_APPLY_DEVIATION   (1 << 2)
#define SI4463_APPLY_CHANSPACING (1 << 3)
#define SI4463_APPLY_DATARATE    (1 << 4)
#define SI4463_APPLY_BT          (1 << 5)
#define SI4463_APPLY_RXBW        (1 << 6)
#define SI4463_APPLY_TXFILT      (1 << 10)
#define SI4463_APPLY_RXFILT      (1 << 11)

/****************************************************************************
 ****************************************************************************/

/* A SI4463 device instance */

struct si4463_dev_s
{
  struct genradio_dev_s            genradio;    /* The public device instance */
  FAR struct spi_dev_s            *spi;         /* Underlying SPI bus */
  int                              spiid;       /* Device identifier within type SPIDEVTYPE_GENRADIO */
  struct work_s                    irqwork;     /* Interrupt continuation work queue support */
  FAR const struct si4463_lower_s *lower;       /* Low-level MCU-specific support */
  uint32_t                         xtal;        /* Frequency of the attached oscillator */
  bool                             hdr_done;    /* Set to TRUE when field 1 (header) has been received. */
#if defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L1 )
  uint8_t                          header[1];   /* Packet header, si4463 field 1, fixed size */
#elif defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2B ) || \
      defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2L )
  uint8_t                          header[2];
#endif

  /* Requested parameters, input to modem calculator */
  uint8_t                          requested_modtype; /* Requested modulation */
  uint32_t                         requested_datarate; /* Requested data rate in milli bps */
  uint32_t                         requested_basefreq; /* Requested base frequency in Hz */
  uint32_t                         requested_deviation; /* Requested FSK deviation in mHz */
  uint32_t                         requested_chanspacing; /* Requested channel spacing in mHz */
  uint32_t                         requested_bt; /* Requested GFSK parameter */
  uint32_t                         requested_rxbw; /* Requested RX bandwidth */
};

/****************************************************************************
 * Private function prototypes
 ****************************************************************************/

static void si4463_lock      (FAR struct spi_dev_s *spi);
static int  si4463_initialize(FAR struct si4463_dev_s *dev, int txpin,
                              int rxpin);
static void si4463_irqworker (FAR void *arg);
static int  si4463_interrupt (int irq, FAR void *context, FAR void *arg);

static int si4463_setbasefreq       (FAR struct genradio_dev_s *dev, 
                                     uint32_t Hz);
static int si4463_setchanspacing    (FAR struct genradio_dev_s *dev,
                                     uint32_t mHz);
static int si4463_setchannel        (FAR struct genradio_dev_s *dev,
                                     int32_t chan);
static int si4463_setdatarate       (FAR struct genradio_dev_s *dev,
                                     uint32_t bps);
static int si4463_ioctl             (FAR struct genradio_dev_s *dev, int cmd,
                                     unsigned long arg);
static int si4463_setmodulation     (FAR struct genradio_dev_s *dev,
                                     uint32_t mod);
static int si4463_setmodulationindex(FAR struct genradio_dev_s *dev,
                                     uint32_t ppm);
static int si4463_setdeviation      (FAR struct genradio_dev_s *dev,
                                     uint32_t mHz);
static int si4463_setfilterbt       (FAR struct genradio_dev_s *dev,
                                     uint32_t bt);
static int si4463_setpreamblelength (FAR struct genradio_dev_s *dev,
                                     uint32_t bits);
static int si4463_setsyncword       (FAR struct genradio_dev_s *dev,
                                     FAR uint8_t *data, uint32_t bits);
static int si4463_setrxbandwidth    (FAR struct genradio_dev_s *dev,
                                     uint32_t mHz);
static int si4463_getrssi           (FAR struct genradio_dev_s *dev,
                                     FAR int32_t *mB);
static int si4463_getlqi            (FAR struct genradio_dev_s *dev,
                                     FAR int32_t *lqi);
static int si4463_rxenable          (FAR struct genradio_dev_s *dev,
                                     int state);
static int si4463_settxpower        (FAR struct genradio_dev_s *dev,
                                     int32_t mB);
Sebastien Lorquet's avatar
Sebastien Lorquet committed
static void si4463_transmit         (FAR struct genradio_dev_s *dev,
                                     FAR const uint8_t *packet, size_t len);

/****************************************************************************
 * Global variables
 ****************************************************************************/

struct genradio_devops_s si4463_devops = 
{
  si4463_setbasefreq,        NULL,
  si4463_setchanspacing,     NULL,
  si4463_setchannel,         NULL,
  si4463_setdatarate,        NULL,
  si4463_ioctl,
  si4463_setmodulation,      NULL,
  si4463_setmodulationindex, NULL,
  si4463_setdeviation,       NULL,
  si4463_setfilterbt,        NULL,
  si4463_setpreamblelength,  NULL,
  si4463_setsyncword,        NULL,
  si4463_setrxbandwidth,     NULL,
  si4463_getrssi,
  si4463_getlqi,
  si4463_rxenable,
  si4463_settxpower,         NULL,
  si4463_transmit,
#ifdef CONFIG_GENERICRADIO_SI4463_USE_WDS_CONFIG
#include "si4463_config.h"
static const uint8_t si4463_config_wds[] = RADIO_CONFIGURATION_DATA_ARRAY;
#endif

/****************************************************************************
 * Private functions
 ****************************************************************************/

/* htobel: convert uint32_t from host order to big endian
 * htobes: convert uint16_t from host order to big endian
 * betohl: convert uint32_t from big endian to host order
 * betohs: convert uint16_t from big endian to host order
 */

#ifdef CONFIG_ENDIAN_BIG
#define htobel(n) (n)
#define htobes(n) (n)
#define betohl(n) (n)
#define betohs(n) (n)
#else

static inline uint32_t htobel(uint32_t n)
{
  return ( n     &0xFF)<<24 |
         ((n>> 8)&0xFF)<<16 |
         ((n>>16)&0xFF)<< 8 |
         ((n>>24)&0xFF);
}

static inline uint16_t htobes(uint16_t n)
{
  return ( (n&0xFF)<<8 | n>>8 );
}

#define betohl(n) htobel(n)
#define betohs(n) htobes(n)
#endif

/* Hardware access routines */

/****************************************************************************
 * Name: si4463_lock
 *
 * Description:
 *   Acquire exclusive access to the shared SPI bus.
 *
 ****************************************************************************/

static void si4463_lock(FAR struct spi_dev_s *spi)
{
  SPI_LOCK        (spi, 1);
  SPI_SETBITS     (spi, 8);
  SPI_SETMODE     (spi, CONFIG_GENERICRADIO_SI4463_SPIMODE);
  SPI_SETFREQUENCY(spi, CONFIG_GENERICRADIO_SI4463_FREQUENCY);
}

/****************************************************************************
 * Name: si4463_unlock
 *
 * Description:
 *   Release exclusive access to the shared SPI bus.
 *
 ****************************************************************************/

static inline void si4463_unlock(FAR struct spi_dev_s *spi)
{
  SPI_LOCK(spi,0);
}

/****************************************************************************
 * Name: si4463_command
 *
 * Description:
 *   Send a command to a si4463 device
 *
 ****************************************************************************/

#define SI4463_RETRIES 2000
Sebastien Lorquet's avatar
Sebastien Lorquet committed

static int si4463_command(FAR struct si4463_dev_s *dev, uint8_t command,
                          FAR void *param   , uint16_t paramlen,
                          FAR void *response, uint16_t rsplen)
Sebastien Lorquet's avatar
Sebastien Lorquet committed
  uint8_t pollbuf[2];
  int retries = SI4463_RETRIES;
  si4463_lock  (dev->spi);
Sebastien Lorquet's avatar
Sebastien Lorquet committed

  /* First, TX phase */

  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), true);

  /* Send command */
  SPI_SEND     (dev->spi, command);

  /* Send additional parameters */
      SPI_SNDBLOCK(dev->spi, (uint8_t*)param, paramlen);
  /* Execute */
  SPI_SELECT(dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
  /* Then, poll CTS until RX data is available */
      /* wait tSW = 80 ns */
      up_udelay(1);

      /* Store command at each attemt, it will get overwritten */
      pollbuf[0] = SI4463_CMD_READ_CMD_BUFF;
      pollbuf[1] = 0xFF;

Sebastien Lorquet's avatar
Sebastien Lorquet committed
      SPI_SELECT  (dev->spi, SPIDEV_GENRADIO(dev->spiid), true);
      SPI_EXCHANGE(dev->spi, pollbuf, pollbuf, 2);
      if (pollbuf[1] == 0xFF)
        {
          /* we're done */
Sebastien Lorquet's avatar
Sebastien Lorquet committed
        }
      else if(pollbuf[1] == 0x00)
        {
          /* command not completed yet */
Sebastien Lorquet's avatar
Sebastien Lorquet committed
          SPI_SELECT(dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
          retries -= 1;
        }
      else
        {
          /* unexpected value! */
          _err("Unexpected rx value when reading CTS!\n");
          ret = -EIO; /* Transaction failed */
          goto unlock;
  while(retries != 0);
    {
      _err("Command timeout!\n");
      ret = -ETIMEDOUT;
      goto unlock;
    }
Sebastien Lorquet's avatar
Sebastien Lorquet committed

  /* Continue by reading the response data */
      SPI_RECVBLOCK(dev->spi, (uint8_t*)response, rsplen);
  SPI_SELECT(dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
  si4463_unlock(dev->spi);
  return ret; /* Transaction finished */
/****************************************************************************
 * Name: si4463_getfrr
 *
 * Description:
 *   Return the value of the SI4463 fast registers. The transaction is executed
 *   as a single burst to avoid the DMA setup/teardown delays and be as fast
 *   as possible.
 *
 ****************************************************************************/
static int si4463_getfrr(FAR struct si4463_dev_s *dev, uint32_t firstreg,
                         FAR uint8_t *dest, size_t count)
{
  static const uint8_t cmds[4] =
    {
      SI4463_CMD_FRR_A_READ,
      SI4463_CMD_FRR_B_READ,
      SI4463_CMD_FRR_C_READ,
      SI4463_CMD_FRR_D_READ
    };
  uint8_t buf[5];

  if(firstreg > 3 || count < 0 || count > 4)
      return -ERANGE;
    }

  buf[0] = cmds[firstreg];

  si4463_lock  (dev->spi);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), true);
  SPI_RECVBLOCK(dev->spi, buf, count + 1);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
  si4463_unlock(dev->spi);
  memcpy(dest, buf + 1, count);

  return 0;
}

/****************************************************************************
 * Name: si4463_writetxfifo
 *
 * Description:
 *   Write data to the TX FIFO. There is no need to poll CTS.
 *
 ****************************************************************************/
Sebastien Lorquet's avatar
Sebastien Lorquet committed
static int si4463_writetxfifo(FAR struct si4463_dev_s *dev,
                              FAR const uint8_t *data, size_t len)
  if(len < 1)
    {
      return 0; /* Nothing to do and wommand will not work in that case */
    }

  si4463_lock  (dev->spi);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), true);
  SPI_SEND     (dev->spi, SI4463_CMD_WRITE_TX_FIFO);
  SPI_SNDBLOCK (dev->spi, data, len);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
  si4463_unlock(dev->spi);
  return 0;
}

/****************************************************************************
 * Name: si4463_readrxfifo
 *
 * Description:
 *   Read data from the RX FIFO. There is no need to poll CTS.
 *
 ****************************************************************************/
static int si4463_readrxfifo(FAR struct si4463_dev_s *dev, FAR uint8_t *data,
                             size_t len)
  si4463_lock  (dev->spi);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), true);
  SPI_SEND     (dev->spi, SI4463_CMD_READ_RX_FIFO);
  SPI_RECVBLOCK(dev->spi, data, len);
  SPI_SELECT   (dev->spi, SPIDEV_GENRADIO(dev->spiid), false);
  si4463_unlock(dev->spi);
  return 0;
/****************************************************************************
 * Name: si4463_getchipstatus
 *
 * Description:
 *   Return information about the execution of the last command.
 *
 ****************************************************************************/
Sebastien Lorquet's avatar
Sebastien Lorquet committed
static int si4463_getchipstatus(FAR struct si4463_dev_s *dev)
{
  struct si4463_cmd_getchipstatus_s cmd;
  struct si4463_rsp_getchipstatus_s rsp;
  int ret;

  cmd.chip_clr_pend = ~CHIP_STATUS_CMD_ERROR; //clear cmd_error status only

  ret = si4463_command(dev, SI4463_CMD_GET_CHIP_STATUS,
                       &cmd, sizeof(struct si4463_cmd_getchipstatus_s),
                       &rsp, sizeof(struct si4463_rsp_getchipstatus_s));
  if (ret)
    {
      return ret;
    }

  if(rsp.chip_pend & CHIP_STATUS_CMD_ERROR)
    {
      _err("Command ID 0x%02X: error %d\n",
           rsp.cmd_err_cmd_id, rsp.cmd_err_status);
      if(!rsp.cmd_err_status)
        {
          return OK; /* Should not happen ??? */
        }
      switch(rsp.cmd_err_status)
        {
          case CMD_ERROR_BAD_COMMAND   /*16*/ : return -EBADMSG;
          case CMD_ERROR_BAD_ARG       /*17*/ : return -EINVAL;
          case CMD_ERROR_COMMAND_BUSY  /*18*/ : return -EBUSY;
          case CMD_ERROR_INVALID_STATE /*19*/ : return -EPROTO;
          case CMD_ERROR_BAD_BOOTMODE  /*49*/ : return -ENOSYS;
          case CMD_ERROR_BAD_PROPERTY  /*64*/ : return -ENOENT;
          case CMD_ERROR_TIMEOUT      /*240*/ : return -ETIMEDOUT;
          default                             : return -EIO;
        }
/****************************************************************************
 * Name: si4463_getintstatus
 *
 * Description:
 *   Return information about the current pending interrupts, then clear them.
 *
 ****************************************************************************/
static int si4463_getintstatus(FAR struct si4463_dev_s *dev,
                               FAR uint8_t *phpend, FAR uint8_t *modempend,
                               FAR uint8_t *chippend)
{
  struct si4463_cmd_getintstatus_s cmd;
  struct si4463_rsp_getintstatus_s rsp;
  int ret;

  //Clear all pending ints after call
  cmd.ph_clr_pend    = 0;
  cmd.modem_clr_pend = 0;
  cmd.chip_clr_pend  = 0;

  ret = si4463_command(dev, SI4463_CMD_GET_INT_STATUS,
                       &cmd, sizeof(struct si4463_cmd_getintstatus_s),
                       &rsp, sizeof(struct si4463_rsp_getintstatus_s));
  if(ret == 0)
    {
      ret = si4463_getchipstatus(dev);
    }
  if (ret != 0)
  if(phpend)
    {
      *phpend    = rsp.ph_pend;
    }

  if(modempend)
    {
      *modempend = rsp.modem_pend;
    }

  if(chippend)
    {
      *chippend  = rsp.chip_pend;
    }
/****************************************************************************
 * Name: si4463_getfifoinfo
 *
 * Description:
 *   Return the number of bytes in each fifo, and provide means to flush
 *   these FIFOs.
 *
 ****************************************************************************/
static int si4463_getfifoinfo(FAR struct si4463_dev_s *priv,
                              bool clean_tx, bool clean_rx,
                              FAR uint8_t *txlen, FAR uint8_t *rxlen)
{
  struct si4463_cmd_fifoinfo_s    cmd_fifo;
  struct si4463_rsp_fifoinfo_s    rsp_fifo;
  int ret;

  cmd_fifo.fifo = 0;
  if(clean_tx)
    {
      cmd_fifo.fifo |= FIFOINFO_CLEAR_TX;
    }
  if(clean_rx)
    {
      cmd_fifo.fifo |= FIFOINFO_CLEAR_RX;
    }

  ret = si4463_command(priv, SI4463_CMD_FIFO_INFO, &cmd_fifo, sizeof(cmd_fifo),
                       &rsp_fifo, sizeof(rsp_fifo));
  if(ret == 0)
    {
      ret = si4463_getchipstatus(priv);
    }
  if(ret != 0)
    {
      return ret;
    }

  if(txlen)
    {
      *txlen = rsp_fifo.tx_fifo_space;
    }
  if(rxlen)
    {
    }

  return ret;
}

/****************************************************************************
 * Name: si4463_changestate
 *
 * Description:
 *   Transition the chip to a specific operating mode.
 *
 ****************************************************************************/
static int si4463_changestate(FAR struct si4463_dev_s *priv, uint8_t state)
{
  struct si4463_cmd_changestate_s cmd_chstate;
  int ret;

  cmd_chstate.next_state1 = state;
  ret = si4463_command(priv, SI4463_CMD_CHANGE_STATE,
        &cmd_chstate, sizeof(cmd_chstate), NULL, 0);

  if(ret == 0)
    {
      ret = si4463_getchipstatus(priv);
    }
/****************************************************************************
 * Name: si4463_setprop
 *
 * Description:
 *   Set the value of a SI4463 property (or successive properties)
 *
 ****************************************************************************/
static int si4463_setprop(FAR struct si4463_dev_s *dev, uint16_t prop,
                          FAR uint8_t* val, size_t len)
{
  struct si4463_cmd_setproperty_s cmd;
  if(len < 0 || len > 12)
  cmd.group      = (prop >> 8) & 0xFF;
  cmd.num_props  = len;
  cmd.start_prop = prop & 0xFF;
  memcpy(&cmd.data, val, len);

  ret = si4463_command(dev, SI4463_CMD_SET_PROPERTY,
                        &cmd,
                        sizeof(struct si4463_cmd_setproperty_s) - 
                            SI4463_SETPROP_DATALEN + len,
  if(ret == 0)
    {
      ret = si4463_getchipstatus(dev);
    }

  return ret;
}

/****************************************************************************
 * Name: si4463_setprop_byte
 *
 * Description:
 *   Set the value of a single byte SI4463 property
 *
 ****************************************************************************/
static inline int si4463_setprop_byte(FAR struct si4463_dev_s *dev,
                                      uint16_t prop, uint8_t val)
{
  return si4463_setprop(dev, prop, &val, 1);
}

/****************************************************************************
 * Name: si4463_setprop_short
 *
 * Description:
 *   Set the value of a short (big endian) SI4463 property
 *
 ****************************************************************************/
static inline int si4463_setprop_short(FAR struct si4463_dev_s *dev,
                                      uint16_t prop, uint16_t val)
{
  uint8_t arr[2];
  arr[0] = val >> 8;
  arr[1] = val & 0xFF;
  return si4463_setprop(dev, prop, arr, 2);
}

/****************************************************************************
 * Name: si4463_getprop
 *
 * Description:
 *   Return the value of a SI4463 property (or successive properties)
 *
 ****************************************************************************/

static int si4463_getprop(FAR struct si4463_dev_s *dev, uint16_t prop,
                          FAR uint8_t*val, size_t len)
{
  struct si4463_cmd_getproperty_s cmd;
  if(len < 0 || len > 16)
  cmd.group      = (prop >> 8) & 0xFF;
  cmd.num_props  = len;
  cmd.start_prop = prop & 0xFF;

  ret = si4463_command(dev, SI4463_CMD_GET_PROPERTY,
                        &cmd, sizeof(struct si4463_cmd_getproperty_s),
                        val, len);
  if(ret == 0)
    {
      ret = si4463_getchipstatus(dev);
    }

  return ret;
Sebastien Lorquet's avatar
Sebastien Lorquet committed
/****************************************************************************
 * Name: si4463_getadcreading
 *
 * Description:
 *   Return the values read on the internal or external ADC lines.
Sebastien Lorquet's avatar
Sebastien Lorquet committed
 *
 ****************************************************************************/
Sebastien Lorquet's avatar
Sebastien Lorquet committed
static int si4463_getadcreading(FAR struct si4463_dev_s *dev, int pin,
                                FAR uint16_t *gpio, FAR uint16_t *temp,
                                FAR uint16_t *vbat)
Sebastien Lorquet's avatar
Sebastien Lorquet committed
{
  struct si4463_cmd_getadcreading_s cmd;
  struct si4463_rsp_getadcreading_s rsp;
  int ret;

  cmd.adc_en  = 0;
  cmd.adc_cfg = 0;

  if (gpio)
      if (pin < SI4463_IO0 || pin > SI4463_IO3)
Sebastien Lorquet's avatar
Sebastien Lorquet committed
        {
          _err("Invalid ADC pin %d\n", pin);
          return -EINVAL;
        }
      cmd.adc_en |=  ADC_EN_GPIO_EN;
      cmd.adc_en &= ~ADC_EN_GPIO_PIN_MASK;
      cmd.adc_en |=  ADC_EN_GPIO_PIN(pin);
    }

  if (temp)
Sebastien Lorquet's avatar
Sebastien Lorquet committed
    {
      cmd.adc_en |= ADC_EN_TEMPERATURE_EN;
    }

  if (vbat)
Sebastien Lorquet's avatar
Sebastien Lorquet committed
    {
      cmd.adc_en  |=  ADC_EN_BATTERY_VOLTAGE_EN;
      cmd.adc_cfg &= ~ADC_CFG_GPIOATT_MASK;
      cmd.adc_cfg |=  ADC_CFG_GPIOATT_3P2;
    }

  if (!cmd.adc_en)
    {
      _err("No ADC reading selected!\n");
      return -EINVAL;
    }

  /* Use the slow conversion rate */
Sebastien Lorquet's avatar
Sebastien Lorquet committed
  cmd.adc_cfg &= ~ADC_CFG_UDTIME_MASK;
  cmd.adc_cfg |=  ADC_CFG_UDTIME_305HZ;

  ret = si4463_command(dev, SI4463_CMD_GET_ADC_READING,
                        &cmd, sizeof(struct si4463_cmd_getadcreading_s),
                        &rsp, sizeof(struct si4463_rsp_getadcreading_s));
  if(ret == 0)
    {
      ret = si4463_getchipstatus(dev);
    }

  if (ret != 0)
  if(gpio)
    {
    *gpio = betohs(rsp.gpio_adc   ) & ADC_VALUE_MASK;
    }
  if(temp)
    {
    *temp = betohs(rsp.temp_adc   ) & ADC_VALUE_MASK;
    }
  if(vbat)
    {
    *vbat = betohs(rsp.battery_adc) & ADC_VALUE_MASK;
    }
/****************************************************************************
 * Name: si4463_packetconfig
 *
 * Description:
 *  Configure the packet handler. 3 configs are available at the moment.
 *   - 1 byte length in header, followed by data payload
 *   - 2 bytes big endian length in header, followed by data payload
 *   - 2 bytes lil endian length in header, followed by data payload
 * All packets end with a 2-byte CRC16.
 * The format is the same for TX and RX.
 * Address bytes in header are not managed yet.
 *
 ****************************************************************************/
static int si4463_packetconfig(FAR struct si4463_dev_s *dev)
{
  int ret;

  _info("Configuring packet handler\n");

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_CRC_CONFIG,
                                PKT_CRC_CONFIG_SEED_FFFF | 
                                PKT_CRC_CONFIG_POLY_CCITT_16);
      if(ret != 0)
        {
          break;
        }
      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_CONFIG1,
                                PKT_CONFIG1_CRC_INVERT);
      if(ret != 0)
        {
          break;
        }

      /* Configure field 1 as a one or two bytes length field */
#if defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L1 )
      ret = si4463_setprop_short(dev, SI4463_PROP_PKT_FIELD_1_LENGTH_0, 1);
#elif defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2B ) || \
      defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2L )
      ret = si4463_setprop_short(dev, SI4463_PROP_PKT_FIELD_1_LENGTH_0, 2);
#endif
      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_FIELD_1_CONFIG, 0);
      if(ret != 0)
        {
          break;
        }

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_FIELD_1_CRC_CONFIG,
                                PKT_FIELD_CRC_CONFIG_START |
                                PKT_FIELD_CRC_CONFIG_ENABLE );
      if(ret != 0)
        {
          break;
        }

  /* Configure field 2 - length will be set before tx */
      ret = si4463_setprop_short(dev, SI4463_PROP_PKT_FIELD_2_LENGTH_0, 1);
      if(ret != 0)
        {
          break;
        }

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_FIELD_2_CONFIG, 0);
      if(ret != 0)
        {
          break;
        }

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_FIELD_2_CRC_CONFIG,
                                PKT_FIELD_CRC_CONFIG_ENABLE |
                                PKT_FIELD_CRC_CONFIG_CHECK |
                                PKT_FIELD_CRC_CONFIG_SEND);
      if(ret != 0)
        {
          break;
        }

  /* Disable fields 3-5 */
      ret = si4463_setprop_short(dev, SI4463_PROP_PKT_FIELD_3_LENGTH_0, 0);
      if(ret != 0)
        {
          break;
        }

  /* Configure rx length: in field 1, defines length of field 2. */

#if defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L1 )
      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_LEN, 2 | PKT_LEN_IN_FIFO |
                                PKT_LEN_SIZE_1);
#elif defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2B )
      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_LEN, 2 | PKT_LEN_IN_FIFO |
                                PKT_LEN_SIZE_2 | PKT_LEN_ENDIAN_BIG);
#elif defined( CONFIG_GENERICRADIO_SI4463_FRAMING_L2L )
      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_LEN, 2 | PKT_LEN_IN_FIFO |
                                PKT_LEN_SIZE_2 | PKT_LEN_ENDIAN_LIL);
#endif
      if(ret != 0)
        {
          break;
        }

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_LEN_FIELD_SOURCE, 1);
      if(ret != 0)
        {
          break;
        }

      ret = si4463_setprop_byte(dev, SI4463_PROP_PKT_LEN_ADJUST, 0);
    }
  while(0);

  if(ret != 0)
    {
      _err("something failed\n", ret);
    }
/****************************************************************************
 * Name: si4463_initialize
 *
 * Description:
Sebastien Lorquet's avatar
Sebastien Lorquet committed
 *  Put the SI4463 in working order. Returns 0 if all is ok, else a negative
 *  errno value.
 *
 ****************************************************************************/
static int si4463_initialize(FAR struct si4463_dev_s *dev,
                             int papin, int lnapin)
  int ret;
#ifndef CONFIG_GENERICRADIO_SI4463_USE_WDS_CONFIG
  uint8_t regs[2];
  struct si4463_rsp_partinfo_s part;
  struct si4463_rsp_funcinfo_s func;
Sebastien Lorquet's avatar
Sebastien Lorquet committed
  uint16_t vbat, temp;
  int32_t conv, iprt;
Sebastien Lorquet's avatar
Sebastien Lorquet committed

  /* check parameters */

  if(papin < SI4463_IO0 || papin > SI4463_IO3)
    {
      _err("Invalid PA control pin %d\n", papin);
      return -EINVAL;
Sebastien Lorquet's avatar
Sebastien Lorquet committed
    }

  if(lnapin < SI4463_IO0 || lnapin > SI4463_IO3)
    {
      _err("Invalid LNA control pin %d\n", lnapin);
      return -EINVAL;
#ifndef CONFIG_GENERICRADIO_SI4463_USE_WDS_CONFIG
Sebastien Lorquet's avatar
Sebastien Lorquet committed
  /* send POWER_UP command */
  _info("power up...\n");
  pwrup.boot_options = PWRUP_BOOTOPT_NO_PATCH | PWRUP_BOOTOPT_FUNC_PRO;
  pwrup.xtal_options = PWRUP_XTALOPT_XTAL;
  pwrup.xo_freq      = htobel(dev->xtal);
  ret = si4463_command(dev, SI4463_CMD_POWER_UP,
                       &pwrup, sizeof(struct si4463_cmd_pwrup_s), NULL, 0);
Sebastien Lorquet's avatar
Sebastien Lorquet committed
  if(ret != 0)
    {
      _err("Could not power up chip (errno %d)\n", ret);
      return ret;
Sebastien Lorquet's avatar
Sebastien Lorquet committed
    }

  /* Clean any pending interrupt */

  _info("clean up status...\n");
  ret = si4463_getintstatus(dev, NULL, NULL, NULL);