incubator-nuttx/drivers/wireless/nrf24l01.c

2114 lines
58 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
* drivers/wireless/nrf24l01/nrf24l01.c
*
* Copyright (C) 2013 Laurent Latil. All rights reserved.
* Authors: Laurent Latil <laurent@latil.nom.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.
*
****************************************************************************/
/* Features:
* - Fixed length and dynamically sized payloads (1 - 32 bytes)
* - Management of the 6 receiver pipes
* - Configuration of each pipe: address, packet length, auto-acknowledge, etc.
* - Use a FIFO buffer to store the received packets
*
* Todo:
* - Add support for payloads in ACK packets (?)
* - Add compatibility with nRF24L01 (not +) hardware (?)
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <poll.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
# include <nuttx/wqueue.h>
#endif
#include <nuttx/wireless/nrf24l01.h>
#include "nrf24l01.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_WL_NRF24L01_DFLT_ADDR_WIDTH
# define CONFIG_WL_NRF24L01_DFLT_ADDR_WIDTH 5
#endif
#ifndef CONFIG_WL_NRF24L01_RXFIFO_LEN
# define CONFIG_WL_NRF24L01_RXFIFO_LEN 128
#endif
#if defined(CONFIG_WL_NRF24L01_RXSUPPORT) && !defined(CONFIG_SCHED_HPWORK)
# error RX support requires CONFIG_SCHED_HPWORK
#endif
#ifdef CONFIG_WL_NRF24L01_CHECK_PARAMS
# define CHECK_ARGS(cond) do { if (!(cond)) return -EINVAL; } while (0)
#else
# define CHECK_ARGS(cond)
#endif
/* NRF24L01 Definitions *****************************************************/
/* Default SPI bus frequency (in Hz).
* Can go up to 10 Mbs according to datasheet.
*/
#define NRF24L01_SPIFREQ 9000000
/* power-down -> standby transition timing (in us). Note: this value is
* probably larger than required.
*/
#define NRF24L01_TPD2STBY_DELAY 4500
/* Max time to wait for TX irq (in ms) */
#define NRF24L01_MAX_TX_IRQ_WAIT 2000
#define FIFO_PKTLEN_MASK 0x1F /* 5 ls bits used to store packet length */
#define FIFO_PKTLEN_SHIFT 0
#define FIFO_PIPENO_MASK 0xE0 /* 3 ms bits used to store pipe # */
#define FIFO_PIPENO_SHIFT 4
#define FIFO_PKTLEN(dev) \
(((dev->rx_fifo[dev->nxt_read] & FIFO_PKTLEN_MASK) >> FIFO_PKTLEN_SHIFT) + 1)
#define FIFO_PIPENO(dev) \
(((dev->rx_fifo[dev->nxt_read] & FIFO_PIPENO_MASK) >> FIFO_PIPENO_SHIFT))
#define FIFO_HEADER(pktlen,pipeno) \
((pktlen - 1) | (pipeno << FIFO_PIPENO_SHIFT))
#define DEV_NAME "/dev/nrf24l01"
#define FL_AA_ENABLED (1 << 0)
/****************************************************************************
* Private Data Types
****************************************************************************/
typedef enum
{
MODE_READ,
MODE_WRITE
} nrf24l01_access_mode_t;
struct nrf24l01_dev_s
{
FAR struct spi_dev_s *spi; /* Reference to SPI bus device */
FAR struct nrf24l01_config_s *config; /* Board specific GPIO functions */
nrf24l01_state_t state; /* Current state of the nRF24L01 */
uint8_t en_aa; /* Cache EN_AA register value */
uint8_t en_pipes; /* Cache EN_RXADDR register value */
bool ce_enabled; /* Cache the value of CE pin */
uint8_t lastxmitcount; /* Retransmit count of the last succeeded AA transmission */
uint8_t addrlen; /* Address width (3-5) */
uint8_t pipedatalen[NRF24L01_PIPE_COUNT];
uint8_t pipe0addr[NRF24L01_MAX_ADDR_LEN]; /* Configured address on pipe 0 */
uint8_t last_recvpipeno;
sem_t sem_tx;
bool tx_pending; /* Is userspace waiting for TX IRQ? - accessor
* needs to hold lock on SPI bus */
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
uint8_t *rx_fifo; /* Circular RX buffer. [pipe# / pkt_len] [packet data...] */
uint16_t fifo_len; /* Number of bytes stored in fifo */
uint16_t nxt_read; /* Next read index */
uint16_t nxt_write; /* Next write index */
sem_t sem_fifo; /* Protect access to rx fifo */
sem_t sem_rx; /* Wait for availability of received data */
struct work_s irq_work; /* Interrupt handling "bottom half" */
#endif
uint8_t nopens; /* Number of times the device has been opened */
sem_t devsem; /* Ensures exclusive access to this structure */
#ifndef CONFIG_DISABLE_POLL
FAR struct pollfd *pfd; /* Polled file descr (or NULL if any) */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Low-level SPI helpers */
static inline void nrf24l01_configspi(FAR struct spi_dev_s *spi);
static void nrf24l01_lock(FAR struct spi_dev_s *spi);
static void nrf24l01_unlock(FAR struct spi_dev_s *spi);
static uint8_t nrf24l01_access(FAR struct nrf24l01_dev_s *dev,
nrf24l01_access_mode_t mode, uint8_t cmd, uint8_t *buf,
int length);
static uint8_t nrf24l01_flush_rx(FAR struct nrf24l01_dev_s *dev);
static uint8_t nrf24l01_flush_tx(FAR struct nrf24l01_dev_s *dev);
/* Read register from nrf24 */
static uint8_t nrf24l01_readreg(FAR struct nrf24l01_dev_s *dev, uint8_t reg,
FAR uint8_t *value, int len);
/* Read single byte value from a register of nrf24 */
static uint8_t nrf24l01_readregbyte(FAR struct nrf24l01_dev_s *dev,
uint8_t reg);
static void nrf24l01_writeregbyte(FAR struct nrf24l01_dev_s *dev,
uint8_t reg, uint8_t value);
static uint8_t nrf24l01_setregbit(FAR struct nrf24l01_dev_s *dev,
uint8_t reg, uint8_t value, bool set);
static void nrf24l01_tostate(FAR struct nrf24l01_dev_s *dev,
nrf24l01_state_t state);
static int nrf24l01_irqhandler(FAR int irq, FAR void *context,
FAR void *arg);
static inline int nrf24l01_attachirq(FAR struct nrf24l01_dev_s *dev,
xcpt_t isr, FAR void *arg);
static int dosend(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data,
size_t datalen);
static int nrf24l01_unregister(FAR struct nrf24l01_dev_s *dev);
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
static void fifoput(struct nrf24l01_dev_s *dev, uint8_t pipeno,
FAR uint8_t *buffer, uint8_t buflen);
static uint8_t fifoget(struct nrf24l01_dev_s *dev, FAR uint8_t *buffer,
uint8_t buflen, FAR uint8_t *pipeno);
static void nrf24l01_worker(FAR void *arg);
#endif
#ifdef CONFIG_DEBUG_WIRELESS
static void binarycvt(char *deststr, const uint8_t *srcbin, size_t srclen)
#endif
/* POSIX API */
static int nrf24l01_open(FAR struct file *filep);
static int nrf24l01_close(FAR struct file *filep);
static ssize_t nrf24l01_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t nrf24l01_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static int nrf24l01_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
#ifndef CONFIG_DISABLE_POLL
static int nrf24l01_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations nrf24l01_fops =
{
nrf24l01_open, /* open */
nrf24l01_close, /* close */
nrf24l01_read, /* read */
nrf24l01_write, /* write */
NULL, /* seek */
nrf24l01_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
, nrf24l01_poll /* poll */
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nrf24l01_lock
****************************************************************************/
static void nrf24l01_lock(FAR struct spi_dev_s *spi)
{
/* Lock the SPI bus because there are multiple devices competing for the
* SPI bus
*/
(void)SPI_LOCK(spi, true);
/* We have the lock. Now make sure that the SPI bus is configured for the
* NRF24L01 (it might have gotten configured for a different device while
* unlocked)
*/
SPI_SELECT(spi, SPIDEV_WIRELESS(0), true);
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETBITS(spi, 8);
(void)SPI_HWFEATURES(spi, 0);
(void)SPI_SETFREQUENCY(spi, NRF24L01_SPIFREQ);
SPI_SELECT(spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: nrf24l01_unlock
*
* Description:
* Un-lock the SPI bus after each transfer, possibly losing the current
* configuration if we are sharing the SPI bus with other devices.
*
* Parameters:
* spi - Reference to the SPI driver structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void nrf24l01_unlock(FAR struct spi_dev_s *spi)
{
/* Relinquish the SPI bus. */
(void)SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: nrf24l01_configspi
*
* Description:
* Configure the SPI for use with the NRF24L01.
*
* Parameters:
* spi - Reference to the SPI driver structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static inline void nrf24l01_configspi(FAR struct spi_dev_s *spi)
{
/* Configure SPI for the NRF24L01 module. */
SPI_SELECT(spi, SPIDEV_WIRELESS(0), true); /* Useful ? */
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETBITS(spi, 8);
(void)SPI_HWFEATURES(spi, 0);
(void)SPI_SETFREQUENCY(spi, NRF24L01_SPIFREQ);
SPI_SELECT(spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: nrf24l01_select
****************************************************************************/
static inline void nrf24l01_select(struct nrf24l01_dev_s * dev)
{
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), true);
}
/****************************************************************************
* Name: nrf24l01_deselect
****************************************************************************/
static inline void nrf24l01_deselect(struct nrf24l01_dev_s * dev)
{
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: nrf24l01_access
****************************************************************************/
static uint8_t nrf24l01_access(FAR struct nrf24l01_dev_s *dev,
nrf24l01_access_mode_t mode, uint8_t cmd,
FAR uint8_t *buf, int length)
{
uint8_t status;
/* Prepare SPI */
nrf24l01_select(dev);
/* Transfer */
status = SPI_SEND(dev->spi, cmd);
switch (mode)
{
case MODE_WRITE:
if (length > 0)
{
SPI_SNDBLOCK(dev->spi, buf, length);
}
break;
case MODE_READ:
SPI_RECVBLOCK(dev->spi, buf, length);
break;
}
nrf24l01_deselect(dev);
return status;
}
/****************************************************************************
* Name: nrf24l01_flush_rx
****************************************************************************/
static inline uint8_t nrf24l01_flush_rx(struct nrf24l01_dev_s *dev)
{
return nrf24l01_access(dev, MODE_WRITE, NRF24L01_FLUSH_RX, NULL, 0);
}
/****************************************************************************
* Name: nrf24l01_flush_tx
****************************************************************************/
static inline uint8_t nrf24l01_flush_tx(struct nrf24l01_dev_s *dev)
{
return nrf24l01_access(dev, MODE_WRITE, NRF24L01_FLUSH_TX, NULL, 0);
}
/****************************************************************************
* Name: nrf24l01_readreg
*
* Description:
* Read register from nrf24l01
*
****************************************************************************/
static inline uint8_t nrf24l01_readreg(struct nrf24l01_dev_s *dev,
uint8_t reg, FAR uint8_t *value,
int len)
{
return nrf24l01_access(dev, MODE_READ, reg | NRF24L01_R_REGISTER,
value, len);
}
/****************************************************************************
* Name: nrf24l01_readregbyte
*
* Description:
* Read single byte value from a register of nrf24l01
*
****************************************************************************/
static inline uint8_t nrf24l01_readregbyte(struct nrf24l01_dev_s *dev,
uint8_t reg)
{
uint8_t val;
nrf24l01_readreg(dev, reg, &val, 1);
return val;
}
/****************************************************************************
* Name: nrf24l01_writereg
*
* Description:
* Write value to a register of nrf24l01
*
****************************************************************************/
static inline int nrf24l01_writereg(FAR struct nrf24l01_dev_s *dev,
uint8_t reg, FAR const uint8_t *value,
int len)
{
return nrf24l01_access(dev, MODE_WRITE, reg | NRF24L01_W_REGISTER,
(FAR uint8_t *)value, len);
}
/****************************************************************************
* Name: nrf24l01_writeregbyte
*
* Description:
* Write single byte value to a register of nrf24l01
*
****************************************************************************/
static inline void nrf24l01_writeregbyte(FAR struct nrf24l01_dev_s *dev,
uint8_t reg, uint8_t value)
{
nrf24l01_writereg(dev, reg, &value, 1);
}
/****************************************************************************
* Name: nrf24l01_setregbit
****************************************************************************/
static uint8_t nrf24l01_setregbit(FAR struct nrf24l01_dev_s *dev,
uint8_t reg, uint8_t value, bool set)
{
uint8_t val;
nrf24l01_readreg(dev, reg, &val, 1);
if (set)
{
val |= value;
}
else
{
val &= ~value;
}
nrf24l01_writereg(dev, reg, &val, 1);
return val;
}
/****************************************************************************
* Name: fifoput
*
* Description:
* RX fifo mgt
*
****************************************************************************/
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
static void fifoput(FAR struct nrf24l01_dev_s *dev, uint8_t pipeno,
FAR uint8_t *buffer, uint8_t buflen)
{
sem_wait(&dev->sem_fifo);
while (dev->fifo_len + buflen + 1 > CONFIG_WL_NRF24L01_RXFIFO_LEN)
{
/* TODO: Set fifo overrun flag ! */
int skiplen = FIFO_PKTLEN(dev) + 1;
dev->nxt_read = (dev->nxt_read + skiplen) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
dev->fifo_len -= skiplen;
}
dev->rx_fifo[dev->nxt_write] = FIFO_HEADER(buflen, pipeno);
dev->nxt_write = (dev->nxt_write + 1) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
/* Adjust fifo bytes count */
dev->fifo_len += (buflen + 1);
while (buflen--)
{
dev->rx_fifo[dev->nxt_write] = *(buffer++);
dev->nxt_write = (dev->nxt_write + 1) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
}
sem_post(&dev->sem_fifo);
}
/****************************************************************************
* Name: fifoget
****************************************************************************/
static uint8_t fifoget(FAR struct nrf24l01_dev_s *dev, FAR uint8_t *buffer,
uint8_t buflen, uint8_t *pipeno)
{
uint8_t pktlen;
uint8_t i;
sem_wait(&dev->sem_fifo);
/* sem_rx contains count of inserted packets in FIFO, but FIFO can
* overflow - fail smart.
*/
if (dev->fifo_len == 0)
{
pktlen = 0;
goto no_data;
}
pktlen = FIFO_PKTLEN(dev);
if (NULL != pipeno)
{
*pipeno = FIFO_PIPENO(dev);
}
dev->nxt_read = (dev->nxt_read + 1) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
for (i = 0; i < pktlen && i < buflen; i++)
{
*(buffer++) = dev->rx_fifo[dev->nxt_read];
dev->nxt_read = (dev->nxt_read + 1) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
}
if (i < pktlen)
{
dev->nxt_read = (dev->nxt_read + pktlen - i) % CONFIG_WL_NRF24L01_RXFIFO_LEN;
}
/* Adjust fifo bytes count */
dev->fifo_len -= (pktlen + 1);
no_data:
sem_post(&dev->sem_fifo);
return pktlen;
}
#endif
/****************************************************************************
* Name: nrf24l01_irqhandler
****************************************************************************/
static int nrf24l01_irqhandler(int irq, FAR void *context, FAR void *arg)
{
FAR struct nrf24l01_dev_s *dev = (FAR struct nrf24l01_dev_s *)arg;
wlinfo("*IRQ*\n");
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
/* If RX is enabled we delegate the actual work to bottom-half handler */
work_queue(HPWORK, &dev->irq_work, nrf24l01_worker, dev, 0);
#else
/* Otherwise we simply wake up the send function */
sem_post(&dev->sem_tx); /* Wake up the send function */
#endif
return OK;
}
/****************************************************************************
* Name: nrf24l01_attachirq
*
* Description:
* Configure IRQ pin (falling edge)
*
****************************************************************************/
static inline int nrf24l01_attachirq(FAR struct nrf24l01_dev_s *dev, xcpt_t isr,
FAR void *arg)
{
return dev->config->irqattach(isr, arg);
}
/****************************************************************************
* Name: nrf24l01_chipenable
****************************************************************************/
static inline bool nrf24l01_chipenable(FAR struct nrf24l01_dev_s *dev,
bool enable)
{
if (dev->ce_enabled != enable)
{
dev->config->chipenable(enable);
dev->ce_enabled = enable;
return !enable;
}
else
{
return enable;
}
}
/****************************************************************************
* Name: nrf24l01_worker
****************************************************************************/
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
static void nrf24l01_worker(FAR void *arg)
{
FAR struct nrf24l01_dev_s *dev = (FAR struct nrf24l01_dev_s *) arg;
uint8_t status;
uint8_t fifo_status;
nrf24l01_lock(dev->spi);
status = nrf24l01_readregbyte(dev, NRF24L01_STATUS);
if (status & NRF24L01_RX_DR)
{
/* Put CE low */
bool ce = nrf24l01_chipenable(dev, false);
#ifndef CONFIG_DISABLE_POLL
bool has_data = false;
#endif
wlinfo("RX_DR is set!\n");
/* Read and store all received payloads */
do
{
uint8_t pipeno;
uint8_t pktlen;
uint8_t buf[NRF24L01_MAX_PAYLOAD_LEN];
/* For each packet:
* - Get pipe #
* - Get payload length (either static or dynamic)
* - Read payload content
*/
pipeno = (status & NRF24L01_RX_P_NO_MASK) >> NRF24L01_RX_P_NO_SHIFT;
if (pipeno >= NRF24L01_PIPE_COUNT) /* 6=invalid 7=fifo empty */
{
wlerr("invalid pipe rx: %d\n", (int)pipeno);
nrf24l01_flush_rx(dev);
break;
}
pktlen = dev->pipedatalen[pipeno];
if (NRF24L01_DYN_LENGTH == pktlen)
{
/* If dynamic length payload need to use R_RX_PL_WID command
* to get actual length.
*/
nrf24l01_access(dev, MODE_READ, NRF24L01_R_RX_PL_WID, &pktlen, 1);
}
if (pktlen > NRF24L01_MAX_PAYLOAD_LEN) /* bad length */
{
wlerr("invalid length in rx: %d\n", (int)pktlen);
nrf24l01_flush_rx(dev);
break;
}
/* Get payload content */
nrf24l01_access(dev, MODE_READ, NRF24L01_R_RX_PAYLOAD, buf, pktlen);
fifoput(dev, pipeno, buf, pktlen);
#ifndef CONFIG_DISABLE_POLL
has_data = true;
#endif
sem_post(&dev->sem_rx); /* Wake-up any thread waiting in recv */
status = nrf24l01_readreg(dev, NRF24L01_FIFO_STATUS, &fifo_status, 1);
wlinfo("FIFO_STATUS=%02x\n", fifo_status);
wlinfo("STATUS=%02x\n", status);
}
while ((fifo_status & NRF24L01_RX_EMPTY) == 0);
#ifndef CONFIG_DISABLE_POLL
if (dev->pfd && has_data)
{
dev->pfd->revents |= POLLIN; /* Data available for input */
wlinfo("Wake up polled fd\n");
sem_post(dev->pfd->sem);
}
#endif
/* Clear interrupt sources */
nrf24l01_writeregbyte(dev, NRF24L01_STATUS, NRF24L01_RX_DR);
/* Restore CE */
nrf24l01_chipenable(dev, ce);
}
if (status & (NRF24L01_TX_DS | NRF24L01_MAX_RT))
{
/* Confirm send */
nrf24l01_chipenable(dev, false);
if (dev->tx_pending)
{
/* The actual work is done in the send function */
sem_post(&dev->sem_tx);
}
else
{
wlerr("invalid length in rx: %d\n", (int)pktlen);
}
}
if (dev->state == ST_RX)
{
/* Re-enable CE (to go back to RX mode state) */
nrf24l01_chipenable(dev, true);
}
nrf24l01_unlock(dev->spi);
}
#endif
/****************************************************************************
* Name: nrf24l01_tostate
****************************************************************************/
static void nrf24l01_tostate(struct nrf24l01_dev_s *dev,
nrf24l01_state_t state)
{
nrf24l01_state_t oldstate = dev->state;
if (oldstate == state)
{
return;
}
if (oldstate == ST_POWER_DOWN)
{
/* Leaving power down (note: new state cannot be power down here) */
nrf24l01_setregbit(dev, NRF24L01_CONFIG, NRF24L01_PWR_UP, true);
usleep(NRF24L01_TPD2STBY_DELAY);
}
/* Entering new state */
switch (state)
{
case ST_UNKNOWN:
/* Power down the module here... */
case ST_POWER_DOWN:
nrf24l01_chipenable(dev, false);
nrf24l01_setregbit(dev, NRF24L01_CONFIG, NRF24L01_PWR_UP, false);
break;
case ST_STANDBY:
nrf24l01_chipenable(dev, false);
nrf24l01_setregbit(dev, NRF24L01_CONFIG, NRF24L01_PRIM_RX, false);
break;
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
case ST_RX:
nrf24l01_setregbit(dev, NRF24L01_CONFIG, NRF24L01_PRIM_RX, true);
nrf24l01_chipenable(dev, true);
break;
#endif
}
dev->state = state;
}
/****************************************************************************
* Name: dosend
****************************************************************************/
static int dosend(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data,
size_t datalen)
{
uint8_t status;
uint8_t obsvalue;
int result;
/* Store the current lifecycle state in order to restore it after transmit
* done.
*/
nrf24l01_state_t prevstate = dev->state;
nrf24l01_tostate(dev, ST_STANDBY);
/* Flush old - can't harm */
nrf24l01_flush_tx(dev);
/* Write payload */
nrf24l01_access(dev, MODE_WRITE, NRF24L01_W_TX_PAYLOAD,
(FAR uint8_t *)data, datalen);
dev->tx_pending = true;
/* Free the SPI bus during the IRQ wait */
nrf24l01_unlock(dev->spi);
/* Cause rising CE edge to start transmission */
nrf24l01_chipenable(dev, true);
/* Wait for IRQ (TX_DS or MAX_RT) - but don't hang on lost IRQ */
result = sem_tickwait(&dev->sem_tx, clock_systimer(),
MSEC2TICK(NRF24L01_MAX_TX_IRQ_WAIT));
/* Re-acquire the SPI bus */
nrf24l01_lock(dev->spi);
dev->tx_pending = false;
if (result < 0)
{
wlerr("wait for irq failed\n");
nrf24l01_flush_tx(dev);
goto out;
}
status = nrf24l01_readreg(dev, NRF24L01_OBSERVE_TX, &obsvalue, 1);
if (status & NRF24L01_TX_DS)
{
/* Transmit OK */
result = OK;
dev->lastxmitcount = (obsvalue & NRF24L01_ARC_CNT_MASK)
>> NRF24L01_ARC_CNT_SHIFT;
wlinfo("Transmission OK (lastxmitcount=%d)\n", dev->lastxmitcount);
}
else if (status & NRF24L01_MAX_RT)
{
wlinfo("MAX_RT! (lastxmitcount=%d)\n", dev->lastxmitcount);
result = -ECOMM;
dev->lastxmitcount = NRF24L01_XMIT_MAXRT;
/* If no ACK packet is received the payload remains in TX fifo. We
* need to flush it.
*/
nrf24l01_flush_tx(dev);
}
else
{
/* Unexpected... */
wlerr("ERROR: No TX_DS nor MAX_RT bit set in STATUS reg!\n");
result = -EIO;
}
out:
/* Clear interrupt sources */
nrf24l01_writeregbyte(dev, NRF24L01_STATUS, NRF24L01_TX_DS | NRF24L01_MAX_RT);
/* Clear fifo */
nrf24l01_flush_tx(dev);
/* Restore state */
nrf24l01_tostate(dev, prevstate);
return result;
}
/****************************************************************************
* Name: binarycvt
****************************************************************************/
#ifdef CONFIG_DEBUG_WIRELESS
static void binarycvt(char *deststr, const uint8_t *srcbin, size_t srclen)
{
int i = 0;
while (i < srclen)
{
sprintf(deststr + i*2, "%02x", srcbin[i]);
++i;
}
*(deststr + i*2) = '\0';
}
#endif
/****************************************************************************
* POSIX API
****************************************************************************/
/****************************************************************************
* Name: nrf24l01_open
****************************************************************************/
static int nrf24l01_open(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct nrf24l01_dev_s *dev;
int result;
wlinfo("Opening nRF24L01 dev\n");
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
/* Get exclusive access to the driver data structure */
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
/* Check if device is not already used */
if (dev->nopens > 0)
{
result = -EBUSY;
goto errout;
}
result = nrf24l01_init(dev);
if (!result)
{
dev->nopens++;
}
errout:
sem_post(&dev->devsem);
return result;
}
/****************************************************************************
* Name: nrf24l01_close
****************************************************************************/
static int nrf24l01_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct nrf24l01_dev_s *dev;
wlinfo("Closing nRF24L01 dev\n");
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
/* Get exclusive access to the driver data structure */
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
nrf24l01_changestate(dev, ST_POWER_DOWN);
dev->nopens--;
sem_post(&dev->devsem);
return OK;
}
/****************************************************************************
* Name: nrf24l01_read
****************************************************************************/
static ssize_t nrf24l01_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
#ifndef CONFIG_WL_NRF24L01_RXSUPPORT
return -ENOSYS;
#else
FAR struct nrf24l01_dev_s *dev;
FAR struct inode *inode;
int result;
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
result = nrf24l01_recv(dev, (uint8_t *)buffer, buflen, &dev->last_recvpipeno);
sem_post(&dev->devsem);
return result;
#endif
}
/****************************************************************************
* Name: nrf24l01_write
****************************************************************************/
static ssize_t nrf24l01_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct nrf24l01_dev_s *dev;
FAR struct inode *inode;
int result;
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
result = nrf24l01_send(dev, (const uint8_t *)buffer, buflen);
sem_post(&dev->devsem);
return result;
}
/****************************************************************************
* Name: nrf24l01_ioctl
****************************************************************************/
static int nrf24l01_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct nrf24l01_dev_s *dev;
int result = OK;
wlinfo("cmd: %d arg: %ld\n", cmd, arg);
DEBUGASSERT(filep);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
/* Get exclusive access to the driver data structure */
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
/* Process the IOCTL by command */
switch (cmd)
{
case WLIOC_SETRADIOFREQ: /* Set radio frequency. Arg: Pointer to
* uint32_t frequency value */
{
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
nrf24l01_setradiofreq(dev, *ptr);
}
break;
case WLIOC_GETRADIOFREQ: /* Get current radio frequency. arg: Pointer
* to uint32_t frequency value */
{
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
*ptr = nrf24l01_getradiofreq(dev);
}
break;
case NRF24L01IOC_SETTXADDR: /* Set current TX addr. arg: Pointer to
* uint8_t array defining the address */
{
FAR const uint8_t *addr = (FAR const uint8_t *)(arg);
DEBUGASSERT(addr != NULL);
nrf24l01_settxaddr(dev, addr);
}
break;
case NRF24L01IOC_GETTXADDR: /* Get current TX addr. arg: Pointer to
* uint8_t array defining the address */
{
FAR uint8_t *addr = (FAR uint8_t *)(arg);
DEBUGASSERT(addr != NULL);
nrf24l01_gettxaddr(dev, addr);
}
break;
case WLIOC_SETTXPOWER: /* Set current radio frequency. arg: Pointer
* to int32_t, output power */
{
FAR int32_t *ptr = (FAR int32_t *)(arg);
DEBUGASSERT(ptr != NULL);
nrf24l01_settxpower(dev, *ptr);
}
break;
case WLIOC_GETTXPOWER: /* Get current radio frequency. arg: Pointer
* to int32_t, output power */
{
FAR int32_t *ptr = (FAR int32_t *)(arg);
DEBUGASSERT(ptr != NULL);
*ptr = nrf24l01_gettxpower(dev);
}
break;
case NRF24L01IOC_SETRETRCFG: /* Set retransmit params. arg: Pointer
* to nrf24l01_retrcfg_t */
{
FAR nrf24l01_retrcfg_t *ptr = (FAR nrf24l01_retrcfg_t *)(arg);
DEBUGASSERT(ptr != NULL);
nrf24l01_setretransmit(dev, ptr->delay, ptr->count);
}
break;
case NRF24L01IOC_GETRETRCFG: /* Get retransmit params. arg: Pointer
* to nrf24l01_retrcfg_t */
result = -ENOSYS; /* TODO */
break;
case NRF24L01IOC_SETPIPESCFG:
{
int i;
FAR nrf24l01_pipecfg_t **cfg_array = (FAR nrf24l01_pipecfg_t **)(arg);
DEBUGASSERT(cfg_array != NULL);
for (i = 0; i < NRF24L01_PIPE_COUNT; i++)
{
if (cfg_array[i])
{
nrf24l01_setpipeconfig(dev, i, cfg_array[i]);
}
}
}
break;
case NRF24L01IOC_GETPIPESCFG:
{
int i;
FAR nrf24l01_pipecfg_t **cfg_array = (FAR nrf24l01_pipecfg_t **)(arg);
DEBUGASSERT(cfg_array != NULL);
for (i = 0; i < NRF24L01_PIPE_COUNT; i++)
{
if (cfg_array[i])
{
nrf24l01_getpipeconfig(dev, i, cfg_array[i]);
}
}
}
break;
case NRF24L01IOC_SETPIPESENABLED:
{
int i;
uint8_t en_pipes;
FAR uint8_t *en_pipesp = (FAR uint8_t *)(arg);
DEBUGASSERT(en_pipesp != NULL);
en_pipes = *en_pipesp;
for (i = 0; i < NRF24L01_PIPE_COUNT; i++)
{
if ((dev->en_pipes & (1 << i)) != (en_pipes & (1 << i)))
{
nrf24l01_enablepipe(dev, i, en_pipes & (1 << i));
}
}
}
break;
case NRF24L01IOC_GETPIPESENABLED:
{
FAR uint8_t *en_pipesp = (FAR uint8_t *)(arg);
DEBUGASSERT(en_pipesp != NULL);
*en_pipesp = dev->en_pipes;
break;
}
case NRF24L01IOC_SETDATARATE:
{
FAR nrf24l01_datarate_t *drp = (FAR nrf24l01_datarate_t *)(arg);
DEBUGASSERT(drp != NULL);
nrf24l01_setdatarate(dev, *drp);
break;
}
case NRF24L01IOC_GETDATARATE:
result = -ENOSYS; /* TODO */
break;
case NRF24L01IOC_SETADDRWIDTH:
{
FAR uint32_t *widthp = (FAR uint32_t *)(arg);
DEBUGASSERT(widthp != NULL);
nrf24l01_setaddrwidth(dev, *widthp);
break;
}
case NRF24L01IOC_GETADDRWIDTH:
{
FAR int *widthp = (FAR int *)(arg);
DEBUGASSERT(widthp != NULL);
*widthp = (int)dev->addrlen;
break;
}
case NRF24L01IOC_SETSTATE:
{
FAR nrf24l01_state_t *statep = (FAR nrf24l01_state_t *)(arg);
DEBUGASSERT(statep != NULL);
nrf24l01_changestate(dev, *statep);
break;
}
case NRF24L01IOC_GETSTATE:
{
FAR nrf24l01_state_t *statep = (FAR nrf24l01_state_t *)(arg);
DEBUGASSERT(statep != NULL);
*statep = dev->state;
break;
}
case NRF24L01IOC_GETLASTXMITCOUNT:
{
FAR uint32_t *xmitcntp = (FAR uint32_t *)(arg);
DEBUGASSERT(xmitcntp != NULL);
*xmitcntp = dev->lastxmitcount;
break;
}
case NRF24L01IOC_GETLASTPIPENO:
{
FAR uint32_t *lastpipep = (FAR uint32_t *)(arg);
DEBUGASSERT(lastpipep != NULL);
*lastpipep = dev->last_recvpipeno;
break;
}
default:
result = -ENOTTY;
break;
}
sem_post(&dev->devsem);
return result;
}
/****************************************************************************
* Name: nrf24l01_poll
****************************************************************************/
#ifndef CONFIG_DISABLE_POLL
static int nrf24l01_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
#ifndef CONFIG_WL_NRF24L01_RXSUPPORT
/* Polling is currently implemented for data input only */
return -ENOSYS;
#else
FAR struct inode *inode;
FAR struct nrf24l01_dev_s *dev;
int result = OK;
wlinfo("setup: %d\n", (int)setup);
DEBUGASSERT(filep && fds);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
dev = (FAR struct nrf24l01_dev_s *)inode->i_private;
/* Exclusive access */
if (sem_wait(&dev->devsem) < 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
/* Are we setting up the poll? Or tearing it down? */
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
result = -EDEADLK;
goto errout;
}
/* Check if we can accept this poll.
* For now, only one thread can poll the device at any time
* (shorter / simpler code)
*/
if (dev->pfd)
{
result = -EBUSY;
goto errout;
}
dev->pfd = fds;
/* Is there is already data in the fifo? then trigger POLLIN now -
* don't wait for RX.
*/
sem_wait(&dev->sem_fifo);
if (dev->fifo_len > 0)
{
dev->pfd->revents |= POLLIN; /* Data available for input */
sem_post(dev->pfd->sem);
}
sem_post(&dev->sem_fifo);
}
else /* Tear it down */
{
dev->pfd = NULL;
}
errout:
sem_post(&dev->devsem);
return result;
#endif
}
#endif
/****************************************************************************
* Name: nrf24l01_unregister
****************************************************************************/
static int nrf24l01_unregister(FAR struct nrf24l01_dev_s *dev)
{
CHECK_ARGS(dev);
/* Release IRQ */
nrf24l01_attachirq(dev, NULL, NULL);
/* Free memory */
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
kmm_free(dev->rx_fifo);
#endif
kmm_free(dev);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nrf24l01_register
****************************************************************************/
int nrf24l01_register(FAR struct spi_dev_s *spi,
FAR struct nrf24l01_config_s *cfg)
{
FAR struct nrf24l01_dev_s *dev;
int result = OK;
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
uint8_t *rx_fifo;
#endif
ASSERT((spi != NULL) & (cfg != NULL));
if ((dev = kmm_malloc(sizeof(struct nrf24l01_dev_s))) == NULL)
{
return -ENOMEM;
}
dev->spi = spi;
dev->config = cfg;
dev->state = ST_UNKNOWN;
dev->en_aa = 0;
dev->ce_enabled = false;
sem_init(&(dev->devsem), 0, 1);
dev->nopens = 0;
#ifndef CONFIG_DISABLE_POLL
dev->pfd = NULL;
#endif
sem_init(&dev->sem_tx, 0, 0);
sem_setprotocol(&dev->sem_tx, SEM_PRIO_NONE);
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
if ((rx_fifo = kmm_malloc(CONFIG_WL_NRF24L01_RXFIFO_LEN)) == NULL)
{
kmm_free(dev);
return -ENOMEM;
}
dev->rx_fifo = rx_fifo;
dev->nxt_read = 0;
dev->nxt_write = 0;
dev->fifo_len = 0;
sem_init(&(dev->sem_fifo), 0, 1);
sem_init(&(dev->sem_rx), 0, 0);
sem_setprotocol(&dev->sem_rx, SEM_PRIO_NONE);
#endif
/* Configure IRQ pin (falling edge) */
nrf24l01_attachirq(dev, nrf24l01_irqhandler, dev);
/* Register the device as an input device */
wlinfo("Registering " DEV_NAME "\n");
result = register_driver(DEV_NAME, &nrf24l01_fops, 0666, dev);
if (result < 0)
{
wlerr("ERROR: register_driver() failed: %d\n", result);
nrf24l01_unregister(dev);
}
return result;
}
/****************************************************************************
* Name: nrf24l01_init
*
* Description:
* (Re)set the device in a default initial state
*
****************************************************************************/
int nrf24l01_init(FAR struct nrf24l01_dev_s *dev)
{
int result = OK;
uint8_t features;
CHECK_ARGS(dev);
nrf24l01_lock(dev->spi);
/* Configure the SPI parameters before communicating */
nrf24l01_configspi(dev->spi);
/* Enable features. */
nrf24l01_writeregbyte(dev, NRF24L01_FEATURE, NRF24L01_EN_DPL);
features = nrf24l01_readregbyte(dev, NRF24L01_FEATURE);
if (0 == features)
{
/* The ACTIVATE instruction is not documented in the nRF24L01+ docs.
* However it is referenced / described by many sources on Internet,
*
* Is it for nRF24L01 (not +) hardware ?
*/
uint8_t v = 0x73;
nrf24l01_access(dev, MODE_WRITE, NRF24L01_ACTIVATE, &v, 1);
features = nrf24l01_readregbyte(dev, NRF24L01_FEATURE);
if (0 == features)
{
/* If FEATURES reg is still unset here, consider there is no
* actual hardware.
*/
result = -ENODEV;
goto out;
}
}
/* Set initial state */
nrf24l01_tostate(dev, ST_POWER_DOWN);
/* Disable all pipes */
dev->en_pipes = 0;
nrf24l01_writeregbyte(dev, NRF24L01_EN_RXADDR, 0);
/* Set addr width to default */
dev->addrlen = CONFIG_WL_NRF24L01_DFLT_ADDR_WIDTH;
nrf24l01_writeregbyte(dev, NRF24L01_SETUP_AW,
CONFIG_WL_NRF24L01_DFLT_ADDR_WIDTH - 2);
/* Get pipe #0 addr */
nrf24l01_readreg(dev, NRF24L01_RX_ADDR_P0, dev->pipe0addr, dev->addrlen);
dev->en_aa = nrf24l01_readregbyte(dev, NRF24L01_EN_AA);
/* Flush HW fifo */
nrf24l01_flush_rx(dev);
nrf24l01_flush_tx(dev);
/* Clear interrupt sources (useful ?) */
nrf24l01_writeregbyte(dev, NRF24L01_STATUS,
NRF24L01_RX_DR | NRF24L01_TX_DS | NRF24L01_MAX_RT);
out:
nrf24l01_unlock(dev->spi);
return result;
}
/****************************************************************************
* Name: nrf24l01_setpipeconfig
****************************************************************************/
int nrf24l01_setpipeconfig(FAR struct nrf24l01_dev_s *dev,
unsigned int pipeno,
FAR const nrf24l01_pipecfg_t *pipecfg)
{
bool dynlength;
bool en_aa;
int addrlen;
CHECK_ARGS(dev && pipecfg && pipeno < NRF24L01_PIPE_COUNT);
dynlength = (pipecfg->payload_length == NRF24L01_DYN_LENGTH);
/* Need to enable AA to enable dynamic length payload */
en_aa = dynlength || pipecfg->en_aa;
nrf24l01_lock(dev->spi);
/* Set addr
* Pipe 0 & 1 are the only ones to have a full length address.
*/
addrlen = (pipeno <= 1) ? dev->addrlen : 1;
nrf24l01_writereg(dev, NRF24L01_RX_ADDR_P0 + pipeno, pipecfg->rx_addr,
addrlen);
/* Auto ack */
if (en_aa)
{
dev->en_aa |= 1 << pipeno;
}
else
{
dev->en_aa &= ~(1 << pipeno);
}
nrf24l01_setregbit(dev, NRF24L01_EN_AA, 1 << pipeno, en_aa);
/* Payload config */
nrf24l01_setregbit(dev, NRF24L01_DYNPD, 1 << pipeno, dynlength);
if (!dynlength)
{
nrf24l01_writeregbyte(dev, NRF24L01_RX_PW_P0 + pipeno,
pipecfg->payload_length);
}
nrf24l01_unlock(dev->spi);
dev->pipedatalen[pipeno] = pipecfg->payload_length;
return OK;
}
/****************************************************************************
* Name: nrf24l01_getpipeconfig
****************************************************************************/
int nrf24l01_getpipeconfig(FAR struct nrf24l01_dev_s *dev,
unsigned int pipeno,
FAR nrf24l01_pipecfg_t *pipecfg)
{
bool dynlength;
int addrlen;
CHECK_ARGS(dev && pipecfg && pipeno < NRF24L01_PIPE_COUNT);
nrf24l01_lock(dev->spi);
/* Get pipe address.
* Pipe 0 & 1 are the only ones to have a full length address.
*/
addrlen = (pipeno <= 1) ? dev->addrlen : 1;
nrf24l01_readreg(dev, NRF24L01_RX_ADDR_P0 + pipeno, pipecfg->rx_addr,
addrlen);
/* Auto ack */
pipecfg->en_aa = ((nrf24l01_readregbyte(dev, NRF24L01_EN_AA) & (1 << pipeno)) != 0);
/* Payload config */
dynlength = ((nrf24l01_readregbyte(dev, NRF24L01_DYNPD) & (1 << pipeno)) != 0);
if (dynlength)
{
pipecfg->payload_length = NRF24L01_DYN_LENGTH;
}
else
{
pipecfg->payload_length = nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P0 + pipeno);
}
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_enablepipe
****************************************************************************/
int nrf24l01_enablepipe(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno,
bool enable)
{
CHECK_ARGS(dev && pipeno < NRF24L01_PIPE_COUNT);
uint8_t rxaddrval;
uint8_t pipemask = 1 << pipeno;
nrf24l01_lock(dev->spi);
/* Enable pipe on nRF24L01 */
rxaddrval = nrf24l01_readregbyte(dev, NRF24L01_EN_RXADDR);
if (enable)
{
rxaddrval |= pipemask;
}
else
{
rxaddrval &= ~pipemask;
}
nrf24l01_writeregbyte(dev, NRF24L01_EN_RXADDR, rxaddrval);
nrf24l01_unlock(dev->spi);
/* Update cached value */
dev->en_pipes = rxaddrval;
return OK;
}
/****************************************************************************
* Name: nrf24l01_settxaddr
****************************************************************************/
int nrf24l01_settxaddr(FAR struct nrf24l01_dev_s *dev,
FAR const uint8_t *txaddr)
{
CHECK_ARGS(dev && txaddr);
nrf24l01_lock(dev->spi);
nrf24l01_writereg(dev, NRF24L01_TX_ADDR, txaddr, dev->addrlen);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_gettxaddr
****************************************************************************/
int nrf24l01_gettxaddr(FAR struct nrf24l01_dev_s *dev, FAR uint8_t *txaddr)
{
CHECK_ARGS(dev && txaddr);
nrf24l01_lock(dev->spi);
nrf24l01_readreg(dev, NRF24L01_TX_ADDR, txaddr, dev->addrlen);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_setretransmit
****************************************************************************/
int nrf24l01_setretransmit(FAR struct nrf24l01_dev_s *dev,
nrf24l01_retransmit_delay_t retrdelay,
uint8_t retrcount)
{
uint8_t val;
CHECK_ARGS(dev && retrcount <= NRF24L01_MAX_XMIT_RETR);
val = (retrdelay << NRF24L01_ARD_SHIFT) | (retrcount << NRF24L01_ARC_SHIFT);
nrf24l01_lock(dev->spi);
nrf24l01_writeregbyte(dev, NRF24L01_SETUP_RETR, val);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_settxpower
****************************************************************************/
int nrf24l01_settxpower(FAR struct nrf24l01_dev_s *dev, int outpower)
{
uint8_t value;
uint8_t hwpow;
/* RF_PWR value <-> Output power in dBm
*
* '00' -18dBm
* '01' -12dBm
* '10' -6dBm
* '11' 0dBm
*/
switch (outpower)
{
case 0:
hwpow = 3 << NRF24L01_RF_PWR_SHIFT;
break;
case -6:
hwpow = 2 << NRF24L01_RF_PWR_SHIFT;
break;
case -12:
hwpow = 1 << NRF24L01_RF_PWR_SHIFT;
break;
case -18:
hwpow = 0;
break;
default:
return -EINVAL;
}
nrf24l01_lock(dev->spi);
value = nrf24l01_readregbyte(dev, NRF24L01_RF_SETUP);
value &= ~(NRF24L01_RF_PWR_MASK);
value |= hwpow;
nrf24l01_writeregbyte(dev, NRF24L01_RF_SETUP, value);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_gettxpower
****************************************************************************/
int nrf24l01_gettxpower(FAR struct nrf24l01_dev_s *dev)
{
uint8_t value;
int powers[] = { -18, -12, -6, 0};
nrf24l01_lock(dev->spi);
value = nrf24l01_readregbyte(dev, NRF24L01_RF_SETUP);
nrf24l01_unlock(dev->spi);
value = (value & NRF24L01_RF_PWR_MASK) >> NRF24L01_RF_PWR_SHIFT;
return powers[value];
}
/****************************************************************************
* Name: nrf24l01_setdatarate
****************************************************************************/
int nrf24l01_setdatarate(FAR struct nrf24l01_dev_s *dev,
nrf24l01_datarate_t datarate)
{
uint8_t value;
nrf24l01_lock(dev->spi);
value = nrf24l01_readregbyte(dev, NRF24L01_RF_SETUP);
value &= ~(NRF24L01_RF_DR_HIGH | NRF24L01_RF_DR_LOW);
switch (datarate)
{
case RATE_1Mbps:
break;
case RATE_2Mbps:
value |= NRF24L01_RF_DR_HIGH;
break;
case RATE_250kbps:
value |= NRF24L01_RF_DR_LOW;
break;
}
nrf24l01_writeregbyte(dev, NRF24L01_RF_SETUP, value);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_setradiofreq
****************************************************************************/
int nrf24l01_setradiofreq(FAR struct nrf24l01_dev_s *dev, uint32_t freq)
{
uint8_t value;
CHECK_ARGS(dev && freq >= NRF24L01_MIN_FREQ && freq <= NRF24L01_MAX_FREQ);
value = freq - NRF24L01_MIN_FREQ;
nrf24l01_lock(dev->spi);
nrf24l01_writeregbyte(dev, NRF24L01_RF_CH, value);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_getradiofreq
****************************************************************************/
uint32_t nrf24l01_getradiofreq(FAR struct nrf24l01_dev_s *dev)
{
int rffreq;
CHECK_ARGS(dev);
nrf24l01_lock(dev->spi);
rffreq = (int)nrf24l01_readregbyte(dev, NRF24L01_RF_CH);
nrf24l01_unlock(dev->spi);
return rffreq + NRF24L01_MIN_FREQ;
}
/****************************************************************************
* Name: nrf24l01_setaddrwidth
****************************************************************************/
int nrf24l01_setaddrwidth(FAR struct nrf24l01_dev_s *dev, uint32_t width)
{
CHECK_ARGS(dev && width <= NRF24L01_MAX_ADDR_LEN &&
width >= NRF24L01_MIN_ADDR_LEN);
nrf24l01_lock(dev->spi);
nrf24l01_writeregbyte(dev, NRF24L01_SETUP_AW, width-2);
nrf24l01_unlock(dev->spi);
dev->addrlen = width;
return OK;
}
/****************************************************************************
* Name: nrf24l01_changestate
****************************************************************************/
int nrf24l01_changestate(FAR struct nrf24l01_dev_s *dev, nrf24l01_state_t state)
{
nrf24l01_lock(dev->spi);
nrf24l01_tostate(dev, state);
nrf24l01_unlock(dev->spi);
return OK;
}
/****************************************************************************
* Name: nrf24l01_send
****************************************************************************/
int nrf24l01_send(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data,
size_t datalen)
{
int result;
CHECK_ARGS(dev && data && datalen <= NRF24L01_MAX_PAYLOAD_LEN);
nrf24l01_lock(dev->spi);
result = dosend(dev, data, datalen);
nrf24l01_unlock(dev->spi);
return result;
}
/****************************************************************************
* Name: nrf24l01_sendto
****************************************************************************/
int nrf24l01_sendto(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data,
size_t datalen, FAR const uint8_t *destaddr)
{
bool pipeaddrchg = false;
int result;
nrf24l01_lock(dev->spi);
/* If AA is enabled (pipe 0 is active and its AA flag is set) and the dest
* addr is not the current pipe 0 addr we need to change pipe 0 addr in
* order to receive the ACK packet.
*/
if ((dev->en_aa & 1) && (memcmp(destaddr, dev->pipe0addr, dev->addrlen)))
{
wlinfo("Change pipe #0 addr to dest addr\n");
nrf24l01_writereg(dev, NRF24L01_RX_ADDR_P0, destaddr,
NRF24L01_MAX_ADDR_LEN);
pipeaddrchg = true;
}
result = dosend(dev, data, datalen);
if (pipeaddrchg)
{
/* Restore pipe #0 addr */
nrf24l01_writereg(dev, NRF24L01_RX_ADDR_P0, dev->pipe0addr,
NRF24L01_MAX_ADDR_LEN);
wlinfo("Pipe #0 default addr restored\n");
}
nrf24l01_unlock(dev->spi);
return result;
}
/****************************************************************************
* Name: nrf24l01_lastxmitcount
****************************************************************************/
int nrf24l01_lastxmitcount(FAR struct nrf24l01_dev_s *dev)
{
return dev->lastxmitcount;
}
/****************************************************************************
* Name: nrf24l01_recv
****************************************************************************/
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
ssize_t nrf24l01_recv(struct nrf24l01_dev_s *dev, uint8_t *buffer,
size_t buflen, uint8_t *recvpipe)
{
if (sem_wait(&dev->sem_rx) != 0)
{
/* This should only happen if the wait was canceled by an signal */
DEBUGASSERT(errno == EINTR);
return -EINTR;
}
return fifoget(dev, buffer, buflen, recvpipe);
}
#endif
/****************************************************************************
* Name: nrf24l01_dumpregs
****************************************************************************/
#ifdef CONFIG_DEBUG_WIRELESS
void nrf24l01_dumpregs(struct nrf24l01_dev_s *dev)
{
uint8_t addr[NRF24L01_MAX_ADDR_LEN];
char addrstr[NRF24L01_MAX_ADDR_LEN * 2 +1];
syslog(LOG_INFO, "CONFIG: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_CONFIG));
syslog(LOG_INFO, "EN_AA: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_EN_AA));
syslog(LOG_INFO, "EN_RXADDR: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_EN_RXADDR));
syslog(LOG_INFO, "SETUP_AW: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_SETUP_AW));
syslog(LOG_INFO, "SETUP_RETR:%02x\n",
nrf24l01_readregbyte(dev, NRF24L01_SETUP_RETR));
syslog(LOG_INFO, "RF_CH: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RF_CH));
syslog(LOG_INFO, "RF_SETUP: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RF_SETUP));
syslog(LOG_INFO, "STATUS: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_STATUS));
syslog(LOG_INFO, "OBS_TX: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_OBSERVE_TX));
nrf24l01_readreg(dev, NRF24L01_TX_ADDR, addr, dev->addrlen);
binarycvt(addrstr, addr, dev->addrlen);
syslog(LOG_INFO, "TX_ADDR: %s\n", addrstr);
syslog(LOG_INFO, "CD: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_CD));
syslog(LOG_INFO, "RX_PW_P0: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P0));
syslog(LOG_INFO, "RX_PW_P1: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P1));
syslog(LOG_INFO, "RX_PW_P2: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P2));
syslog(LOG_INFO, "RX_PW_P3: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P3));
syslog(LOG_INFO, "RX_PW_P4: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P4));
syslog(LOG_INFO, "RX_PW_P5: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P5));
syslog(LOG_INFO, "FIFO_STAT: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_FIFO_STATUS));
syslog(LOG_INFO, "DYNPD: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_DYNPD));
syslog(LOG_INFO, "FEATURE: %02x\n",
nrf24l01_readregbyte(dev, NRF24L01_FEATURE));
}
#endif /* CONFIG_DEBUG_WIRELESS */
/****************************************************************************
* Name: nrf24l01_dumprxfifo
****************************************************************************/
#if defined(CONFIG_DEBUG_WIRELESS) && defined(CONFIG_WL_NRF24L01_RXSUPPORT)
void nrf24l01_dumprxfifo(struct nrf24l01_dev_s *dev)
{
syslog(LOG_INFO, "bytes count: %d\n", dev->fifo_len);
syslog(LOG_INFO, "next read: %d, next write: %d\n",
dev->nxt_read, dev-> nxt_write);
}
#endif /* CONFIG_DEBUG_WIRELESS && CONFIG_WL_NRF24L01_RXSUPPORT */