diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-06-01 08:03:55 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-06-01 08:03:55 -0600 |
commit | c9f4d6abf9560ca89dccbae0296a32f508744270 (patch) | |
tree | 42386bef4bbd84add2e6b1730516f412dd176e5d | |
parent | 83d8a80691ba5ae0b9288c8767342ef535ef4958 (diff) | |
download | px4-nuttx-c9f4d6abf9560ca89dccbae0296a32f508744270.tar.gz px4-nuttx-c9f4d6abf9560ca89dccbae0296a32f508744270.tar.bz2 px4-nuttx-c9f4d6abf9560ca89dccbae0296a32f508744270.zip |
Add NSRF42L01 driver. From Laurent Latil
-rw-r--r-- | apps/ChangeLog.txt | 5 | ||||
-rw-r--r-- | nuttx/ChangeLog | 9 | ||||
-rwxr-xr-x | nuttx/configs/vsn/nsh/defconfig | 1 | ||||
-rw-r--r-- | nuttx/drivers/wireless/ISM1_868MHzGFSK100kbps.c | 123 | ||||
-rw-r--r-- | nuttx/drivers/wireless/ISM2_905MHzGFSK250kbps.c | 121 | ||||
-rw-r--r-- | nuttx/drivers/wireless/Kconfig | 52 | ||||
-rw-r--r-- | nuttx/drivers/wireless/Make.defs | 14 | ||||
-rw-r--r-- | nuttx/drivers/wireless/cc1101.c (renamed from nuttx/drivers/wireless/cc1101/cc1101.c) | 748 | ||||
-rw-r--r-- | nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c | 113 | ||||
-rw-r--r-- | nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c | 111 | ||||
-rw-r--r-- | nuttx/drivers/wireless/cc1101/Kconfig | 4 | ||||
-rw-r--r-- | nuttx/drivers/wireless/nrf24l01.c | 1772 | ||||
-rw-r--r-- | nuttx/drivers/wireless/nrf24l01.h | 183 | ||||
-rw-r--r-- | nuttx/include/nuttx/fs/ioctl.h | 7 | ||||
-rw-r--r-- | nuttx/include/nuttx/wireless/cc1101.h | 391 | ||||
-rw-r--r-- | nuttx/include/nuttx/wireless/nrf24l01.h | 444 | ||||
-rw-r--r-- | nuttx/include/nuttx/wireless/wireless.h | 74 |
17 files changed, 3415 insertions, 757 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index 4680ab194..8dc8ce783 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -566,3 +566,8 @@ (2013-5-26). * apps/examples/slcd: This test now sets the SLCD brightness level to the mid-point as part of its initialization (2013-5-27). + * .gitignore: Clean-up of most all .gitignore files: Make scope of + ignore to be only the current directory; Ignore .dSYM files in + directories where .exe's may be build. Also, in Makefiles, + clean .dSYM files in directories where .exe may be built (2013-5-30). + diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index f909c708a..8dc73af5e 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -4839,3 +4839,12 @@ avoid requests of exactly MAXPACKET size and, hence, avoid so many NULL packets. Also, fix the OUT request buffers size to exactly the max packet size. It cannot be any other size (2013-5-29). + * .gitignore: Clean-up of most all .gitignore files: Make scope of + ignore to be only the current directory; Ignore .dSYM files in + directories where .exe's may be build. Also, in Makefiles, + clean .dSYM files in directories where .exe may be built (2013-5-30). + * drivers/wireless/nrf24101.c/.h and include/nuttx/wireless/nrf24101.h: + Add new driver for the wireless nRF24L01+ transceiver. From Laurent + Latil (2013-6-1). + * drivers/wireless/cc1101: Move files in the cc1101 up one directory. + From Laurent Latil (2013-6-1). diff --git a/nuttx/configs/vsn/nsh/defconfig b/nuttx/configs/vsn/nsh/defconfig index 35f787bfc..49de28aa8 100755 --- a/nuttx/configs/vsn/nsh/defconfig +++ b/nuttx/configs/vsn/nsh/defconfig @@ -194,6 +194,7 @@ CONFIG_I2C=y # Note: this has (at the moment) nothing to do with WIFI # CONFIG_WIRELESS=y +CONFIG_WL_CC1101=y # # OS support for hardware RTC diff --git a/nuttx/drivers/wireless/ISM1_868MHzGFSK100kbps.c b/nuttx/drivers/wireless/ISM1_868MHzGFSK100kbps.c new file mode 100644 index 000000000..a02b2838a --- /dev/null +++ b/nuttx/drivers/wireless/ISM1_868MHzGFSK100kbps.c @@ -0,0 +1,123 @@ +/**************************************************************************** + * drivers/wireless/ISM1_868MHzGFSK100kbps.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Copyright (C) 2011 Ales Verbic. All rights reserved. + * + * Authors: Uros Platise <uros.platise@isotel.eu> + * Ales Verbic <ales.verbic@isotel.eu> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/wireless/cc1101.h> + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/** Settings for 868 MHz, GFSK at 100kbps + * + * ISM Region 1 (Europe) only, Band 868–870 MHz + * + * Frequency ERP Duty Cycle Bandwidth Remarks + * 868 – 868.6 MHz +14 dBm < 1% No limits + * 868.7 – 869.2 MHz +14 dBm < 0.1% No limits + * 869.3 – 869.4 MHz +10 dBm No limits < 25 kHz Appropriate access protocol required + * 869.4 – 869.65 MHz +27 dBm < 10% < 25 kHz Channels may be combined to one high speed channel + * 869.7 -870 MHz +7 dBm No limits No limits + * + * Deviation = 46.142578 + * Base frequency = 867.999985 + * Carrier frequency = 867.999985 + * Channel number = 0 + * Carrier frequency = 867.999985 + * Modulated = true + * Modulation format = GFSK + * Manchester enable = false + * Sync word qualifier mode = 30/32 sync word bits detected + * Preamble count = 4 + * Channel spacing = 199.813843 + * Carrier frequency = 867.999985 + * Data rate = 99.9069 + * RX filter BW = 210.937500 + * Data format = Normal mode + * Length config = Fixed packet length mode. Length configured in PKTLEN register + * CRC enable = true + * Packet length = 62 + * Device address = 00 + * Address config = NO Address check, no broadcast + * CRC autoflush = true + * PA ramping = false + * TX power = 0 + */ + +const struct c1101_rfsettings_s cc1101_rfsettings_ISM1_868MHzGFSK100kbps = +{ + .FSCTRL1 = 0x08, /* FSCTRL1 Frequency Synthesizer Control */ + .FSCTRL0 = 0x00, /* FSCTRL0 Frequency Synthesizer Control */ + + .FREQ2 = 0x20, /* FREQ2 Frequency Control Word, High Byte */ + .FREQ1 = 0x25, /* FREQ1 Frequency Control Word, Middle Byte */ + .FREQ0 = 0xED, /* FREQ0 Frequency Control Word, Low Byte */ + + .MDMCFG4 = 0x8B, /* MDMCFG4 Modem Configuration */ + .MDMCFG3 = 0xE5, /* MDMCFG3 Modem Configuration */ + .MDMCFG2 = 0x13, /* MDMCFG2 Modem Configuration */ + .MDMCFG1 = 0x22, /* MDMCFG1 Modem Configuration */ + .MDMCFG0 = 0xE5, /* MDMCFG0 Modem Configuration */ + + .DEVIATN = 0x46, /* DEVIATN Modem Deviation Setting */ + + .FOCCFG = 0x1D, /* FOCCFG Frequency Offset Compensation Configuration */ + + .BSCFG = 0x1C, /* BSCFG Bit Synchronization Configuration */ + + .AGCCTRL2= 0xC7, /* AGCCTRL2 AGC Control */ + .AGCCTRL1= 0x00, /* AGCCTRL1 AGC Control */ + .AGCCTRL0= 0xB2, /* AGCCTRL0 AGC Control */ + + .FREND1 = 0xB6, /* FREND1 Front End RX Configuration */ + .FREND0 = 0x10, /* FREND0 Front End TX Configuration */ + + .FSCAL3 = 0xEA, /* FSCAL3 Frequency Synthesizer Calibration */ + .FSCAL2 = 0x2A, /* FSCAL2 Frequency Synthesizer Calibration */ + .FSCAL1 = 0x00, /* FSCAL1 Frequency Synthesizer Calibration */ + .FSCAL0 = 0x1F, /* FSCAL0 Frequency Synthesizer Calibration */ + + .CHMIN = 0, /* Fix at 9th channel: 869.80 MHz +- 100 kHz RF Bandwidth */ + .CHMAX = 9, /* single channel */ + + .PAMAX = 8, /* 0 means power OFF, 8 represents PA[7] */ + .PA = {0x03, 0x0F, 0x1E, 0x27, 0x67, 0x50, 0x81, 0xC2} +}; diff --git a/nuttx/drivers/wireless/ISM2_905MHzGFSK250kbps.c b/nuttx/drivers/wireless/ISM2_905MHzGFSK250kbps.c new file mode 100644 index 000000000..c78cee586 --- /dev/null +++ b/nuttx/drivers/wireless/ISM2_905MHzGFSK250kbps.c @@ -0,0 +1,121 @@ +/**************************************************************************** + * drivers/wireless/ISM2_905MHzGFSK250kbps.c + * + * Copyright (C) 2011 Uros Platise. All rights reserved. + * Copyright (C) 2011 Ales Verbic. All rights reserved. + * + * Authors: Uros Platise <uros.platise@isotel.eu> + * Ales Verbic <ales.verbic@isotel.eu> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/wireless/cc1101.h> + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/** Settings for 905 MHz, GFSK at 250kbps + * + * ISM Region 2 (America) only, Band 902–928 MHz + * + * Cordless phones 1 W + * Microwave ovens 750 W + * Industrial heaters 100 kW + * Military radar 1000 kW + * + * Deviation = 126.953125 + * Base frequency = 901.999969 + * Carrier frequency = 905.998993 + * Channel number = 20 + * Carrier frequency = 905.998993 + * Modulated = true + * Modulation format = GFSK + * Manchester enable = false + * Sync word qualifier mode = 30/32 sync word bits detected + * Preamble count = 4 + * Channel spacing = 199.951172 + * Carrier frequency = 905.998993 + * Data rate = 249.939 + * RX filter BW = 541.666667 + * Data format = Normal mode + * Length config = Variable packet length mode. Packet length configured by the first byte after sync word + * CRC enable = true + * Packet length = 61 + * Device address = 0 + * Address config = No address check + * CRC autoflush = false + * PA ramping = false + * TX power = 0 + */ + +const struct c1101_rfsettings_s cc1101_rfsettings_ISM2_905MHzGFSK250kbps = +{ + .FSCTRL1 = 0x0C, /* FSCTRL1 Frequency Synthesizer Control */ + .FSCTRL0 = 0x00, /* FSCTRL0 Frequency Synthesizer Control */ + + .FREQ2 = 0x22, /* FREQ2 Frequency Control Word, High Byte */ + .FREQ1 = 0xB1, /* FREQ1 Frequency Control Word, Middle Byte */ + .FREQ0 = 0x3B, /* FREQ0 Frequency Control Word, Low Byte */ + + .MDMCFG4 = 0x2D, /* MDMCFG4 Modem Configuration */ + .MDMCFG3 = 0x3B, /* MDMCFG3 Modem Configuration */ + .MDMCFG2 = 0x13, /* MDMCFG2 Modem Configuration */ + .MDMCFG1 = 0x22, /* MDMCFG1 Modem Configuration */ + .MDMCFG0 = 0xF8, /* MDMCFG0 Modem Configuration */ + + .DEVIATN = 0x62, /* DEVIATN Modem Deviation Setting */ + + .FOCCFG = 0x1D, /* FOCCFG Frequency Offset Compensation Configuration */ + + .BSCFG = 0x1C, /* BSCFG Bit Synchronization Configuration */ + + .AGCCTRL2= 0xC7, /* AGCCTRL2 AGC Control */ + .AGCCTRL1= 0x00, /* AGCCTRL1 AGC Control */ + .AGCCTRL0= 0xB0, /* AGCCTRL0 AGC Control */ + + .FREND1 = 0xB6, /* FREND1 Front End RX Configuration */ + .FREND0 = 0x10, /* FREND0 Front End TX Configuration */ + + .FSCAL3 = 0xEA, /* FSCAL3 Frequency Synthesizer Calibration */ + .FSCAL2 = 0x2A, /* FSCAL2 Frequency Synthesizer Calibration */ + .FSCAL1 = 0x00, /* FSCAL1 Frequency Synthesizer Calibration */ + .FSCAL0 = 0x1F, /* FSCAL0 Frequency Synthesizer Calibration */ + + .CHMIN = 0, /* VERIFY REGULATIONS! */ + .CHMAX = 0xFF, + + .PAMAX = 8, /* 0 means power OFF, 8 represents PA[7] */ + .PA = {0x03, 0x0E, 0x1E, 0x27, 0x39, 0x8E, 0xCD, 0xC0} +}; diff --git a/nuttx/drivers/wireless/Kconfig b/nuttx/drivers/wireless/Kconfig index ae2bf3130..9e0c09625 100644 --- a/nuttx/drivers/wireless/Kconfig +++ b/nuttx/drivers/wireless/Kconfig @@ -2,3 +2,55 @@ # For a description of the syntax of this configuration file, # see misc/tools/kconfig-language.txt. # + +config WL_CC1101 + bool "CC1101 RF transceiver support" + default n + select SPI + +config WL_NRF24L01 + bool "nRF24l01+ transceiver support" + default n + select SPI + ---help--- + This options adds driver support for the Nordic nRF24L01+ chip. + +if WL_NRF24L01 + +config WL_NRF24L01_DFLT_ADDR_WIDTH + int "Default address width" + default 5 + range 3 5 + ---help--- + Default address width to be used by the nRF24l01+ driver. + Note that this default can be changed through the driver API. + +config WL_NRF24L01_CHECK_PARAMS + bool "Check call parameters" + default y + ---help--- + This option adds some sanity check code to parameters given in the + driver API functions. If this option is disabled, parameter + values are passed 'as is' to the module hardware registers. + +config WL_NRF24L01_RXSUPPORT + bool "Support messages reception" + default y + ---help--- + If this opion is disabled the driver supports only the transmission of messages. + Reception of messages will be disabled (and corresponding functions removed). + Note: this option is intended to reduce driver code size for 'transmission + only' devices. + +if WL_NRF24L01_RXSUPPORT + +config WL_NRF24L01_RXFIFO_LEN + int "RX fifo length" + default 128 + range 34 2048 + ---help--- + Length of the software based fifo used to store content of + received messages. + +endif +endif diff --git a/nuttx/drivers/wireless/Make.defs b/nuttx/drivers/wireless/Make.defs index fa8e8acb5..3e7126acc 100644 --- a/nuttx/drivers/wireless/Make.defs +++ b/nuttx/drivers/wireless/Make.defs @@ -37,11 +37,17 @@ ifeq ($(CONFIG_WIRELESS),y) # Include wireless drivers +ifeq ($(CONFIG_WL_CC1101),y) CSRCS += cc1101.c ISM1_868MHzGFSK100kbps.c ISM2_905MHzGFSK250kbps.c +endif + +ifeq ($(CONFIG_WL_NRF24L01),y) +CSRCS += nrf24l01.c +endif -# Include wireless build support +# Include wireless devices build support -DEPPATH += --dep-path wireless$(DELIM)cc1101 -VPATH += :wireless$(DELIM)cc1101 -CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)wireless$(DELIM)cc1101} +DEPPATH += --dep-path wireless +VPATH += :wireless +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)wireless} endif diff --git a/nuttx/drivers/wireless/cc1101/cc1101.c b/nuttx/drivers/wireless/cc1101.c index 45faaecd2..b573a5342 100644 --- a/nuttx/drivers/wireless/cc1101/cc1101.c +++ b/nuttx/drivers/wireless/cc1101.c @@ -1,5 +1,5 @@ /**************************************************************************** - * drivers/wireless/cc1101/cc1101.c + * drivers/wireless/cc1101.c * * Copyright (C) 2011 Uros Platise. All rights reserved. * @@ -34,68 +34,68 @@ * ****************************************************************************/ -/** \file - * \author Uros Platise - * \brief Chipcon CC1101 Device Driver - * - * Features: +/* Features: * - Maximum data length: 61 bytes CC1101_PACKET_MAXDATALEN * - Packet length includes two additional bytes: CC1101_PACKET_MAXTOTALLEN * - Requires one GDO to trigger end-of-packets in RX and TX modes. * - Variable packet length with data payload between 1..61 bytes * (three bytes are reserved for packet length, and RSSI and LQI * appended at the end of RXFIFO after each reception) - * - Support for General Digital Outputs with overload protection + * - Support for General Digital Outputs with overload protection * (single XOSC pin is allowed, otherwise error is returned) * - Loadable RF settings, one for ISM Region 1 (Europe) and one for * ISM Region 2 (Complete America) - * + * * Todo: * - Extend max packet length up to 255 bytes or rather infinite < 4096 bytes * - Power up/down modes * - Sequencing between states or add protection for correct termination of * various different state (so that CC1101 does not block in case of improper use) - * - * \par RSSI and LQI value interpretation - * - * The LQI can be read from the LQI status register or it can be appended - * to the received packet in the RX FIFO. LQI is a metric of the current - * quality of the received signal. The LQI gives an estimate of how easily - * a received signal can be demodulated by accumulating the magnitude of - * the error between ideal constellations and the received signal over - * the 64 symbols immediately following the sync word. LQI is best used - * as a relative measurement of the link quality (a high value indicates - * a better link than what a low value does), since the value is dependent + * + * RSSI and LQI value interpretation + * + * The LQI can be read from the LQI status register or it can be appended + * to the received packet in the RX FIFO. LQI is a metric of the current + * quality of the received signal. The LQI gives an estimate of how easily + * a received signal can be demodulated by accumulating the magnitude of + * the error between ideal constellations and the received signal over + * the 64 symbols immediately following the sync word. LQI is best used + * as a relative measurement of the link quality (a high value indicates + * a better link than what a low value does), since the value is dependent * on the modulation format. - * - * To simplify: If the received modulation is FSK or GFSK, the receiver - * will measure the frequency of each "bit" and compare it with the - * expected frequency based on the channel frequency and the deviation - * and the measured frequency offset. If other modulations are used, the - * error of the modulated parameter (frequency for FSK/GFSK, phase for - * MSK, amplitude for ASK etc) will be measured against the expected + * + * To simplify: If the received modulation is FSK or GFSK, the receiver + * will measure the frequency of each "bit" and compare it with the + * expected frequency based on the channel frequency and the deviation + * and the measured frequency offset. If other modulations are used, the + * error of the modulated parameter (frequency for FSK/GFSK, phase for + * MSK, amplitude for ASK etc) will be measured against the expected * ideal value - * - * RSSI (Received Signal Strength Indicator) is a signal strength - * indication. It does not care about the "quality" or "correctness" of - * the signal. LQI does not care about the actual signal strength, but - * the signal quality often is linked to signal strength. This is because - * a strong signal is likely to be less affected by noise and thus will + * + * RSSI (Received Signal Strength Indicator) is a signal strength + * indication. It does not care about the "quality" or "correctness" of + * the signal. LQI does not care about the actual signal strength, but + * the signal quality often is linked to signal strength. This is because + * a strong signal is likely to be less affected by noise and thus will * be seen as "cleaner" or more "correct" by the receiver. - * - * There are four to five "extreme cases" that can be used to illustrate + * + * There are four to five "extreme cases" that can be used to illustrate * how RSSI and LQI work: * 1. A weak signal in the presence of noise may give low RSSI and low LQI. * 2. A weak signal in "total" absence of noise may give low RSSI and high LQI. * 3. Strong noise (usually coming from an interferer) may give high RSSI and low LQI. * 4. A strong signal without much noise may give high RSSI and high LQI. - * 5. A very strong signal that causes the receiver to saturate may give + * 5. A very strong signal that causes the receiver to saturate may give * high RSSI and low LQI. - * - * Note that both RSSI and LQI are best used as relative measurements since + * + * Note that both RSSI and LQI are best used as relative measurements since * the values are dependent on the modulation format. - **/ - + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + #include <nuttx/config.h> #include <assert.h> #include <stdlib.h> @@ -106,9 +106,8 @@ #include <nuttx/kmalloc.h> #include <nuttx/wireless/cc1101.h> - /**************************************************************************** - * Declarations + * Pre-rocessor Definitions ****************************************************************************/ #define CC1101_SPIFREQ_BURST 6500000 /* Hz, no delay */ @@ -221,9 +220,8 @@ #define CC1101_MCSM0_XOSC_FORCE_ON 0x01 - -/* - * Chip Status Byte +/* + * Chip Status Byte */ /* Bit fields in the chip status byte */ @@ -276,13 +274,13 @@ #define CC1101_VERSION_VALUE 0x04 /* - * Others ... + * Others ... */ #define CC1101_LQI_CRC_OK_BM 0x80 #define CC1101_LQI_EST_BM 0x7F - + /**************************************************************************** * Private Data Types ****************************************************************************/ @@ -290,97 +288,96 @@ #define FLAGS_RXONLY 1 /* Indicates receive operation only */ #define FLAGS_XOSCENABLED 2 /* Indicates that one pin is configured as XOSC/n */ -struct cc1101_dev_s { - const struct c1101_rfsettings_s *rfsettings; - - struct spi_dev_s * spi; - uint8_t isrpin; /* CC1101 pin used to trigger interrupts */ - uint32_t pinset; /* GPIO of the MCU */ - uint8_t flags; - uint8_t channel; - uint8_t power; +struct cc1101_dev_s +{ + const struct c1101_rfsettings_s *rfsettings; + + struct spi_dev_s * spi; + uint8_t isrpin; /* CC1101 pin used to trigger interrupts */ + uint32_t pinset; /* GPIO of the MCU */ + uint8_t flags; + uint8_t channel; + uint8_t power; }; - /**************************************************************************** * Private Functions ****************************************************************************/ - + void cc1101_access_begin(struct cc1101_dev_s * dev) { - (void)SPI_LOCK(dev->spi, true); - SPI_SELECT(dev->spi, SPIDEV_WIRELESS, true); - SPI_SETMODE(dev->spi, SPIDEV_MODE0); /* CPOL=0, CPHA=0 */ - SPI_SETBITS(dev->spi, 8); + (void)SPI_LOCK(dev->spi, true); + SPI_SELECT(dev->spi, SPIDEV_WIRELESS, true); + SPI_SETMODE(dev->spi, SPIDEV_MODE0); /* CPOL=0, CPHA=0 */ + SPI_SETBITS(dev->spi, 8); } - void cc1101_access_end(struct cc1101_dev_s * dev) { - SPI_SELECT(dev->spi, SPIDEV_WIRELESS, false); - (void)SPI_LOCK(dev->spi, false); + SPI_SELECT(dev->spi, SPIDEV_WIRELESS, false); + (void)SPI_LOCK(dev->spi, false); } - /** CC1101 Access with Range Check - * + * * \param dev CC1101 Private Structure * \param addr CC1101 Address * \param buf Pointer to buffer, either for read or write access - * \param length when >0 it denotes read access, when <0 it denotes write - * access of -length. abs(length) greater of 1 implies burst mode, - * however + * \param length when >0 it denotes read access, when <0 it denotes write + * access of -length. abs(length) greater of 1 implies burst mode, + * however * \return OK on success or errno is set. */ + int cc1101_access(struct cc1101_dev_s * dev, uint8_t addr, uint8_t *buf, int length) { - int stabyte; + int stabyte; - /* Address cannot explicitly define READ command while length WRITE. - * Also access to these cells is only permitted as one byte, eventhough - * transfer is marked as BURST! - */ + /* Address cannot explicitly define READ command while length WRITE. + * Also access to these cells is only permitted as one byte, eventhough + * transfer is marked as BURST! + */ - if ( (addr & CC1101_READ_SINGLE) && length != 1 ) + if ((addr & CC1101_READ_SINGLE) && length != 1) return ERROR; /* Prepare SPI */ - + cc1101_access_begin(dev); - + if (length>1 || length < -1) SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_BURST); else SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_SINGLE); - + /* Transfer */ - + if (length <= 0) { /* 0 length are command strobes */ - if (length < -1) + if (length < -1) addr |= CC1101_WRITE_BURST; - + stabyte = SPI_SEND(dev->spi, addr); if (length) { SPI_SNDBLOCK(dev->spi, buf, -length); - } + } } else { addr |= CC1101_READ_SINGLE; - if (length > 1) + if (length > 1) addr |= CC1101_READ_BURST; - + stabyte = SPI_SEND(dev->spi, addr); SPI_RECVBLOCK(dev->spi, buf, length); } - + cc1101_access_end(dev); return stabyte; } -/** Strobes command and returns chip status byte - * - * By default commands are send as Write. To a command, +/** Strobes command and returns chip status byte + * + * By default commands are send as Write. To a command, * CC1101_READ_SINGLE may be OR'ed to obtain the number of RX bytes * pending in RX FIFO. */ @@ -390,11 +387,11 @@ inline uint8_t cc1101_strobe(struct cc1101_dev_s * dev, uint8_t command) cc1101_access_begin(dev); SPI_SETFREQUENCY(dev->spi, CC1101_SPIFREQ_SINGLE); - + status = SPI_SEND(dev->spi, command); - + cc1101_access_end(dev); - + return status; } @@ -409,14 +406,14 @@ int cc1101_reset(struct cc1101_dev_s * dev) int cc1101_checkpart(struct cc1101_dev_s * dev) { uint8_t partnum, version; - + if (cc1101_access(dev, CC1101_PARTNUM, &partnum, 1) < 0 || - cc1101_access(dev, CC1101_VERSION, &version, 1) < 0) + cc1101_access(dev, CC1101_VERSION, &version, 1) < 0) return ERROR; - + if (partnum == CC1101_PARTNUM_VALUE && version == CC1101_VERSION_VALUE) return OK; - + return ERROR; } @@ -424,9 +421,9 @@ int cc1101_checkpart(struct cc1101_dev_s * dev) void cc1101_dumpregs(struct cc1101_dev_s * dev, uint8_t addr, uint8_t length) { uint8_t buf[0x30], i; - + cc1101_access(dev, addr, buf, length); - + printf("CC1101[%2x]: ", addr); for (i=0; i<length; i++) printf(" %2x,", buf[i]); printf("\n"); @@ -436,19 +433,19 @@ void cc1101_dumpregs(struct cc1101_dev_s * dev, uint8_t addr, uint8_t length) void cc1101_setpacketctrl(struct cc1101_dev_s * dev) { uint8_t values[3]; - + values[0] = 0; /* Rx FIFO threshold = 32, Tx FIFO threshold = 33 */ cc1101_access(dev, CC1101_FIFOTHR, values, -1); - - /* Packet length - * Limit it to 61 bytes in total: pktlen, data[61], rssi, lqi + + /* Packet length + * Limit it to 61 bytes in total: pktlen, data[61], rssi, lqi */ - - values[0] = CC1101_PACKET_MAXDATALEN; + + values[0] = CC1101_PACKET_MAXDATALEN; cc1101_access(dev, CC1101_PKTLEN, values, -1); /* Packet Control */ - + values[0] = 0x04; /* Append status: RSSI and LQI at the end of received packet */ /* TODO: CRC Auto Flash bit 0x08 ??? */ values[1] = 0x05; /* CRC in Rx and Tx Enabled: Variable Packet mode, defined by first byte */ @@ -456,18 +453,18 @@ void cc1101_setpacketctrl(struct cc1101_dev_s * dev) cc1101_access(dev, CC1101_PKTCTRL1, values, -2); /* Main Radio Control State Machine */ - + values[0] = 0x07; /* No time-out */ values[1] = 0x00; /* Clear channel if RSSI < thr && !receiving; * TX -> RX, RX -> RX: 0x3F */ values[2] = CC1101_MCSM0_VALUE; /* Calibrate on IDLE -> RX/TX, OSC Timeout = ~500 us TODO: has XOSC_FORCE_ON */ cc1101_access(dev, CC1101_MCSM2, values, -3); - + /* Wake-On Radio Control */ - + // Not used yet. - + // WOREVT1:WOREVT0 - 16-bit timeout register } @@ -476,14 +473,14 @@ void cc1101_setpacketctrl(struct cc1101_dev_s * dev) * Callbacks ****************************************************************************/ -volatile int cc1101_interrupt = 0; - -/** External line triggers this callback - * +volatile int cc1101_interrupt = 0; + +/** External line triggers this callback + * * The concept todo is: * - GPIO provides EXTI Interrupt * - It should handle EXTI Interrupts in ISR, to which chipcon can - * register a callback (and others). The ISR then foreach() calls a + * register a callback (and others). The ISR then foreach() calls a * its callback, and it is up to peripheral to find, whether the cause * of EXTI ISR was itself. **/ @@ -498,315 +495,362 @@ int cc1101_eventcb(int irq, FAR void *context) * Public Functions ****************************************************************************/ -struct cc1101_dev_s * cc1101_init(struct spi_dev_s * spi, uint8_t isrpin, +struct cc1101_dev_s * cc1101_init(struct spi_dev_s * spi, uint8_t isrpin, uint32_t pinset, const struct c1101_rfsettings_s * rfsettings) { - struct cc1101_dev_s * dev; - - ASSERT(spi); - - if ( (dev = kmalloc( sizeof(struct cc1101_dev_s) )) == NULL) { - errno = ENOMEM; - return NULL; + struct cc1101_dev_s * dev; + + ASSERT(spi); + + if ((dev = kmalloc(sizeof(struct cc1101_dev_s))) == NULL) + { + errno = ENOMEM; + return NULL; } - - dev->rfsettings = rfsettings; - dev->spi = spi; - dev->isrpin = isrpin; - dev->pinset = pinset; - dev->flags = 0; - dev->channel = rfsettings->CHMIN; - dev->power = rfsettings->PAMAX; - - /* Reset chip, check status bytes */ - - if ( cc1101_reset(dev) < 0 ) { - kfree(dev); - errno = EFAULT; - return NULL; + + dev->rfsettings = rfsettings; + dev->spi = spi; + dev->isrpin = isrpin; + dev->pinset = pinset; + dev->flags = 0; + dev->channel = rfsettings->CHMIN; + dev->power = rfsettings->PAMAX; + + /* Reset chip, check status bytes */ + + if (cc1101_reset(dev) < 0) + { + kfree(dev); + errno = EFAULT; + return NULL; } - /* Check part compatibility */ - - if ( cc1101_checkpart(dev) < 0 ) { - kfree(dev); - errno = ENODEV; - return NULL; + /* Check part compatibility */ + + if (cc1101_checkpart(dev) < 0) + { + kfree(dev); + errno = ENODEV; + return NULL; } - - /* Configure CC1101: - * - disable GDOx for best performance - * - load RF - * - and packet control - */ - - cc1101_setgdo(dev, CC1101_PIN_GDO0, CC1101_GDO_HIZ); - cc1101_setgdo(dev, CC1101_PIN_GDO1, CC1101_GDO_HIZ); - cc1101_setgdo(dev, CC1101_PIN_GDO2, CC1101_GDO_HIZ); - cc1101_setrf(dev, rfsettings); - cc1101_setpacketctrl(dev); - - /* Set the ISR to be triggerred on falling edge of the: - * - * 6 (0x06) Asserts when sync word has been sent / received, and - * de-asserts at the end of the packet. In RX, the pin will de-assert - * when the optional address check fails or the RX FIFO overflows. - * In TX the pin will de-assert if the TX FIFO underflows. - */ - cc1101_setgdo(dev, dev->isrpin, CC1101_GDO_SYNC); - - /* Bind to external interrupt line */ + /* Configure CC1101: + * - disable GDOx for best performance + * - load RF + * - and packet control + */ - // depends on STM32: TODO: Make that config within pinset and - // provide general gpio interface - //stm32_gpiosetevent(pinset, false, true, true, cc1101_eventcb); + cc1101_setgdo(dev, CC1101_PIN_GDO0, CC1101_GDO_HIZ); + cc1101_setgdo(dev, CC1101_PIN_GDO1, CC1101_GDO_HIZ); + cc1101_setgdo(dev, CC1101_PIN_GDO2, CC1101_GDO_HIZ); + cc1101_setrf(dev, rfsettings); + cc1101_setpacketctrl(dev); - return dev; -} + /* Set the ISR to be triggerred on falling edge of the: + * + * 6 (0x06) Asserts when sync word has been sent / received, and + * de-asserts at the end of the packet. In RX, the pin will de-assert + * when the optional address check fails or the RX FIFO overflows. + * In TX the pin will de-assert if the TX FIFO underflows. + */ + + cc1101_setgdo(dev, dev->isrpin, CC1101_GDO_SYNC); + + /* Bind to external interrupt line */ + /* depends on STM32: TODO: Make that config within pinset and + * provide general gpio interface + * stm32_gpiosetevent(pinset, false, true, true, cc1101_eventcb); + */ + + return dev; +} int cc1101_deinit(struct cc1101_dev_s * dev) { - ASSERT(dev); - - /* Release interrupt */ - //stm32_gpiosetevent(pinset, false, false, false, NULL); - - /* Power down chip */ - cc1101_powerdown(dev); - - /* Release external interrupt line */ - kfree(dev); - - return 0; -} + ASSERT(dev); + + /* Release interrupt */ + /* stm32_gpiosetevent(pinset, false, false, false, NULL); */ + + /* Power down chip */ + cc1101_powerdown(dev); + + /* Release external interrupt line */ + + kfree(dev); + return 0; +} int cc1101_powerup(struct cc1101_dev_s * dev) { - ASSERT(dev); - return 0; + ASSERT(dev); + return 0; } - int cc1101_powerdown(struct cc1101_dev_s * dev) { - ASSERT(dev); - return 0; + ASSERT(dev); + return 0; } - int cc1101_setgdo(struct cc1101_dev_s * dev, uint8_t pin, uint8_t function) { - ASSERT(dev); - ASSERT(pin <= CC1101_IOCFG0); - - if (function >= CC1101_GDO_CLK_XOSC1) { - - /* Only one pin can be enabled at a time as XOSC/n */ - - if (dev->flags & FLAGS_XOSCENABLED) return -EPERM; - - /* Force XOSC to stay active even in sleep mode */ - - int value = CC1101_MCSM0_VALUE | CC1101_MCSM0_XOSC_FORCE_ON; - cc1101_access(dev, CC1101_MCSM0, &value, -1); - - dev->flags |= FLAGS_XOSCENABLED; + ASSERT(dev); + ASSERT(pin <= CC1101_IOCFG0); + + if (function >= CC1101_GDO_CLK_XOSC1) + { + /* Only one pin can be enabled at a time as XOSC/n */ + + if (dev->flags & FLAGS_XOSCENABLED) + { + return -EPERM; + } + + /* Force XOSC to stay active even in sleep mode */ + + int value = CC1101_MCSM0_VALUE | CC1101_MCSM0_XOSC_FORCE_ON; + cc1101_access(dev, CC1101_MCSM0, &value, -1); + + dev->flags |= FLAGS_XOSCENABLED; } - else if (dev->flags & FLAGS_XOSCENABLED) { - - /* Disable XOSC in sleep mode */ - - int value = CC1101_MCSM0_VALUE; - cc1101_access(dev, CC1101_MCSM0, &value, -1); - - dev->flags &= ~FLAGS_XOSCENABLED; + else if (dev->flags & FLAGS_XOSCENABLED) + { + /* Disable XOSC in sleep mode */ + + int value = CC1101_MCSM0_VALUE; + cc1101_access(dev, CC1101_MCSM0, &value, -1); + + dev->flags &= ~FLAGS_XOSCENABLED; } - - return cc1101_access(dev, pin, &function, -1); -} + return cc1101_access(dev, pin, &function, -1); +} int cc1101_setrf(struct cc1101_dev_s * dev, const struct c1101_rfsettings_s *settings) { - ASSERT(dev); - ASSERT(settings); - - if (cc1101_access(dev, CC1101_FSCTRL1, &settings->FSCTRL1, -11) < 0) return ERROR; - if (cc1101_access(dev, CC1101_FOCCFG, &settings->FOCCFG, -5) < 0) return ERROR; - if (cc1101_access(dev, CC1101_FREND1, &settings->FREND1, -6) < 0) return ERROR; - - /* Load Power Table */ - - if (cc1101_access(dev, CC1101_PATABLE, settings->PA, -8) < 0) return ERROR; - - /* If channel is out of valid range, mark that. Limit power. - * We are not allowed to send any data, but are allowed to listen - * and receive. - */ + ASSERT(dev); + ASSERT(settings); + + if (cc1101_access(dev, CC1101_FSCTRL1, &settings->FSCTRL1, -11) < 0) + { + return ERROR; + } + + if (cc1101_access(dev, CC1101_FOCCFG, &settings->FOCCFG, -5) < 0) + { + return ERROR; + } + + if (cc1101_access(dev, CC1101_FREND1, &settings->FREND1, -6) < 0) + { + return ERROR; + } + + /* Load Power Table */ + + if (cc1101_access(dev, CC1101_PATABLE, settings->PA, -8) < 0) + { + return ERROR; + } + + /* If channel is out of valid range, mark that. Limit power. + * We are not allowed to send any data, but are allowed to listen + * and receive. + */ + + cc1101_setchannel(dev, dev->channel); + cc1101_setpower(dev, dev->power); - cc1101_setchannel(dev, dev->channel); - cc1101_setpower(dev, dev->power); - return OK; } - int cc1101_setchannel(struct cc1101_dev_s * dev, uint8_t channel) { - ASSERT(dev); - - /* Store localy in further checks */ - - dev->channel = channel; - - /* If channel is out of valid, we are allowed to listen and receive only */ - - if (channel < dev->rfsettings->CHMIN || channel > dev->rfsettings->CHMAX) - dev->flags |= FLAGS_RXONLY; - else dev->flags &= ~FLAGS_RXONLY; - - cc1101_access(dev, CC1101_CHANNR, &dev->channel, -1); - - return dev->flags & FLAGS_RXONLY; -} + ASSERT(dev); + + /* Store localy in further checks */ + + dev->channel = channel; + /* If channel is out of valid, we are allowed to listen and receive only */ + + if (channel < dev->rfsettings->CHMIN || channel > dev->rfsettings->CHMAX) + { + dev->flags |= FLAGS_RXONLY; + } + else + { + dev->flags &= ~FLAGS_RXONLY; + } + + cc1101_access(dev, CC1101_CHANNR, &dev->channel, -1); + return dev->flags & FLAGS_RXONLY; +} uint8_t cc1101_setpower(struct cc1101_dev_s * dev, uint8_t power) { - ASSERT(dev); - - if (power > dev->rfsettings->PAMAX) - power = dev->rfsettings->PAMAX; - - dev->power = power; - - if (power == 0) { - dev->flags |= FLAGS_RXONLY; - return 0; + ASSERT(dev); + + if (power > dev->rfsettings->PAMAX) + { + power = dev->rfsettings->PAMAX; + } + + dev->power = power; + + if (power == 0) + { + dev->flags |= FLAGS_RXONLY; + return 0; + } + else + { + dev->flags &= ~FLAGS_RXONLY; } - else dev->flags &= ~FLAGS_RXONLY; - - /* Add remaining part from RF table (to get rid of readback) */ - - power--; - power |= dev->rfsettings->FREND0; - - /* On error, report that as zero power */ - - if (cc1101_access(dev, CC1101_FREND0, &power, -1) < 0) - dev->power = 0; - + + /* Add remaining part from RF table (to get rid of readback) */ + + power--; + power |= dev->rfsettings->FREND0; + + /* On error, report that as zero power */ + + if (cc1101_access(dev, CC1101_FREND0, &power, -1) < 0) + { + dev->power = 0; + } + return dev->power; } - int cc1101_calcRSSIdBm(int rssi) { - if (rssi >= 128) rssi -= 256; - return (rssi >> 1) - 74; -} + if (rssi >= 128) + { + rssi -= 256; + } + return (rssi >> 1) - 74; +} int cc1101_receive(struct cc1101_dev_s * dev) { - ASSERT(dev); + ASSERT(dev); - /* \todo Wait for IDLE before going into another state? */ + /* \todo Wait for IDLE before going into another state? */ - cc1101_interrupt = 0; - - cc1101_strobe(dev, CC1101_SRX | CC1101_READ_SINGLE); - - return 0; -} + cc1101_interrupt = 0; + cc1101_strobe(dev, CC1101_SRX | CC1101_READ_SINGLE); + + return 0; +} int cc1101_read(struct cc1101_dev_s * dev, uint8_t * buf, size_t size) { - ASSERT(dev); - - if (buf==NULL) { - if (size==0) return 64; - // else received packet size - return 0; + ASSERT(dev); + + if (buf==NULL) + { + if (size==0) + { + return 64; + } + + /* else received packet size */ + + return 0; + } + + if (cc1101_interrupt == 0) + { + return 0; } - - if (cc1101_interrupt == 0) return 0; - - int status = cc1101_strobe(dev, CC1101_SNOP | CC1101_READ_SINGLE); - - if (status & CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM && - (status & CC1101_STATE_MASK) == CC1101_STATE_IDLE) { - - uint8_t nbytes; - - cc1101_access(dev, CC1101_RXFIFO, &nbytes, 1); - - nbytes += 2; /* RSSI and LQI */ - - cc1101_access(dev, CC1101_RXFIFO, buf, (nbytes > size) ? size : nbytes); - - /* Flush remaining bytes, if there is no room to receive - * or if there is a BAD CRC - */ - - if (nbytes > size || (nbytes <= size && !(buf[nbytes-1]&0x80)) ) { - printf("Flushing RX FIFO\n"); - cc1101_strobe(dev, CC1101_SFRX); + + int status = cc1101_strobe(dev, CC1101_SNOP | CC1101_READ_SINGLE); + + if (status & CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM && + (status & CC1101_STATE_MASK) == CC1101_STATE_IDLE) + { + uint8_t nbytes; + + cc1101_access(dev, CC1101_RXFIFO, &nbytes, 1); + + nbytes += 2; /* RSSI and LQI */ + + cc1101_access(dev, CC1101_RXFIFO, buf, (nbytes > size) ? size : nbytes); + + /* Flush remaining bytes, if there is no room to receive + * or if there is a BAD CRC + */ + + if (nbytes > size || (nbytes <= size && !(buf[nbytes-1]&0x80))) + { + printf("Flushing RX FIFO\n"); + cc1101_strobe(dev, CC1101_SFRX); } - - return nbytes; + + return nbytes; } - - return 0; -} + return 0; +} int cc1101_write(struct cc1101_dev_s * dev, const uint8_t * buf, size_t size) { - uint8_t packetlen; - - ASSERT(dev); - ASSERT(buf); - - if (dev->flags & FLAGS_RXONLY) return -EPERM; - - /* Present limit */ - if (size > CC1101_PACKET_MAXDATALEN) - packetlen = CC1101_PACKET_MAXDATALEN; - else packetlen = size; - - cc1101_access(dev, CC1101_TXFIFO, &packetlen, -1); - cc1101_access(dev, CC1101_TXFIFO, buf, -size); - - return 0; -} + uint8_t packetlen; + + ASSERT(dev); + ASSERT(buf); + + if (dev->flags & FLAGS_RXONLY) + { + return -EPERM; + } + /* Present limit */ + + if (size > CC1101_PACKET_MAXDATALEN) + { + packetlen = CC1101_PACKET_MAXDATALEN; + } + else + { + packetlen = size; + } + + cc1101_access(dev, CC1101_TXFIFO, &packetlen, -1); + cc1101_access(dev, CC1101_TXFIFO, buf, -size); + + return 0; +} int cc1101_send(struct cc1101_dev_s * dev) { - ASSERT(dev); - - if (dev->flags & FLAGS_RXONLY) return -EPERM; - - cc1101_interrupt = 0; - - cc1101_strobe(dev, CC1101_STX); - - /* wait until send, going to IDLE */ - - while( cc1101_interrupt == 0 ); - - return 0; -} + ASSERT(dev); + + if (dev->flags & FLAGS_RXONLY) + { + return -EPERM; + } + + cc1101_interrupt = 0; + cc1101_strobe(dev, CC1101_STX); + + /* wait until send, going to IDLE */ + + while (cc1101_interrupt == 0); + + return 0; +} int cc1101_idle(struct cc1101_dev_s * dev) { - ASSERT(dev); - cc1101_strobe(dev, CC1101_SIDLE); - return 0; + ASSERT(dev); + cc1101_strobe(dev, CC1101_SIDLE); + return 0; } diff --git a/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c b/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c deleted file mode 100644 index 5c4c58ab2..000000000 --- a/nuttx/drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** - * drivers/wireless/cc1101/ISM1_868MHzGFSK100kbps.c - * - * Copyright (C) 2011 Uros Platise. All rights reserved. - * Copyright (C) 2011 Ales Verbic. All rights reserved. - * - * Authors: Uros Platise <uros.platise@isotel.eu> - * Ales Verbic <ales.verbic@isotel.eu> - * - * 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. - * - ****************************************************************************/ - -#include <nuttx/wireless/cc1101.h> - -/** Settings for 868 MHz, GFSK at 100kbps - * - * ISM Region 1 (Europe) only, Band 868–870 MHz - * - * Frequency ERP Duty Cycle Bandwidth Remarks - * 868 – 868.6 MHz +14 dBm < 1% No limits - * 868.7 – 869.2 MHz +14 dBm < 0.1% No limits - * 869.3 – 869.4 MHz +10 dBm No limits < 25 kHz Appropriate access protocol required - * 869.4 – 869.65 MHz +27 dBm < 10% < 25 kHz Channels may be combined to one high speed channel - * 869.7 -870 MHz +7 dBm No limits No limits - * - * Deviation = 46.142578 - * Base frequency = 867.999985 - * Carrier frequency = 867.999985 - * Channel number = 0 - * Carrier frequency = 867.999985 - * Modulated = true - * Modulation format = GFSK - * Manchester enable = false - * Sync word qualifier mode = 30/32 sync word bits detected - * Preamble count = 4 - * Channel spacing = 199.813843 - * Carrier frequency = 867.999985 - * Data rate = 99.9069 - * RX filter BW = 210.937500 - * Data format = Normal mode - * Length config = Fixed packet length mode. Length configured in PKTLEN register - * CRC enable = true - * Packet length = 62 - * Device address = 00 - * Address config = NO Address check, no broadcast - * CRC autoflush = true - * PA ramping = false - * TX power = 0 - */ -const struct c1101_rfsettings_s cc1101_rfsettings_ISM1_868MHzGFSK100kbps = { - .FSCTRL1 = 0x08, // FSCTRL1 Frequency Synthesizer Control - .FSCTRL0 = 0x00, // FSCTRL0 Frequency Synthesizer Control - - .FREQ2 = 0x20, // FREQ2 Frequency Control Word, High Byte - .FREQ1 = 0x25, // FREQ1 Frequency Control Word, Middle Byte - .FREQ0 = 0xED, // FREQ0 Frequency Control Word, Low Byte - - .MDMCFG4 = 0x8B, // MDMCFG4 Modem Configuration - .MDMCFG3 = 0xE5, // MDMCFG3 Modem Configuration - .MDMCFG2 = 0x13, // MDMCFG2 Modem Configuration - .MDMCFG1 = 0x22, // MDMCFG1 Modem Configuration - .MDMCFG0 = 0xE5, // MDMCFG0 Modem Configuration - - .DEVIATN = 0x46, // DEVIATN Modem Deviation Setting - - .FOCCFG = 0x1D, // FOCCFG Frequency Offset Compensation Configuration - - .BSCFG = 0x1C, // BSCFG Bit Synchronization Configuration - - .AGCCTRL2= 0xC7, // AGCCTRL2 AGC Control - .AGCCTRL1= 0x00, // AGCCTRL1 AGC Control - .AGCCTRL0= 0xB2, // AGCCTRL0 AGC Control - - .FREND1 = 0xB6, // FREND1 Front End RX Configuration - .FREND0 = 0x10, // FREND0 Front End TX Configuration - - .FSCAL3 = 0xEA, // FSCAL3 Frequency Synthesizer Calibration - .FSCAL2 = 0x2A, // FSCAL2 Frequency Synthesizer Calibration - .FSCAL1 = 0x00, // FSCAL1 Frequency Synthesizer Calibration - .FSCAL0 = 0x1F, // FSCAL0 Frequency Synthesizer Calibration - - .CHMIN = 0, // Fix at 9th channel: 869.80 MHz +- 100 kHz RF Bandwidth - .CHMAX = 9, // single channel - - .PAMAX = 8, // 0 means power OFF, 8 represents PA[7] - .PA = {0x03, 0x0F, 0x1E, 0x27, 0x67, 0x50, 0x81, 0xC2} -}; diff --git a/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c b/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c deleted file mode 100644 index e5655bed6..000000000 --- a/nuttx/drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** - * drivers/wireless/cc1101/ISM2_905MHzGFSK250kbps.c - * - * Copyright (C) 2011 Uros Platise. All rights reserved. - * Copyright (C) 2011 Ales Verbic. All rights reserved. - * - * Authors: Uros Platise <uros.platise@isotel.eu> - * Ales Verbic <ales.verbic@isotel.eu> - * - * 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. - * - ****************************************************************************/ - -#include <nuttx/wireless/cc1101.h> - -/** Settings for 905 MHz, GFSK at 250kbps - * - * ISM Region 2 (America) only, Band 902–928 MHz - * - * Cordless phones 1 W - * Microwave ovens 750 W - * Industrial heaters 100 kW - * Military radar 1000 kW - * - * Deviation = 126.953125 - * Base frequency = 901.999969 - * Carrier frequency = 905.998993 - * Channel number = 20 - * Carrier frequency = 905.998993 - * Modulated = true - * Modulation format = GFSK - * Manchester enable = false - * Sync word qualifier mode = 30/32 sync word bits detected - * Preamble count = 4 - * Channel spacing = 199.951172 - * Carrier frequency = 905.998993 - * Data rate = 249.939 - * RX filter BW = 541.666667 - * Data format = Normal mode - * Length config = Variable packet length mode. Packet length configured by the first byte after sync word - * CRC enable = true - * Packet length = 61 - * Device address = 0 - * Address config = No address check - * CRC autoflush = false - * PA ramping = false - * TX power = 0 - */ -const struct c1101_rfsettings_s cc1101_rfsettings_ISM2_905MHzGFSK250kbps = { - .FSCTRL1 = 0x0C, // FSCTRL1 Frequency Synthesizer Control - .FSCTRL0 = 0x00, // FSCTRL0 Frequency Synthesizer Control - - .FREQ2 = 0x22, // FREQ2 Frequency Control Word, High Byte - .FREQ1 = 0xB1, // FREQ1 Frequency Control Word, Middle Byte - .FREQ0 = 0x3B, // FREQ0 Frequency Control Word, Low Byte - - .MDMCFG4 = 0x2D, // MDMCFG4 Modem Configuration - .MDMCFG3 = 0x3B, // MDMCFG3 Modem Configuration - .MDMCFG2 = 0x13, // MDMCFG2 Modem Configuration - .MDMCFG1 = 0x22, // MDMCFG1 Modem Configuration - .MDMCFG0 = 0xF8, // MDMCFG0 Modem Configuration - - .DEVIATN = 0x62, // DEVIATN Modem Deviation Setting - - .FOCCFG = 0x1D, // FOCCFG Frequency Offset Compensation Configuration - - .BSCFG = 0x1C, // BSCFG Bit Synchronization Configuration - - .AGCCTRL2= 0xC7, // AGCCTRL2 AGC Control - .AGCCTRL1= 0x00, // AGCCTRL1 AGC Control - .AGCCTRL0= 0xB0, // AGCCTRL0 AGC Control - - .FREND1 = 0xB6, // FREND1 Front End RX Configuration - .FREND0 = 0x10, // FREND0 Front End TX Configuration - - .FSCAL3 = 0xEA, // FSCAL3 Frequency Synthesizer Calibration - .FSCAL2 = 0x2A, // FSCAL2 Frequency Synthesizer Calibration - .FSCAL1 = 0x00, // FSCAL1 Frequency Synthesizer Calibration - .FSCAL0 = 0x1F, // FSCAL0 Frequency Synthesizer Calibration - - .CHMIN = 0, // VERIFY REGULATIONS! - .CHMAX = 0xFF, - - .PAMAX = 8, // 0 means power OFF, 8 represents PA[7] - .PA = {0x03, 0x0E, 0x1E, 0x27, 0x39, 0x8E, 0xCD, 0xC0} -}; diff --git a/nuttx/drivers/wireless/cc1101/Kconfig b/nuttx/drivers/wireless/cc1101/Kconfig deleted file mode 100644 index ae2bf3130..000000000 --- a/nuttx/drivers/wireless/cc1101/Kconfig +++ /dev/null @@ -1,4 +0,0 @@ -# -# For a description of the syntax of this configuration file, -# see misc/tools/kconfig-language.txt. -# diff --git a/nuttx/drivers/wireless/nrf24l01.c b/nuttx/drivers/wireless/nrf24l01.c new file mode 100644 index 000000000..abb71e725 --- /dev/null +++ b/nuttx/drivers/wireless/nrf24l01.c @@ -0,0 +1,1772 @@ +/**************************************************************************** + * 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 + ****************************************************************************/ + +#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 + +#ifdef CONFIG_WL_NRF24L01_CHECK_PARAMS +# define CHECK_ARGS(cond) do { if (!(cond)) return -EINVAL; } while(0) +#else +# define CHECK_ARGS(cond) +#endif + +/* Default SPI bus frequency (in Hz) */ +#define NRF24L01_SPIFREQ 9000000 /* Can go up to 10 Mbs according to datasheet */ + +/* power-down -> standby transition timing (in us). Note: this value is probably larger than required. */ +#define NRF24L01_TPD2STBY_DELAY 4500 + +#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" + +/**************************************************************************** + * Private Data Types + ****************************************************************************/ + +typedef enum +{ + MODE_READ, + MODE_WRITE +} nrf24l01_access_mode_t; + +#define FL_AA_ENABLED (1 << 0) + +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; + +#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 */ + +#ifdef CONFIG_SPI_OWNBUS +static inline void nrf24l01_configspi(FAR struct spi_dev_s *spi); +# define nrf24l01_lock(spi) +# define nrf24l01_unlock(spi) +#else +# define nrf24l01_configspi(spi); +static void nrf24l01_lock(FAR struct spi_dev_s *spi); +static void nrf24l01_unlock(FAR struct spi_dev_s *spi); +#endif + +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, + 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); + +static inline int nrf24l01_attachirq(FAR struct nrf24l01_dev_s *dev, xcpt_t isr); + +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 + +void fifoput(struct nrf24l01_dev_s *dev, uint8_t pipeno, uint8_t *buffer, uint8_t buflen); + +uint8_t fifoget(struct nrf24l01_dev_s *dev, uint8_t *buffer, uint8_t buflen, uint8_t *pipeno); + +static void nrf24l01_worker(FAR void *arg); + +#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 FAR struct nrf24l01_dev_s *g_nrf24l01dev; + +static const struct file_operations nrf24l01_fops = +{ + .open = nrf24l01_open, /* open */ + .close = nrf24l01_close, /* close */ + .read = nrf24l01_read, /* read */ + .write = nrf24l01_write, /* write */ + .seek = NULL, /* seek */ + .ioctl = nrf24l01_ioctl, /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + .poll = nrf24l01_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifndef CONFIG_SPI_OWNBUS +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, true); + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + SPI_SETFREQUENCY(spi, NRF24L01_SPIFREQ); + SPI_SELECT(spi, SPIDEV_WIRELESS, false); +} +#endif + +/**************************************************************************** + * Function: nrf24l01_unlock + * + * Description: + * If we are sharing the SPI bus with other devices (CONFIG_SPI_OWNBUS + * undefined) then we need to un-lock the SPI bus for each transfer, + * possibly losing the current configuration. + * + * Parameters: + * spi - Reference to the SPI driver structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_OWNBUS +static void nrf24l01_unlock(FAR struct spi_dev_s *spi) +{ + /* Relinquish the SPI bus. */ + + (void)SPI_LOCK(spi, false); +} +#endif + +/**************************************************************************** + * Function: nrf24l01_configspi + * + * Description: + * Configure the SPI for use with the NRF24L01. This function should be + * called once during touchscreen initialization to configure the SPI + * bus. Note that if CONFIG_SPI_OWNBUS is not defined, then this function + * does nothing. + * + * Parameters: + * spi - Reference to the SPI driver structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_OWNBUS +static inline void nrf24l01_configspi(FAR struct spi_dev_s *spi) +{ + /* Configure SPI for the NRF24L01 module. + * As we own the SPI bus this method is called just once. + */ + + SPI_SELECT(spi, SPIDEV_WIRELESS, true); // Useful ? + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + SPI_SETFREQUENCY(spi, NRF24L01_SPIFREQ); + SPI_SELECT(spi, SPIDEV_WIRELESS, false); +} +#endif + +static inline void nrf24l01_select(struct nrf24l01_dev_s * dev) +{ + SPI_SELECT(dev->spi, SPIDEV_WIRELESS, true); +} + +static inline void nrf24l01_deselect(struct nrf24l01_dev_s * dev) +{ + SPI_SELECT(dev->spi, SPIDEV_WIRELESS, false); +} + +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; +} + +static inline uint8_t nrf24l01_flush_rx(struct nrf24l01_dev_s *dev) +{ + return nrf24l01_access(dev, MODE_WRITE, NRF24L01_FLUSH_RX, NULL, 0); +} + +static inline uint8_t nrf24l01_flush_tx(struct nrf24l01_dev_s *dev) +{ + return nrf24l01_access(dev, MODE_WRITE, NRF24L01_FLUSH_TX, NULL, 0); +} + +/* Read register from nrf24l01 */ + +static inline uint8_t nrf24l01_readreg(struct nrf24l01_dev_s *dev, uint8_t reg, + uint8_t *value, int len) +{ + return nrf24l01_access(dev, MODE_READ, reg | NRF24L01_R_REGISTER, value, len); +} + +/* 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; +} + +/* 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); +} + +/* Write single byte value to a register of nrf24l01 */ + +static inline void nrf24l01_writeregbyte(struct nrf24l01_dev_s *dev, uint8_t reg, + uint8_t value) +{ + nrf24l01_writereg(dev, reg, &value, 1); +} + +static uint8_t nrf24l01_setregbit(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; +} + +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + +/* RX fifo mgt */ + +void fifoput(struct nrf24l01_dev_s *dev, uint8_t pipeno, 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); +} + +uint8_t fifoget(struct nrf24l01_dev_s *dev, uint8_t *buffer, uint8_t buflen, uint8_t *pipeno) +{ + uint8_t pktlen; + uint8_t i; + + sem_wait(&dev->sem_fifo); + + ASSERT(dev->fifo_len > 0); + + 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); + + sem_post(&dev->sem_fifo); + return pktlen; +} + +#endif + +static int nrf24l01_irqhandler(int irq, FAR void *context) +{ + FAR struct nrf24l01_dev_s *dev = g_nrf24l01dev; + + wllvdbg("*IRQ*"); + +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + + /* If RX is enabled we delegate the actual work to bottom-half handler */ + + work_queue(HPWORK, &g_nrf24l01dev->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; +} + +/* Configure IRQ pin (falling edge) */ + +static inline int nrf24l01_attachirq(FAR struct nrf24l01_dev_s *dev, xcpt_t isr) +{ + return dev->config->irqattach(isr); +} + +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; + } +} + +#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); + + wdbg("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; + + 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); + } + + /* Get payload content */ + + nrf24l01_access(dev, MODE_READ, NRF24L01_R_RX_PAYLOAD, buf, pktlen); + + fifoput(dev, pipeno, buf, pktlen); + sem_post(&dev->sem_rx); /* Wake-up any thread waiting in recv */ + + status = nrf24l01_readreg(dev, NRF24L01_FIFO_STATUS, &fifo_status, 1); + + wdbg("FIFO_STATUS=%02x\n", fifo_status); + wdbg("STATUS=%02x\n", status); + } + while (!(fifo_status | NRF24L01_RX_EMPTY)); + + /* Clear interrupt sources */ + + nrf24l01_writeregbyte(dev, NRF24L01_STATUS, NRF24L01_RX_DR); + + /* Restore CE */ + + nrf24l01_chipenable(dev, ce); + +#ifndef CONFIG_DISABLE_POLL + if (dev->pfd) + { + dev->pfd->revents |= POLLIN; /* Data available for input */ + + wvdbg("Wake up polled fd"); + sem_post(dev->pfd->sem); + } +#endif + } + + if (status & (NRF24L01_TX_DS | NRF24L01_MAX_RT)) + { + /* The actual work is done in the send function */ + + sem_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 + +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; +} + +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); + + /* Write payload */ + + nrf24l01_access(dev, MODE_WRITE, NRF24L01_W_TX_PAYLOAD, (FAR uint8_t *)data, datalen); + + /* Enable CE to start transmission */ + + nrf24l01_chipenable(dev, true); + + /* Free the SPI bus during the IRQ wait */ + + nrf24l01_unlock(dev->spi); + + /* Wait for IRQ (TX_DS or MAX_RT) */ + + while (sem_wait(&dev->sem_tx) != 0) + { + /* Note that we really need to wait here, as the interrupt source + * (either TX_DS in case of success, or MAX_RT for failure) needs to be cleared. + */ + + DEBUGASSERT(errno == EINTR); + } + + /* Re-acquire the SPI bus */ + + nrf24l01_lock(dev->spi); + + 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; + + wvdbg("Transmission OK (lastxmitcount=%d)\n", dev->lastxmitcount); + } + else if (status & NRF24L01_MAX_RT) + { + wvdbg("MAX_RT!\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... */ + + wdbg("No TX_DS nor MAX_RT bit set in STATUS reg!\n"); + result = -EIO; + } + + /* Clear interrupt sources */ + + nrf24l01_writeregbyte(dev, NRF24L01_STATUS, NRF24L01_TX_DS | NRF24L01_MAX_RT); + + /* Restore state */ + + nrf24l01_tostate(dev, prevstate); + return result; +} + +/* POSIX API */ + +static int nrf24l01_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct nrf24l01_dev_s *dev; + int result; + + wvdbg("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; +} + +static int nrf24l01_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct nrf24l01_dev_s *dev; + + wvdbg("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; +} + +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 +} + +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; +} + +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; + + wvdbg("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; +} + +#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; + + wvdbg("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; + } + else /* Tear it down */ + { + dev->pfd = NULL; + } + +errout: + sem_post(&dev->devsem); + return result; +#endif +} + +#endif + +static int nrf24l01_unregister(FAR struct nrf24l01_dev_s *dev) +{ + CHECK_ARGS(dev); + + /* Release IRQ */ + + nrf24l01_attachirq(dev, NULL); + + g_nrf24l01dev = NULL; + + /* Free memory */ +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + kfree(dev->rx_fifo); +#endif + kfree(dev); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +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 = kmalloc(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); + +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + if ((rx_fifo = kmalloc(CONFIG_WL_NRF24L01_RXFIFO_LEN)) == NULL) + { + kfree(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); +#endif + + /* Set the global reference */ + + g_nrf24l01dev = dev; + + /* Configure IRQ pin (falling edge) */ + + nrf24l01_attachirq(dev, nrf24l01_irqhandler); + + /* Register the device as an input device */ + + ivdbg("Registering " DEV_NAME "\n"); + + result = register_driver(DEV_NAME, &nrf24l01_fops, 0666, dev); + if (result < 0) + { + wdbg("register_driver() failed: %d\n", result); + nrf24l01_unregister(dev); + } + + return result; +} + +FAR struct nrf24l01_dev_s * nrf24l01_getinstance(void) +{ + return g_nrf24l01dev; +} + +/* (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 now (if we own the bus) */ + + 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; +} + +int nrf24l01_setpipeconfig(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno, + FAR const nrf24l01_pipecfg_t *pipecfg) +{ + bool dynlength; + bool en_aa; + + 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 */ + + int addrlen = (pipeno <= 1) ? dev->addrlen : 1; /* Pipe 0 & 1 are the only ones to have a full length address */ + 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; +} + +int nrf24l01_getpipeconfig(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno, + FAR nrf24l01_pipecfg_t *pipecfg) +{ + bool dynlength; + + CHECK_ARGS(dev && pipecfg && pipeno < NRF24L01_PIPE_COUNT); + + nrf24l01_lock(dev->spi); + + /* Get pipe address */ + + int addrlen = (pipeno <= 1) ? dev->addrlen : 1; /* Pipe 0 & 1 are the only ones to have a full length address */ + 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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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]; +} + +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; +} + +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 = NRF24L01_MIN_FREQ - freq; + nrf24l01_lock(dev->spi); + nrf24l01_writeregbyte(dev, NRF24L01_RF_CH, value); + nrf24l01_unlock(dev->spi); + return OK; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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))) + { + wdbg("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); + wdbg("Pipe #0 default addr restored\n"); + } + + nrf24l01_unlock(dev->spi); + return result; +} + +int nrf24l01_lastxmitcount(FAR struct nrf24l01_dev_s *dev) +{ + return dev->lastxmitcount; +} + +#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 + +#ifdef NRF24L01_DEBUG + +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'; +} + +void nrf24l01_dumpregs(struct nrf24l01_dev_s *dev) +{ + uint8_t addr[NRF24L01_MAX_ADDR_LEN]; + char addrstr[NRF24L01_MAX_ADDR_LEN * 2 +1]; + + syslog("CONFIG: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_CONFIG)); + syslog("EN_AA: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_EN_AA)); + syslog("EN_RXADDR: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_EN_RXADDR)); + syslog("SETUP_AW: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_SETUP_AW)); + + syslog("SETUP_RETR:%02x\n", nrf24l01_readregbyte(dev, NRF24L01_SETUP_RETR)); + syslog("RF_CH: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RF_CH)); + syslog("RF_SETUP: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RF_SETUP)); + syslog("STATUS: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_STATUS)); + syslog("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("TX_ADDR: %s\n", addrstr); + + syslog("CD: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_CD)); + syslog("RX_PW_P0: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P0)); + syslog("RX_PW_P1: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P1)); + syslog("RX_PW_P2: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P2)); + syslog("RX_PW_P3: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P3)); + syslog("RX_PW_P4: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P4)); + syslog("RX_PW_P5: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_RX_PW_P5)); + + syslog("FIFO_STAT: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_FIFO_STATUS)); + syslog("DYNPD: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_DYNPD)); + syslog("FEATURE: %02x\n", nrf24l01_readregbyte(dev, NRF24L01_FEATURE)); +} + +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT +void nrf24l01_dumprxfifo(struct nrf24l01_dev_s *dev) +{ + syslog("bytes count: %d\n", dev->fifo_len); + syslog("next read: %d, next write: %d\n", dev->nxt_read, dev-> nxt_write); +} +#endif + +#endif diff --git a/nuttx/drivers/wireless/nrf24l01.h b/nuttx/drivers/wireless/nrf24l01.h new file mode 100644 index 000000000..c5bddf0b8 --- /dev/null +++ b/nuttx/drivers/wireless/nrf24l01.h @@ -0,0 +1,183 @@ +/**************************************************************************** + * include/nuttx/drivers/wireless/nrf24l01.h + * + * Copyright (C) 2013 Laurent Latil + * Author: 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. + * + ****************************************************************************/ + +#ifndef __DRIVERS_WIRELESS_NRF24L01_H +#define __DRIVERS_WIRELESS_NRF24L01_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/spi.h> +#include <nuttx/irq.h> +#include <nuttx/wireless/wireless.h> + +#include <stdint.h> +#include <stdbool.h> + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +/* nRF24L01 hardware definitions */ + +/* Commands */ + +#define NRF24L01_R_REGISTER 0x00 +#define NRF24L01_W_REGISTER 0x20 +#define NRF24L01_R_RX_PAYLOAD 0x61 +#define NRF24L01_W_TX_PAYLOAD 0xA0 +#define NRF24L01_FLUSH_TX 0xE1 +#define NRF24L01_FLUSH_RX 0xE2 +#define NRF24L01_REUSE_TX_PL 0xE3 + +#define NRF24L01_ACTIVATE 0x50 +#define NRF24L01_R_RX_PL_WID 0x60 +#define NRF24L01_W_TX_PAYLOAD_NOACK 0xB0 +#define NRF24L01_W_ACK_PAYLOAD 0xA8 +#define NRF24L01_NOP 0xFF + +/* Registers */ + +#define NRF24L01_CONFIG 0x00 +#define NRF24L01_EN_AA 0x01 +#define NRF24L01_EN_RXADDR 0x02 +#define NRF24L01_SETUP_AW 0x03 +#define NRF24L01_SETUP_RETR 0x04 +#define NRF24L01_RF_CH 0x05 +#define NRF24L01_RF_SETUP 0x06 +#define NRF24L01_STATUS 0x07 +#define NRF24L01_OBSERVE_TX 0x08 +#define NRF24L01_CD 0x09 +#define NRF24L01_RX_ADDR_P0 0x0A +#define NRF24L01_RX_ADDR_P1 0x0B +#define NRF24L01_RX_ADDR_P2 0x0C +#define NRF24L01_RX_ADDR_P3 0x0D +#define NRF24L01_RX_ADDR_P4 0x0E +#define NRF24L01_RX_ADDR_P5 0x0F +#define NRF24L01_TX_ADDR 0x10 +#define NRF24L01_RX_PW_P0 0x11 +#define NRF24L01_RX_PW_P1 0x12 +#define NRF24L01_RX_PW_P2 0x13 +#define NRF24L01_RX_PW_P3 0x14 +#define NRF24L01_RX_PW_P4 0x15 +#define NRF24L01_RX_PW_P5 0x16 +#define NRF24L01_FIFO_STATUS 0x17 +#define NRF24L01_DYNPD 0x1C +#define NRF24L01_FEATURE 0x1D + +/* STATUS register definitions */ + +#define NRF24L01_RX_DR (1 << 6) +#define NRF24L01_TX_DS (1 << 5) +#define NRF24L01_MAX_RT (1 << 4) +#define NRF24L01_RX_P_NO_SHIFT 1 +#define NRF24L01_RX_P_NO_MASK (7 << NRF24L01_RX_P_NO_SHIFT) +#define NRF24L01_STAT_TX_FULL (1 << 0) + +/* CONFIG register definitions */ + +#define NRF24L01_MASK_RX_DR (1 << 6) +#define NRF24L01_MASK_TX_DS (1 << 5) +#define NRF24L01_MASK_MAX_RT (1 << 4) +#define NRF24L01_EN_CRC (1 << 3) +#define NRF24L01_CRCO (1 << 2) +#define NRF24L01_PWR_UP (1 << 1) +#define NRF24L01_PRIM_RX (1 << 0) + +/* RF_SETUP register definition */ + +#define NRF24L01_CONT_WAVE (1 << 7) +#define NRF24L01_RF_DR_LOW (1 << 5) +#define NRF24L01_PLL_LOCK (1 << 4) +#define NRF24L01_RF_DR_HIGH (1 << 3) + +#define NRF24L01_RF_PWR_SHIFT 1 +#define NRF24L01_RF_PWR_MASK (3 << NRF24L01_RF_PWR_SHIFT) + +/* FIFO STATUS register definitions */ + +#define NRF24L01_TX_REUSE (1 << 6) +#define NRF24L01_TX_FULL (1 << 5) +#define NRF24L01_TX_EMPTY (1 << 4) +#define NRF24L01_RX_FULL (1 << 1) +#define NRF24L01_RX_EMPTY (1 << 0) + +/* SETUP_RETR */ + +#define NRF24L01_ARC_SHIFT 0 +#define NRF24L01_ARC_MASK (0xF << NRF24L01_ARC_SHIFT) + +#define NRF24L01_ARD_SHIFT 4 +#define NRF24L01_ARD_MASK (0xF << NRF24L01_ARD_SHIFT) + +/* OBSERVE_TX register definitions */ +#define NRF24L01_PLOS_CNT_SHIFT 4 +#define NRF24L01_PLOS_CNT_MASK (0xF << NRF24L01_PLOS_CNT_SHIFT) +#define NRF24L01_ARC_CNT_SHIFT 0 +#define NRF24L01_ARC_CNT_MASK (0xF << NRF24L01_ARC_CNT_SHIFT) +#define NRF24L01_RX_P_NO_SHIFT 1 +#define NRF24L01_RX_P_NO_MASK (7 << NRF24L01_RX_P_NO_SHIFT) + +/* FEATURE register definitions */ + +#define NRF24L01_EN_DPL (1 << 2) +#define NRF24L01_EN_ACK_PAY (1 << 1) +#define NRF24L01_EN_DYN_ACK (1 << 0) + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" + { +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __DRIVERS_WIRELESS_NRF24L01_H */ diff --git a/nuttx/include/nuttx/fs/ioctl.h b/nuttx/include/nuttx/fs/ioctl.h index c22ead682..8e8647a69 100644 --- a/nuttx/include/nuttx/fs/ioctl.h +++ b/nuttx/include/nuttx/fs/ioctl.h @@ -68,6 +68,7 @@ #define _QEIOCBASE (0x0f00) /* Quadrature encoder ioctl commands */ #define _AUDIOIOCBASE (0x1000) /* Audio ioctl commands */ #define _SLCDIOCBASE (0x1100) /* Segment LCD ioctl commands */ +#define _WLIOCBASE (0x1100) /* Wireless modules ioctl commands */ /* Macros used to manage ioctl commands */ @@ -246,6 +247,12 @@ #define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE) #define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr) +/* Wireless driver ioctl definitions ************************************/ +/* (see nuttx/include/wireless/wireless.h */ + +#define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE) +#define _WLIOC(nr) _IOC(_WLIOCBASE,nr) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/nuttx/include/nuttx/wireless/cc1101.h b/nuttx/include/nuttx/wireless/cc1101.h index 9d6b2a673..80d3dbec3 100644 --- a/nuttx/include/nuttx/wireless/cc1101.h +++ b/nuttx/include/nuttx/wireless/cc1101.h @@ -2,7 +2,6 @@ * include/nuttx/wireless/cc1101.h * * Copyright (C) 2011 Uros Platise. All rights reserved. - * * Authors: Uros Platise <uros.platise@isotel.eu> * * Redistribution and use in source and binary forms, with or without @@ -34,14 +33,13 @@ * ****************************************************************************/ -/** \file - * \author Uros Platise - * \brief Chipcon CC1101 Device Driver - **/ - #ifndef __INCLUDE_NUTTX_WIRELESS_CC1101_H #define __INCLUDE_NUTTX_WIRELESS_CC1101_H +/**************************************************************************** + * Included Files + ****************************************************************************/ + #include <nuttx/config.h> #include <nuttx/spi.h> @@ -60,105 +58,142 @@ /* * General Purpose, Test Output Pin Options */ - + /* CC1101 General Purpose Pins */ #define CC1101_PIN_GDO0 2 #define CC1101_PIN_GDO1 1 #define CC1101_PIN_GDO2 0 - -/* Associated to the RX FIFO: Asserts when RX FIFO is filled at or above - * the RX FIFO threshold. De-asserts when RX FIFO is drained below the - * same threshold. */ + +/* Associated to the RX FIFO: Asserts when RX FIFO is filled at or above + * the RX FIFO threshold. De-asserts when RX FIFO is drained below the + * same threshold. + */ + #define CC1101_GDO_RXFIFO_THR 0x00 -/* Associated to the RX FIFO: Asserts when RX FIFO is filled at or above - * the RX FIFO threshold or the end of packet is reached. De-asserts when - * the RX FIFO is empty. */ -#define CC1101_GDO_RXFIFO_THREND 0x01 +/* Associated to the RX FIFO: Asserts when RX FIFO is filled at or above + * the RX FIFO threshold or the end of packet is reached. De-asserts when + * the RX FIFO is empty. + */ + +#define CC1101_GDO_RXFIFO_THREND 0x01 + +/* Associated to the TX FIFO: Asserts when the TX FIFO is filled at or + * above the TX FIFO threshold. De-asserts when the TX FIFO is below the + * same threshold. + */ -/* Associated to the TX FIFO: Asserts when the TX FIFO is filled at or - * above the TX FIFO threshold. De-asserts when the TX FIFO is below the - * same threshold. */ #define CC1101_GDO_TXFIFO_THR 0x02 /* Associated to the TX FIFO: Asserts when TX FIFO is full. De-asserts - * when the TX FIFO is drained below theTX FIFO threshold. */ + * when the TX FIFO is drained below theTX FIFO threshold. + */ + #define CC1101_GDO_TXFIFO_FULL 0x03 -/* Asserts when the RX FIFO has overflowed. De-asserts when the FIFO has - * been flushed. */ +/* Asserts when the RX FIFO has overflowed. De-asserts when the FIFO has + * been flushed. + */ + #define CC1101_GDO_RXFIFO_OVR 0x04 -/* Asserts when the TX FIFO has underflowed. De-asserts when the FIFO is - * flushed. */ +/* Asserts when the TX FIFO has underflowed. De-asserts when the FIFO is + * flushed. + */ + #define CC1101_GDO_TXFIFO_UNR 0x05 /* Asserts when sync word has been sent / received, and de-asserts at the - * end of the packet. In RX, the pin will de-assert when the optional - * address check fails or the RX FIFO overflows. In TX the pin will - * de-assert if the TX FIFO underflows. */ + * end of the packet. In RX, the pin will de-assert when the optional + * address check fails or the RX FIFO overflows. In TX the pin will + * de-assert if the TX FIFO underflows. + */ + #define CC1101_GDO_SYNC 0x06 -/* Asserts when a packet has been received with CRC OK. De-asserts when +/* Asserts when a packet has been received with CRC OK. De-asserts when * the first byte is read from the RX FIFO. */ #define CC1101_GDO_PKTRCV_CRCOK 0x07 -/* Preamble Quality Reached. Asserts when the PQI is above the programmed - * PQT value. */ +/* Preamble Quality Reached. Asserts when the PQI is above the programmed + * PQT value. + */ + #define CC1101_GDO_PREAMBLE 0x08 -/* Clear channel assessment. High when RSSI level is below threshold - * (dependent on the current CCA_MODE setting). */ +/* Clear channel assessment. High when RSSI level is below threshold + * (dependent on the current CCA_MODE setting). + */ + #define CC1101_GDO_CHCLEAR 0x09 -/* Lock detector output. The PLL is in lock if the lock detector output - * has a positive transition or is constantly logic high. To check for - * PLL lock the lock detector output should be used as an interrupt for - * the MCU. */ +/* Lock detector output. The PLL is in lock if the lock detector output + * has a positive transition or is constantly logic high. To check for + * PLL lock the lock detector output should be used as an interrupt for + * the MCU. + */ + #define CC1101_GDO_LOCK 0x0A /* Serial Clock. Synchronous to the data in synchronous serial mode. * In RX mode, data is set up on the falling edge by CC1101 when GDOx_INV=0. - * In TX mode, data is sampled by CC1101 on the rising edge of the serial - * clock when GDOx_INV=0. */ + * In TX mode, data is sampled by CC1101 on the rising edge of the serial + * clock when GDOx_INV=0. + */ + #define CC1101_GDO_SSCLK 0x0B /* Serial Synchronous Data Output. Used for synchronous serial mode. */ + #define CC1101_GDO_SSDO 0x0C /* Serial Data Output. Used for asynchronous serial mode. */ + #define CC1101_GDO_ASDO 0x0D /* Carrier sense. High if RSSI level is above threshold. */ + #define CC1101_GDO_CARRIER 0x0E -/* CRC_OK. The last CRC comparison matched. Cleared when entering or - * restarting RX mode. */ +/* CRC_OK. The last CRC comparison matched. Cleared when entering or + * restarting RX mode. + */ + #define CC1101_GDO_CRCOK 0x0F -/* RX_HARD_DATA[1]. Can be used together with RX_SYMBOL_TICK for - * alternative serial RX output. */ +/* RX_HARD_DATA[1]. Can be used together with RX_SYMBOL_TICK for + * alternative serial RX output. + */ + #define CC1101_GDO_RXOUT1 0x16 - -/* RX_HARD_DATA[0]. Can be used together with RX_SYMBOL_TICK for - * alternative serial RX output. */ + +/* RX_HARD_DATA[0]. Can be used together with RX_SYMBOL_TICK for + * alternative serial RX output. + */ + #define CC1101_GDO_RXOUT0 0x17 -/* PA_PD. Note: PA_PD will have the same signal level in SLEEP and TX - * states. To control an external PA or RX/TX switch in applications - * where the SLEEP state is used it is recommended to use GDOx_CFGx=0x2F - * instead. */ +/* PA_PD. Note: PA_PD will have the same signal level in SLEEP and TX + * states. To control an external PA or RX/TX switch in applications + * where the SLEEP state is used it is recommended to use GDOx_CFGx=0x2F + * instead. + */ + #define CC1101_GDO_PA_PD 0x1B -/* LNA_PD. Note: LNA_PD will have the same signal level in SLEEP and RX - * states. To control an external LNA or RX/TX switch in applications - * where the SLEEP state is used it is recommended to use GDOx_CFGx=0x2F - * instead. */ +/* LNA_PD. Note: LNA_PD will have the same signal level in SLEEP and RX + * states. To control an external LNA or RX/TX switch in applications + * where the SLEEP state is used it is recommended to use GDOx_CFGx=0x2F + * instead. + */ + #define CC1101_GDO_LNA_PD 0x1C -/* RX_SYMBOL_TICK. Can be used together with RX_HARD_DATA for alternative - * serial RX output. */ +/* RX_SYMBOL_TICK. Can be used together with RX_HARD_DATA for alternative + * serial RX output. + */ + #define CC1101_GDO_RXSYMTICK 0x1D #define CC1101_GDO_WOR_EVNT0 0x24 @@ -167,23 +202,30 @@ #define CC1101_GDO_CHIP_RDYn 0x29 #define CC1101_GDO_XOSC_STABLE 0x2B -/* GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input - * (for serial TX data). */ +/* GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input + * (for serial TX data). + */ + #define CC1101_GDO_GDO0_Z_EN_N 0x2D /* High impedance (3-state). */ + #define CC1101_GDO_HIZ 0x2E -/* HW to 0 (HW1 achieved by setting GDOx_INV=1). Can be used to control - * an external LNA/PA or RX/TX switch. */ +/* HW to 0 (HW1 achieved by setting GDOx_INV=1). Can be used to control + * an external LNA/PA or RX/TX switch. + */ + #define CC1101_GDO_HW 0x2F -/* There are 3 GDO pins, but only one CLK_XOSC/n can be selected as an - * output at any time. If CLK_XOSC/n is to be monitored on one of the - * GDO pins, the other two GDO pins must be configured to values less - * than 0x30. The GDO0 default value is CLK_XOSC/192. To optimize RF - * performance, these signals should not be used while the radio is - * in RX or TX mode. */ +/* There are 3 GDO pins, but only one CLK_XOSC/n can be selected as an + * output at any time. If CLK_XOSC/n is to be monitored on one of the + * GDO pins, the other two GDO pins must be configured to values less + * than 0x30. The GDO0 default value is CLK_XOSC/192. To optimize RF + * performance, these signals should not be used while the radio is + * in RX or TX mode. + */ + #define CC1101_GDO_CLK_XOSC1 0x30 #define CC1101_GDO_CLK_XOSC1_5 0x31 #define CC1101_GDO_CLK_XOSC2 0x32 @@ -201,9 +243,11 @@ #define CC1101_GDO_CLK_XOSC128 0x3E #define CC1101_GDO_CLK_XOSC192 0x3F +/**************************************************************************** + * Public Data Types + ****************************************************************************/ #ifndef __ASSEMBLY__ - #undef EXTERN #if defined(__cplusplus) #define EXTERN extern "C" @@ -212,78 +256,74 @@ extern "C" { #define EXTERN extern #endif -/**************************************************************************** - * Public Data Types - ****************************************************************************/ - struct cc1101_dev_s; -/** The RF Settings includes only those fields required to configure - * the RF radio. Other configuration fields depended on this driver - * are configured by the cc1101_init(). +/* The RF Settings includes only those fields required to configure + * the RF radio. Other configuration fields depended on this driver + * are configured by the cc1101_init(). */ -struct c1101_rfsettings_s { - uint8_t FSCTRL1; /* Frequency synthesizer control. */ - uint8_t FSCTRL0; /* Frequency synthesizer control. */ - - uint8_t FREQ2; /* Frequency control word, high byte. */ - uint8_t FREQ1; /* Frequency control word, middle byte. */ - uint8_t FREQ0; /* Frequency control word, low byte. */ - - uint8_t MDMCFG4; /* Modem configuration. */ - uint8_t MDMCFG3; /* Modem configuration. */ - uint8_t MDMCFG2; /* Modem configuration. */ - uint8_t MDMCFG1; /* Modem configuration. */ - uint8_t MDMCFG0; /* Modem configuration. */ - - uint8_t DEVIATN; /* Modem deviation setting (when FSK modulation is enabled). */ - - /* GAP */ - - uint8_t FOCCFG; /* Frequency Offset Compensation Configuration. */ - - uint8_t BSCFG; /* Bit synchronization Configuration. */ - - uint8_t AGCCTRL2; /* AGC control. */ - uint8_t AGCCTRL1; /* AGC control. */ - uint8_t AGCCTRL0; /* AGC control. */ - - /* GAP */ - - uint8_t FREND1; /* Front end RX configuration. */ - uint8_t FREND0; /* Front end RX configuration. */ - - uint8_t FSCAL3; /* Frequency synthesizer calibration. */ - uint8_t FSCAL2; /* Frequency synthesizer calibration. */ - uint8_t FSCAL1; /* Frequency synthesizer calibration. */ - uint8_t FSCAL0; /* Frequency synthesizer calibration. */ - - /* REGULATORY LIMITS */ - - uint8_t CHMIN; /* Channel Range defintion MIN .. */ - uint8_t CHMAX; /* .. and MAX */ - uint8_t PAMAX; /* at given maximum output power */ - - /* Power Table, for ramp-up/down and ASK modulation defined for - * output power values as: - * PA = {-30, -20, -15, -10, -5, 0, 5, 10} [dBm] - */ - uint8_t PA[8]; -}; +struct c1101_rfsettings_s +{ + uint8_t FSCTRL1; /* Frequency synthesizer control. */ + uint8_t FSCTRL0; /* Frequency synthesizer control. */ + + uint8_t FREQ2; /* Frequency control word, high byte. */ + uint8_t FREQ1; /* Frequency control word, middle byte. */ + uint8_t FREQ0; /* Frequency control word, low byte. */ + + uint8_t MDMCFG4; /* Modem configuration. */ + uint8_t MDMCFG3; /* Modem configuration. */ + uint8_t MDMCFG2; /* Modem configuration. */ + uint8_t MDMCFG1; /* Modem configuration. */ + uint8_t MDMCFG0; /* Modem configuration. */ + + uint8_t DEVIATN; /* Modem deviation setting (when FSK modulation is enabled). */ + + /* GAP */ + + uint8_t FOCCFG; /* Frequency Offset Compensation Configuration. */ + + uint8_t BSCFG; /* Bit synchronization Configuration. */ + + uint8_t AGCCTRL2; /* AGC control. */ + uint8_t AGCCTRL1; /* AGC control. */ + uint8_t AGCCTRL0; /* AGC control. */ + + /* GAP */ + + uint8_t FREND1; /* Front end RX configuration. */ + uint8_t FREND0; /* Front end RX configuration. */ + + uint8_t FSCAL3; /* Frequency synthesizer calibration. */ + uint8_t FSCAL2; /* Frequency synthesizer calibration. */ + uint8_t FSCAL1; /* Frequency synthesizer calibration. */ + uint8_t FSCAL0; /* Frequency synthesizer calibration. */ + + /* REGULATORY LIMITS */ + + uint8_t CHMIN; /* Channel Range defintion MIN .. */ + uint8_t CHMAX; /* .. and MAX */ + uint8_t PAMAX; /* at given maximum output power */ + + /* Power Table, for ramp-up/down and ASK modulation defined for + * output power values as: + * PA = {-30, -20, -15, -10, -5, 0, 5, 10} [dBm] + */ + + uint8_t PA[8]; +}; /**************************************************************************** * RF Configuration Database ****************************************************************************/ -EXTERN const struct c1101_rfsettings_s - // \todo Recalculate ERP in maximum power level /* 868 MHz, GFSK, 100 kbps, ISM Region 1 (Europe only) - * + * * ISM Region 1 (Europe) only, Band 868–870 MHz - * + * * Frequency bands for non-specific short range devices in Europe: * * Frequency ERP Duty Cycle Bandwidth Remarks @@ -292,9 +332,9 @@ EXTERN const struct c1101_rfsettings_s * 869.3 – 869.4 MHz +10 dBm No limits < 25 kHz Appropriate access protocol required * 869.4 – 869.65 MHz +27 dBm < 10% < 25 kHz Channels may be combined to one high speed channel * 869.7 -870 MHz +7 dBm No limits No limits - * + * * Frequency Band For License-Free Specific Applications in Europe - * + * * Frequency Application ERP Duty Cycle Bandwidth * 868.6 – 868.7 MHz Alarms +10 dBm < 0.1% 25 kHz(1) * 869.2 – 869.25 MHz Social Alarms +10 dBm < 0.1% 25 kHz @@ -302,30 +342,30 @@ EXTERN const struct c1101_rfsettings_s * 869.65 -869.7 MHz Alarms +14 dBm < 10% 25 kHz * 863 – 865 MHz Radio Microphones +10 dBm No limits 200 kHz * 863 -865 MHz Wireless Audio Applications +10 dBm No limits 300 kHz - * - * Duty Cycle Limit Total On Time Maximum On Time of Minimum Off Time of + * + * Duty Cycle Limit Total On Time Maximum On Time of Minimum Off Time of * Within One Hour One Transmission Two Transmission * < 0.1% 3.6 seconds 0.72 seconds 0.72 seconds * < 1% 36 seconds 3.6 seconds 1.8 seconds * < 10% 360 seconds 36 seconds 3.6 seconds - * + * * Reference: TI Application Report: swra048.pdf, May 2005 * ISM-Band and Short Range Device Regulatory Compliance Overview */ - cc1101_rfsettings_ISM1_868MHzGFSK100kbps, +EXTERN const struct c1101_rfsettings_s cc1101_rfsettings_ISM1_868MHzGFSK100kbps; /* 905 MHz, GFSK, 250 kbps, ISM Region 2 (America only) - * + * * ISM Region 2 (America) only, Band 902–928 MHz - * + * * Cordless phones 1 W * Microwave ovens 750 W * Industrial heaters 100 kW * Military radar 1000 kW */ - cc1101_rfsettings_ISM2_905MHzGFSK250kbps; +EXTERN const struct c1101_rfsettings_s cc1101_rfsettings_ISM2_905MHzGFSK250kbps; /**************************************************************************** * Public Function Prototypes @@ -334,131 +374,136 @@ EXTERN const struct c1101_rfsettings_s /** Initialize Chipcon CC1101 Chip. * After initialization CC1101 is ready to listen, receive and transmit * messages on the default channel 0 at given RF configuration. - * + * * \param spi SPI Device Structure * \param isrpin Select the CC1101_PIN_GDOx used to signal interrupts * \param rfsettings Pointer to default RF Settings loaded at boot time. * \return Pointer to newly allocated CC1101 structure or NULL on error with errno set. - * + * * Possible errno as set by this function on error: * - ENODEV: When device addressed is not compatible or it is not a CC1101 * - EFAULT: When there is no device * - ENOMEM: Out of kernel memory to allocate the device * - EBUSY: When device is already addressed by other device driver (not yet supported by low-level driver) **/ -EXTERN struct cc1101_dev_s * cc1101_init(struct spi_dev_s * spi, uint8_t isrpin, - uint32_t pinset, const struct c1101_rfsettings_s * rfsettings); +struct cc1101_dev_s * cc1101_init(struct spi_dev_s * spi, uint8_t isrpin, + uint32_t pinset, const struct c1101_rfsettings_s * rfsettings); /** Deinitialize Chipcon CC1101 Chip - * + * * \param dev Device to CC1101 device structure, as returned by the cc1101_init() * \return OK On success - * + * **/ -EXTERN int cc1101_deinit(struct cc1101_dev_s * dev); +int cc1101_deinit(struct cc1101_dev_s * dev); /** Power up device, start conversion. \return Zero on success. */ -EXTERN int cc1101_powerup(struct cc1101_dev_s * dev); + +int cc1101_powerup(struct cc1101_dev_s * dev); /** Power down device, stop conversion. \return Zero on success. */ -EXTERN int cc1101_powerdown(struct cc1101_dev_s * dev); + +int cc1101_powerdown(struct cc1101_dev_s * dev); /** Set Multi Purpose Output Function. \return Zero on success. */ -EXTERN int cc1101_setgdo(struct cc1101_dev_s * dev, uint8_t pin, uint8_t function); + +int cc1101_setgdo(struct cc1101_dev_s * dev, uint8_t pin, uint8_t function); /** Set RF settings. Use one from the database above. */ -EXTERN int cc1101_setrf(struct cc1101_dev_s * dev, const struct c1101_rfsettings_s *settings); -/** Set Channel. +int cc1101_setrf(struct cc1101_dev_s * dev, const struct c1101_rfsettings_s *settings); + +/** Set Channel. * Note that regulatory check is made and sending may be prohibited. - * + * * \retval 0 On success, sending and receiving is allowed. * \retval 1 Only receive mode is allowed. * \retval <0 On error. */ -EXTERN int cc1101_setchannel(struct cc1101_dev_s * dev, uint8_t channel); +int cc1101_setchannel(struct cc1101_dev_s * dev, uint8_t channel); /** Set Output Power - * + * * \param power Value from 0 - 8, where 0 means power off, and values * from 1 .. 8 denote the following output power in dBm: * {-30, -20, -15, -10, -5, 0, 5, 10} [dBm] - * + * * If power is above the regulatory limit (defined by the RF settings) * it is limited. - * + * * \return Actual output power in range from 0..8. */ -EXTERN uint8_t cc1101_setpower(struct cc1101_dev_s * dev, uint8_t power); +uint8_t cc1101_setpower(struct cc1101_dev_s * dev, uint8_t power); /** Convert RSSI as obtained from CC1101 to [dBm] */ -EXTERN int cc1101_calcRSSIdBm(int rssi); +int cc1101_calcRSSIdBm(int rssi); -/** Enter receive mode and wait for a packet. +/** Enter receive mode and wait for a packet. * If transmission is in progress, receive mode is entered upon its * completion. As long cc1101_idle() is not called, each transmission * returns to receive mode. - * + * * \param dev Device to CC1101 structure - * \return Zero on success. + * \return Zero on success. */ -EXTERN int cc1101_receive(struct cc1101_dev_s * dev); +int cc1101_receive(struct cc1101_dev_s * dev); -/** Read received packet - * +/** Read received packet + * * If size of buffer is too small then the remaining part of data can * be discarded by the driver. - * + * * Packet contains raw data, including the two bytes: - * - RSSI and - * - LQI + * - RSSI and + * - LQI * appended at the end of the message. - * + * * To inquery about the data pending size you the following: * - pass buf=NULL and size > 0, returns pending data packet size * - pass buf=NULL and size = 0, returns maximum data packet size - * + * * NOTE: messages length are typically defined by the MAC, transmit/ * receive windows at some rate. */ -EXTERN int cc1101_read(struct cc1101_dev_s * dev, uint8_t * buf, size_t size); +int cc1101_read(struct cc1101_dev_s * dev, uint8_t * buf, size_t size); /** Write data to be send, using the cc1101_send() - * + * * \param dev Device to CC1101 structure * \param buf Pointer to data. * \param size Size must be within limits, otherwise data is truncated. - * Present driver limitation supports a single cc1101_write() + * Present driver limitation supports a single cc1101_write() * prioer cc1101_send() is called. */ -EXTERN int cc1101_write(struct cc1101_dev_s * dev, const uint8_t * buf, size_t size); +int cc1101_write(struct cc1101_dev_s * dev, const uint8_t * buf, size_t size); /** Send data previously writtenusing cc1101_write() - * + * * \param dev Device to CC1101 structure - * \return Zero on success. + * \return Zero on success. */ -EXTERN int cc1101_send(struct cc1101_dev_s * dev); +int cc1101_send(struct cc1101_dev_s * dev); -/** Enter idle state (after reception and transmission completes). - * +/** Enter idle state (after reception and transmission completes). + * * \return Zero on success. */ -EXTERN int cc1101_idle(struct cc1101_dev_s * dev); +int cc1101_idle(struct cc1101_dev_s * dev); #undef EXTERN #if defined(__cplusplus) } #endif + #endif /* __ASSEMBLY__ */ #endif /* __INCLUDE_NUTTX_WIRELESS_CC1101_H */ diff --git a/nuttx/include/nuttx/wireless/nrf24l01.h b/nuttx/include/nuttx/wireless/nrf24l01.h new file mode 100644 index 000000000..d23aa3650 --- /dev/null +++ b/nuttx/include/nuttx/wireless/nrf24l01.h @@ -0,0 +1,444 @@ +/**************************************************************************** + * include/nuttx/wireless/nrf24l01.h + * + * Copyright (C) 2013 Laurent Latil + * Author: 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_NRF24L01_H +#define __INCLUDE_NUTTX_NRF24L01_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/spi.h> +#include <nuttx/irq.h> +#include <nuttx/wireless/wireless.h> + +#include <stdint.h> +#include <stdbool.h> + +/**************************************************************************** + * Pre-Processor Declarations + ****************************************************************************/ + +#define NRF24L01_MIN_ADDR_LEN 3 /* Minimal length (in bytes) of a pipe address */ +#define NRF24L01_MAX_ADDR_LEN 5 /* Maximum length (in bytes) of a pipe address */ +#define NRF24L01_MAX_PAYLOAD_LEN 32 /* Maximum length (in bytes) of a payload */ +#define NRF24L01_MAX_XMIT_RETR 15 /* Maximum auto retransmit count (for AA transmissions) */ +#define NRF24L01_PIPE_COUNT 6 /* Number of available pipes */ + +#define NRF24L01_MIN_FREQ 2400 /* Lower bound for RF frequency */ +#define NRF24L01_MAX_FREQ 2525 /* Upper bound for RF frequency */ + +#define NRF24L01_DYN_LENGTH 33 /* Specific length value to use to enable dynamic packet length */ +#define NRF24L01_XMIT_MAXRT 255 /* Specific value returned by Number of available pipes */ + +/* #define NRF24L01_DEBUG 1 */ + +/* IOCTL commands */ + +#define NRF24L01IOC_SETRETRCFG _WLIOC_USER(0x0001) /* arg: Pointer to nrf24l01_retrcfg_t structure */ +#define NRF24L01IOC_GETRETRCFG _WLIOC_USER(0x0002) /* arg: Pointer to nrf24l01_retrcfg_t structure */ +#define NRF24L01IOC_SETPIPESCFG _WLIOC_USER(0x0003) /* arg: Pointer to an array of nrf24l01_pipecfg_t pointers */ +#define NRF24L01IOC_GETPIPESCFG _WLIOC_USER(0x0004) /* arg: Pointer to an array of nrf24l01_pipecfg_t pointers */ +#define NRF24L01IOC_SETPIPESENABLED _WLIOC_USER(0x0005) /* arg: Pointer to a uint8_t value, bit field of enabled / disabled pipes */ +#define NRF24L01IOC_GETPIPESENABLED _WLIOC_USER(0x0006) /* arg: Pointer to a uint8_t value, bit field of enabled / disabled pipes */ +#define NRF24L01IOC_SETDATARATE _WLIOC_USER(0x0007) /* arg: Pointer to a nrf24l01_datarate_t value */ +#define NRF24L01IOC_GETDATARATE _WLIOC_USER(0x0008) /* arg: Pointer to a nrf24l01_datarate_t value */ +#define NRF24L01IOC_SETADDRWIDTH _WLIOC_USER(0x0009) /* arg: Pointer to an uint32_t value, width of the address */ +#define NRF24L01IOC_GETADDRWIDTH _WLIOC_USER(0x000A) /* arg: Pointer to an uint32_t value, width of the address */ +#define NRF24L01IOC_SETSTATE _WLIOC_USER(0x000B) /* arg: Pointer to a nrf24l01_state_t value */ +#define NRF24L01IOC_GETSTATE _WLIOC_USER(0x000C) /* arg: Pointer to a nrf24l01_state_t value */ +#define NRF24L01IOC_GETLASTXMITCOUNT _WLIOC_USER(0x000D) /* arg: Pointer to an uint32_t value, retransmission count of the last send operation (NRF24L01_XMIT_MAXRT if no ACK received)*/ +#define NRF24L01IOC_GETLASTPIPENO _WLIOC_USER(0x000E) /* arg: Pointer to an uint32_t value, pipe # of the last received packet */ + +/* Aliased name for these commands */ + +#define NRF24L01IOC_SETTXADDR WLIOC_SETADDR +#define NRF24L01IOC_GETTXADDR WLIOC_GETADDR + +/* NRF24L01 debug */ + +#ifdef NRF24L01_DEBUG +# define wdbg(format, arg...) dbg(format, ##arg) +# define wlldbg(format, arg...) lldbg(format, ##arg) +# define wvdbg(format, arg...) vdbg(format, ##arg) +# define wllvdbg(format, arg...) llvdbg(format, ##arg) +#else +# define wdbg(x...) +# define wlldbg(x...) +# define wvdbg(x...) +# define wllvdbg(x...) +#endif + +/**************************************************************************** + * Public Data Types + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" + { +#else +# define EXTERN extern +#endif + +/* Data rates available for transmission */ + +typedef enum +{ + RATE_1Mbps, + RATE_2Mbps, + RATE_250kbps +} nrf24l01_datarate_t; + +/* Definition of the different possible states of the module */ + +typedef enum +{ + ST_UNKNOWN, + ST_POWER_DOWN, + ST_STANDBY, +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + ST_RX +#endif +} nrf24l01_state_t; + +/* Re-transmission delay */ + +typedef enum +{ + DELAY_250us, + DELAY_500us, + DELAY_750us, + DELAY_1000us, + DELAY_1250us, + DELAY_1500us, + DELAY_1750us, + DELAY_2000us, + DELAY_2250us, + DELAY_2500us, + DELAY_2750us, + DELAY_3000us, + DELAY_3250us, + DELAY_3500us, + DELAY_3750us, + DELAY_4000us +} nrf24l01_retransmit_delay_t; + +/* Opaque definition of a nRF24L01 device */ + +struct nrf24l01_dev_s; + +/* Configuration info for a RX pipe */ + +struct nrf24l01_pipecfg_s +{ + bool en_aa; /* Enable auto-acknowledge */ + uint8_t rx_addr[NRF24L01_MAX_ADDR_LEN]; /* Receive address for the data pipe (LSB first) */ + uint8_t payload_length; /* Define packet size (NRF24L01_DYN_LENGTH : dynamic length payload ) */ +}; +typedef struct nrf24l01_pipecfg_s nrf24l01_pipecfg_t; + +/* Configuration of the retransmission parameters (used when AA is enabled) */ + +struct nrf24l01_retrcfg_s +{ + nrf24l01_retransmit_delay_t delay; /* Delay before retransmitting (when no ACK received) */ + uint8_t count; /* Retransmit retries count */ +}; +typedef struct nrf24l01_retrcfg_s nrf24l01_retrcfg_t; + +/* A reference to a structure of this type must be passed to the initialization + * method (nrf24l01_init() ). It provides some board-specific hooks used + * by driver to manage the GPIO lines (IRQ and CE). + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +struct nrf24l01_config_s +{ + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the ADS7843E driver from differences in GPIO + * interrupt handling by varying boards and MCUs. If possible, + * interrupts should be configured on both rising and falling edges + * so that contact and loss-of-contact events can be detected. + * + * irqattach - Attach the driver interrupt handler to the GPIO interrupt + * chipenable - Enable or disable the chip (CE line) + */ + + int (*irqattach)(xcpt_t isr); + void (*chipenable)(bool enable); +}; + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/** Register the nRF24L01+ device. + * + * \param spi SPI Device structure + * \param cfg Board specific configuration info + * \return Pointer to newly allocated nrf24l01 device structure or NULL on error (errno is set accordingly in this case). + * + * Possible errors: + * - ENOMEM: Out of kernel memory to allocate the device + **/ + +int nrf24l01_register(FAR struct spi_dev_s *spi, FAR struct nrf24l01_config_s *cfg); + +/** Initialize the nRF24L01+ chip to a default initial state. + * + * \param dev Pointer to a registered nRF24L01 device structure + **/ + +int nrf24l01_init(FAR struct nrf24l01_dev_s *dev); + +/** Get a pointer to the registered device + */ + +FAR struct nrf24l01_dev_s * nrf24l01_getinstance(void); + +/** Set the default TX address. + * + * \param dev Pointer to an nRF24L01 device structure + * \param addr TX address (LSByte first) + * + * \return 0 + **/ + +int nrf24l01_settxaddr(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *addr); + +/** Get the default TX address. + * + * \param dev Pointer to an nRF24L01 device structure + * \param addr TX address (LSByte first) + * + * \return 0 + **/ + +int nrf24l01_gettxaddr(FAR struct nrf24l01_dev_s *dev, FAR uint8_t *addr); + +/** Configure auto-retransmit + * + * \param dev Pointer to an nRF24L01 device structure + * \param retrdelay Auto-retransmit delay + * \param retrcount Auto-retransmit count (0 - 15) + * \return 0 + **/ + +int nrf24l01_setretransmit(FAR struct nrf24l01_dev_s *dev, nrf24l01_retransmit_delay_t retrdelay, uint8_t retrcount); + +/** Configure a RX pipe. + * + * \param dev Pointer to an nRF24L01 device structure + * \param pipeno Pipe number to configure + * \param pipecfg Pointer to configuration data + * + * \return 0 + **/ + +int nrf24l01_setpipeconfig(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno, + FAR const nrf24l01_pipecfg_t *pipecfg); + +/** Get pipe configuration. + * + * \param dev Pointer to an nRF24L01 device structure + * \param pipeno Pipe number to configure + * \param pipecfg Pointer to configuration data used to store the config + * + * \return 0 + **/ + +int nrf24l01_getpipeconfig(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno, + FAR nrf24l01_pipecfg_t *pipecfg); + +/** Enable a RX pipe. + * + * \param dev Pointer to an nRF24L01 device structure + * \param pipeno Pipe number + * \param enable true to enable the pipe, false to disable it + * + * \return 0 + **/ + +int nrf24l01_enablepipe(FAR struct nrf24l01_dev_s *dev, unsigned int pipeno, bool enable); + +/** Configure RF. + * + * \param dev Pointer to an nRF24L01 device structure + * \param datarate Datarate + * \param outpower Output power + * + * \return 0 + **/ + +int nrf24l01_setuprf(FAR struct nrf24l01_dev_s *dev, nrf24l01_datarate_t datarate, + int outpower); + +/** Configure the TX output power. + * + * Note that hardware supports only -18, -12, -6 and 0 dBm values. + * + * \param dev Pointer to an nRF24L01 device structure + * \param outpower Output power (in dBm). + * + * \return 0 + **/ + +int nrf24l01_settxpower(FAR struct nrf24l01_dev_s *dev, int outpower); + +/** Get the current TX output power. + * + * Note that hardware supports only -18, -12, -6 and 0 dBm values. + * + * \param dev Pointer to an nRF24L01 device structure + * \return outpower Output power (in dBm) + **/ + +int nrf24l01_gettxpower(FAR struct nrf24l01_dev_s *dev); + +/** Set transmission data rate + + * \param dev Pointer to an nRF24L01 device structure + * \return datarate Data rate + **/ + +int nrf24l01_setdatarate(FAR struct nrf24l01_dev_s *dev, nrf24l01_datarate_t datarate); + +/** Set radio frequency. + * + * \param dev Pointer to an nRF24L01 device structure + * \param freq New frequency value (in Mhz: 2400 to 2525) + * + * \return OK + **/ + +int nrf24l01_setradiofreq(FAR struct nrf24l01_dev_s *dev, uint32_t freq); + +/** Get current radio frequency. + * + * \param dev Pointer to an nRF24L01 device structure + * + * \return Radio frequency (in Mhz) + **/ + +uint32_t nrf24l01_getradiofreq(FAR struct nrf24l01_dev_s *dev); + +/** Configure address length. + * + * \param dev Pointer to an nRF24L01 device structure + * \param width Address width to use (3-5) + * \return 0 + **/ + +int nrf24l01_setaddrwidth(FAR struct nrf24l01_dev_s *dev, uint32_t width); + +/** Change the current lifecycle state of the nRF24L01+ chip. + * + * \param dev Pointer to an nRF24L01 device structure + * \param state New state to put the nRF24L01 module into + **/ + +int nrf24l01_changestate(FAR struct nrf24l01_dev_s *dev, nrf24l01_state_t state); + +/** Send data to the default address. + * + * \param dev Pointer to an nRF24L01 device structure + * \param data Pointer on the data buffer + * \param datalen Length of the buffer (in bytes) + * + * \return + **/ + +int nrf24l01_send(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data, size_t datalen); + +/** Send data to the specified address. + * + * \param dev Pointer to an nRF24L01 device structure + * \param data Pointer on the data buffer + * \param datalen Length of the buffer (in bytes) + * \param destaddr Destination address + * + * \return + **/ + +int nrf24l01_sendto(FAR struct nrf24l01_dev_s *dev, FAR const uint8_t *data, + size_t datalen, FAR const uint8_t *destaddr); + +/** Get the retransmits count of the last transmission. + * This value is meaningful only if auto-acknowledge is enabled. + * + * \param dev Pointer to an nRF24L01 device structure + * \return Retransmit count, or NRF24L01_XMIT_MAXRT if no ACK received (transmission failure) + */ + +int nrf24l01_xmitcount(FAR struct nrf24l01_dev_s *dev); + + +#ifdef CONFIG_WL_NRF24L01_RXSUPPORT + +/** Read the next received frame. + * + * \param dev Pointer to an nRF24L01 device structure + * \param buffer Pointer on buffer used to store the received frame bytes + * \param buflen Length of the buffer (in bytes) + * \param recvpipe Pointer to a byte value used to store the pipe number of the frame + * (use NULL if the pipe number info is not required) + * + * \return Length of the actual data + **/ + +ssize_t nrf24l01_recv(struct nrf24l01_dev_s *dev, uint8_t *buffer, + size_t buflen, uint8_t *recvpipe); + +#endif + +#ifdef NRF24L01_DEBUG + +void nrf24l01_dumpregs(FAR struct nrf24l01_dev_s *dev); + +void nrf24l01_dumprxfifo(FAR struct nrf24l01_dev_s *dev); + +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_NRF24L01_H */ diff --git a/nuttx/include/nuttx/wireless/wireless.h b/nuttx/include/nuttx/wireless/wireless.h new file mode 100644 index 000000000..de1f097ec --- /dev/null +++ b/nuttx/include/nuttx/wireless/wireless.h @@ -0,0 +1,74 @@ +/************************************************************************************ + * include/nuttx/wireless/wireless.h + * + * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved. + * Author: Laurent Latil <gnutt@nuttx.org> + * + * 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. + * + ************************************************************************************/ + +/* This file includes common definitions to be used in all wireless drivers + * (when applicable). + */ + +#ifndef __INCLUDE_NUTTX_WIRELESS_H +#define __INCLUDE_NUTTX_WIRELESS_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/fs/ioctl.h> + +#ifdef CONFIG_WIRELESS + +/************************************************************************************ + * Pre-Processor Definitions + ************************************************************************************/ +/* IOCTL Commands *******************************************************************/ + +#define WLIOC_SETRADIOFREQ _WLIOC(0x0001) /* arg: Pointer to uint32_t, frequency value (in Mhz) */ +#define WLIOC_GETRADIOFREQ _WLIOC(0x0002) /* arg: Pointer to uint32_t, frequency value (in Mhz) */ +#define WLIOC_SETADDR _WLIOC(0x0003) /* arg: Pointer to address value, format of the address is driver specific */ +#define WLIOC_GETADDR _WLIOC(0x0004) /* arg: Pointer to address value, format of the address is driver specific */ +#define WLIOC_SETTXPOWER _WLIOC(0x0005) /* arg: Pointer to int32_t, output power (in dBm) */ +#define WLIOC_GETTXPOWER _WLIOC(0x0006) /* arg: Pointer to int32_t, output power (in dBm) */ + +/* Wireless drivers can provide additional, device specific ioctl + * commands, beginning with this value: + */ + +#define WLIOC_USER 0x0007 /* Lowest, unused WL ioctl command */ + +#define _WLIOC_USER(nr) _WLIOC(nr + WLIOC_USER) + +#endif + +#endif /* __INCLUDE_NUTTX_WIRELESS_H */ |