incubator-nuttx/drivers/wireless/nrf24l01.c

2121 lines
57 KiB
C

/****************************************************************************
* drivers/wireless/nrf24l01.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* 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 <poll.h>
#include <debug.h>
#include <fcntl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/signal.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 5
#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 */
bool tx_payload_noack; /* TX without waiting for ACK */
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 */
mutex_t lock_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 */
mutex_t devlock; /* Ensures exclusive access to this structure */
FAR struct pollfd *pfd; /* Polled file descr (or NULL if any) */
};
/****************************************************************************
* 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,
FAR 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(FAR struct nrf24l01_dev_s *dev, uint8_t pipeno,
FAR uint8_t *buffer, uint8_t buflen);
static uint8_t fifoget(FAR 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(FAR char *deststr, size_t destlen,
FAR 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);
static int nrf24l01_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_nrf24l01_fops =
{
nrf24l01_open, /* open */
nrf24l01_close, /* close */
nrf24l01_read, /* read */
nrf24l01_write, /* write */
NULL, /* seek */
nrf24l01_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
nrf24l01_poll /* poll */
};
/****************************************************************************
* 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
*/
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);
SPI_HWFEATURES(spi, 0);
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.
*
* Input 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. */
SPI_LOCK(spi, false);
}
/****************************************************************************
* Name: nrf24l01_configspi
*
* Description:
* Configure the SPI for use with the NRF24L01.
*
* Input 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);
SPI_HWFEATURES(spi, 0);
SPI_SETFREQUENCY(spi, NRF24L01_SPIFREQ);
SPI_SELECT(spi, SPIDEV_WIRELESS(0), false);
}
/****************************************************************************
* Name: nrf24l01_select
****************************************************************************/
static inline void nrf24l01_select(FAR struct nrf24l01_dev_s *dev)
{
SPI_SELECT(dev->spi, SPIDEV_WIRELESS(0), true);
}
/****************************************************************************
* Name: nrf24l01_deselect
****************************************************************************/
static inline void nrf24l01_deselect(FAR 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(FAR 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(FAR 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(FAR 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(FAR 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)
{
nxmutex_lock(&dev->lock_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;
}
nxmutex_unlock(&dev->lock_fifo);
}
/****************************************************************************
* Name: fifoget
****************************************************************************/
static uint8_t fifoget(FAR struct nrf24l01_dev_s *dev, FAR uint8_t *buffer,
uint8_t buflen, FAR uint8_t *pipeno)
{
uint8_t pktlen;
uint8_t i;
nxmutex_lock(&dev->lock_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:
nxmutex_unlock(&dev->lock_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 */
nxsem_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);
bool has_data = false;
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);
has_data = true;
nxsem_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);
if (dev->pfd && has_data)
{
poll_notify(&dev->pfd, 1, POLLIN);
}
/* 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 */
nxsem_post(&dev->sem_tx);
}
}
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(FAR 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);
nxsig_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;
uint8_t cmd;
int ret;
/* 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 - use different command depending on ACK setting */
cmd = dev->tx_payload_noack ? NRF24L01_W_TX_PAYLOAD_NOACK :
NRF24L01_W_TX_PAYLOAD;
nrf24l01_access(dev, MODE_WRITE, cmd, (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 */
ret = nxsem_tickwait(&dev->sem_tx, MSEC2TICK(NRF24L01_MAX_TX_IRQ_WAIT));
/* Re-acquire the SPI bus */
nrf24l01_lock(dev->spi);
dev->tx_pending = false;
if (ret < 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 */
ret = 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);
ret = -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");
ret = -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 ret;
}
/****************************************************************************
* Name: binarycvt
****************************************************************************/
#ifdef CONFIG_DEBUG_WIRELESS
static void binarycvt(FAR char *deststr, size_t destlen,
FAR const uint8_t *srcbin, size_t srclen)
{
int i = 0;
while (i < srclen && 2 * (i + 1) < destlen)
{
snprintf(deststr + i * 2, destlen - 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 ret;
wlinfo("Opening nRF24L01 dev\n");
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
/* Get exclusive access to the driver data structure */
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
/* Check if device is not already used */
if (dev->nopens > 0)
{
ret = -EBUSY;
goto errout;
}
ret = nrf24l01_init(dev);
if (!ret)
{
dev->nopens++;
}
errout:
nxmutex_unlock(&dev->devlock);
return ret;
}
/****************************************************************************
* Name: nrf24l01_close
****************************************************************************/
static int nrf24l01_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct nrf24l01_dev_s *dev;
int ret;
wlinfo("Closing nRF24L01 dev\n");
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
/* Get exclusive access to the driver data structure */
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
nrf24l01_changestate(dev, ST_POWER_DOWN);
dev->nopens--;
nxmutex_unlock(&dev->devlock);
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 ret;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
if (filep->f_oflags & O_NONBLOCK)
{
int packet_count;
/* Test if data is ready */
ret = nxsem_get_value(&dev->sem_rx, &packet_count);
if (ret)
{
goto errout; /* getvalue failed */
}
if (!packet_count)
{
ret = -EWOULDBLOCK; /* don't wait for packets */
goto errout;
}
}
ret = nrf24l01_recv(dev, (FAR uint8_t *)buffer, buflen,
&dev->last_recvpipeno);
errout:
nxmutex_unlock(&dev->devlock);
return ret;
#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 ret;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
ret = nrf24l01_send(dev, (FAR const uint8_t *)buffer, buflen);
nxmutex_unlock(&dev->devlock);
return ret;
}
/****************************************************************************
* 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 ret;
wlinfo("cmd: %d arg: %ld\n", cmd, arg);
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
/* Get exclusive access to the driver data structure */
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
/* 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 */
ret = -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:
ret = -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;
}
case NRF24L01IOC_SETTXPAYLOADNOACK:
{
FAR uint32_t *tx_payload_noack = (FAR uint32_t *)(arg);
DEBUGASSERT(tx_payload_noack != NULL);
dev->tx_payload_noack = (*tx_payload_noack) > 0;
break;
}
case NRF24L01IOC_GETTXPAYLOADNOACK:
{
FAR uint32_t *tx_payload_noack = (FAR uint32_t *)(arg);
DEBUGASSERT(tx_payload_noack != NULL);
*tx_payload_noack = dev->tx_payload_noack ? 1 : 0;
break;
}
default:
ret = -ENOTTY;
break;
}
nxmutex_unlock(&dev->devlock);
return ret;
}
/****************************************************************************
* Name: nrf24l01_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 ret;
wlinfo("setup: %d\n", (int)setup);
DEBUGASSERT(fds);
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
/* Exclusive access */
ret = nxmutex_lock(&dev->devlock);
if (ret < 0)
{
return ret;
}
/* Are we setting up the poll? Or tearing it down? */
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
ret = -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)
{
ret = -EBUSY;
goto errout;
}
dev->pfd = fds;
/* Is there is already data in the fifo? then trigger POLLIN now -
* don't wait for RX.
*/
nxmutex_lock(&dev->lock_fifo);
if (dev->fifo_len > 0)
{
poll_notify(&dev->pfd, 1, POLLIN);
}
nxmutex_unlock(&dev->lock_fifo);
}
else /* Tear it down */
{
dev->pfd = NULL;
}
errout:
nxmutex_unlock(&dev->devlock);
return ret;
#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
nxmutex_destroy(&dev->lock_fifo);
nxsem_destroy(&dev->sem_rx);
kmm_free(dev->rx_fifo);
#endif
nxmutex_destroy(&dev->devlock);
nxsem_destroy(&dev->sem_tx);
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 ret = OK;
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
FAR uint8_t *rx_fifo;
#endif
DEBUGASSERT((spi != NULL) & (cfg != NULL));
if ((dev = kmm_zalloc(sizeof(struct nrf24l01_dev_s))) == NULL)
{
return -ENOMEM;
}
dev->spi = spi;
dev->config = cfg;
dev->state = ST_UNKNOWN;
dev->ce_enabled = false;
nxmutex_init(&dev->devlock);
nxsem_init(&dev->sem_tx, 0, 0);
#ifdef CONFIG_WL_NRF24L01_RXSUPPORT
if ((rx_fifo = kmm_malloc(CONFIG_WL_NRF24L01_RXFIFO_LEN)) == NULL)
{
nxmutex_destroy(&dev->devlock);
nxsem_destroy(&dev->sem_tx);
kmm_free(dev);
return -ENOMEM;
}
dev->rx_fifo = rx_fifo;
nxmutex_init(&dev->lock_fifo);
nxsem_init(&dev->sem_rx, 0, 0);
#endif
/* Configure IRQ pin (falling edge) */
nrf24l01_attachirq(dev, nrf24l01_irqhandler, dev);
/* Register the device as an input device */
wlinfo("Registering " DEV_NAME "\n");
ret = register_driver(DEV_NAME, &g_nrf24l01_fops, 0666, dev);
if (ret < 0)
{
wlerr("ERROR: register_driver() failed: %d\n", ret);
nrf24l01_unregister(dev);
}
return ret;
}
/****************************************************************************
* Name: nrf24l01_init
*
* Description:
* (Re)set the device in a default initial state
*
****************************************************************************/
int nrf24l01_init(FAR struct nrf24l01_dev_s *dev)
{
int ret = OK;
uint8_t features;
CHECK_ARGS(dev);
nrf24l01_lock(dev->spi);
/* Configure the SPI parameters before communicating */
nrf24l01_configspi(dev->spi);
/* Enable features in hardware: dynamic payload length + sending without
* expecting ACK
*/
nrf24l01_writeregbyte(dev, NRF24L01_FEATURE, NRF24L01_EN_DPL |
NRF24L01_EN_DYN_ACK);
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.
*/
ret = -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 ret;
}
/****************************************************************************
* 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 ret;
CHECK_ARGS(dev && data && datalen <= NRF24L01_MAX_PAYLOAD_LEN);
nrf24l01_lock(dev->spi);
ret = dosend(dev, data, datalen);
nrf24l01_unlock(dev->spi);
return ret;
}
/****************************************************************************
* 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 ret;
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;
}
ret = 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 ret;
}
/****************************************************************************
* 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(FAR struct nrf24l01_dev_s *dev, FAR uint8_t *buffer,
size_t buflen, FAR uint8_t *recvpipe)
{
int ret = nxsem_wait(&dev->sem_rx);
if (ret < 0)
{
return ret;
}
return fifoget(dev, buffer, buflen, recvpipe);
}
#endif
/****************************************************************************
* Name: nrf24l01_dumpregs
****************************************************************************/
#ifdef CONFIG_DEBUG_WIRELESS
void nrf24l01_dumpregs(FAR 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, sizeof(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(FAR 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 */