summaryrefslogtreecommitdiffstats
path: root/drivers/staging/pi433
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/pi433')
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts48
-rw-r--r--drivers/staging/pi433/Documentation/devicetree/pi433.txt62
-rw-r--r--drivers/staging/pi433/Documentation/pi433.txt274
-rw-r--r--drivers/staging/pi433/Kconfig17
-rw-r--r--drivers/staging/pi433/Makefile4
-rw-r--r--drivers/staging/pi433/TODO5
-rw-r--r--drivers/staging/pi433/pi433_if.c1436
-rw-r--r--drivers/staging/pi433/pi433_if.h148
-rw-r--r--drivers/staging/pi433/rf69.c832
-rw-r--r--drivers/staging/pi433/rf69.h70
-rw-r--r--drivers/staging/pi433/rf69_enum.h126
-rw-r--r--drivers/staging/pi433/rf69_registers.h478
12 files changed, 3500 insertions, 0 deletions
diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts
new file mode 100644
index 000000000..096137fcd
--- /dev/null
+++ b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts
@@ -0,0 +1,48 @@
+// Definitions for Pi433
+/dts-v1/;
+/plugin/;
+
+/ {
+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709";
+};
+
+&spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ spidev@0{
+ reg = <0>;
+ status = "disabled";
+ };
+
+ spidev@1{
+ reg = <1>;
+ status = "disabled";
+ };
+};
+
+&gpio {
+ pi433_pins: pi433_pins {
+ brcm,pins = <7 25 24>;
+ brcm,function = <0 0 0>; // in in in
+ };
+};
+
+&spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ pi433: pi433@0 {
+ compatible = "Smarthome-Wolf,pi433";
+ reg = <0>;
+ spi-max-frequency = <10000000>;
+ status = "okay";
+
+ pinctrl-0 = <&pi433_pins>;
+ DIO0-gpio = <&gpio 24 0>;
+ DIO1-gpio = <&gpio 25 0>;
+ DIO2-gpio = <&gpio 7 0>;
+ };
+};
diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433.txt b/drivers/staging/pi433/Documentation/devicetree/pi433.txt
new file mode 100644
index 000000000..a810548c5
--- /dev/null
+++ b/drivers/staging/pi433/Documentation/devicetree/pi433.txt
@@ -0,0 +1,62 @@
+* Smarthome-Wolf Pi433 - a 433MHz radio module/shield for Raspberry Pi (see www.pi433.de)
+
+Required properties:
+- compatible: must be "Smarthome-Wolf,pi433"
+- reg: chip select of SPI Interface
+- DIOx-gpio must be dedicated to the GPIO, connected with DIOx of the RFM69 module
+
+
+Example:
+
+With the following lines in gpio-section, the gpio pins, connected with pi433 are
+reserved/declared.
+
+&gpio{
+ [...]
+
+ pi433_pins: pi433_pins {
+ brcm,pins = <7 25 24>;
+ brcm,function = <0 0 0>; // in in in
+ };
+
+ [...]
+}
+
+With the following lines in spi section, the device pi433 is declared.
+It consists of the three gpio pins and an spi interface (here chip select 0)
+
+&spi0{
+ [...]
+
+ pi433: pi433@0 {
+ compatible = "Smarthome-Wolf,pi433";
+ reg = <0>; /* CE 0 */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ spi-max-frequency = <10000000>;
+
+ pinctrl-0 = <&pi433_pins>;
+ DIO0-gpio = <&gpio 24 0>;
+ DIO1-gpio = <&gpio 25 0>;
+ DIO2-gpio = <&gpio 7 0>;
+ };
+}
+
+
+
+For Raspbian users only
+=======================
+Since Raspbian supports device tree overlays, you may use an overlay instead
+of editing your boards device tree.
+To use the overlay, you need to compile the file pi433-overlay.dts which can
+be found alongside this documentation.
+The file needs to be compiled - either manually or by integration in your kernel
+source tree. For a manual compile, you may use a command line like the following:
+'linux/scripts/dtc/dtc -@ -I dts -O dtb -o pi433.dtbo pi433-overlay.dts'
+
+For compiling inside of the kernel tree, you need to copy pi433-overlay.dts to
+arch/arm/boot/dts/overlays and you need to add the file to the list of files
+in the Makefile over there. Execute 'make dtbs' in kernel tree root to make the
+kernel make files compile the device tree overlay for you.
+
+
diff --git a/drivers/staging/pi433/Documentation/pi433.txt b/drivers/staging/pi433/Documentation/pi433.txt
new file mode 100644
index 000000000..4a0d34b4a
--- /dev/null
+++ b/drivers/staging/pi433/Documentation/pi433.txt
@@ -0,0 +1,274 @@
+=====
+Pi433
+=====
+
+
+Introduction
+============
+This driver is for controlling pi433, a radio module for the Raspberry Pi
+(www.pi433.de). It supports transmission and reception. It can be opened
+by multiple applications for transmission and reception. While transmit
+jobs are queued and processed automatically in the background, the first
+application asking for reception will block out all other applications
+until something gets received terminates the read request.
+The driver supports on the fly reloading of the hardware fifo of the rf
+chip, thus enabling for much longer telegrams than the hardware fifo size.
+
+Description of driver operation
+===============================
+
+a) transmission
+
+Each transmission can take place with a different configuration of the rf
+module. Therefore each application can set its own set of parameters. The driver
+takes care, that each transmission takes place with the parameterset of the
+application, that requests the transmission. To allow the transmission to take
+place in the background, a tx thread is introduced.
+The transfer of data from the main thread to the tx thread is realised by a
+kfifo. With each write request of an application, the passed in data and the
+corresponding parameter set gets written to the kfifo.
+On the other "side" of the kfifo, the tx thread continuously checks, whether the
+kfifo is empty. If not, it gets one set of config and data from the kfifo. If
+there is no receive request or the receiver is still waiting for something in
+the air, the rf module is set to standby, the parameters for transmission gets
+set, the hardware fifo of the rf chip gets preloaded and the transmission gets
+started. Upon hardware fifo threshold interrupt it gets reloaded, thus enabling
+much longer telegrams than the hardware fifo size. If the telegram is sent and there
+is more data available in the kfifo, the procedure is repeated. If not the
+transmission cycle ends.
+
+b) reception
+
+Since there is only one application allowed to receive data at a time, for
+reception there is only one configuration set.
+As soon as an application sets a request for receiving a telegram, the reception
+configuration set is written to the rf module and it gets set into receiving mode.
+Now the driver is waiting, that a predefined RSSI level (signal strength at the
+receiver) is reached. Until this hasn't happened, the reception can be
+interrupted by the transmission thread at any time to insert a transmission cycle.
+As soon as the predefined RSSI level is met, a receiving cycle starts. Similar
+as described for the transmission cycle the read out of the hardware fifo is done
+dynamically. Upon each hardware fifo threshold interrupt, a portion of data gets
+read. So also for reception it is possible to receive more data than the hardware
+fifo can hold.
+
+
+Driver API
+==========
+
+The driver is currently implemented as a character device. Therefore it supports
+the calls open, ioctl, read, write and close.
+
+
+params for ioctl
+----------------
+
+There are four options:
+PI433_IOC_RD_TX_CFG - get the transmission parameters from the driver
+PI433_IOC_WR_TX_CFG - set the transmission parameters
+PI433_IOC_RD_RX_CFG - get the receiving parameters from the driver
+PI433_IOC_WR_RX_CFG - set the receiving parameters
+
+The tx configuration is transferred via struct pi433_tx_cfg, the parameterset for transmission.
+It is divided into two sections: rf parameters and packet format.
+
+rf params:
+ frequency
+ frequency used for transmission.
+ Allowed values: 433050000...434790000
+ bit_rate
+ bit rate used for transmission.
+ Allowed values: #####
+ dev_frequency
+ frequency deviation in case of FSK.
+ Allowed values: 600...500000
+ modulation
+ FSK - frequency shift key
+ OOK - On-Off-key
+ modShaping
+ shapingOff - no shaping
+ shaping1_0 - gauss filter with BT 1 (FSK only)
+ shaping0_5 - gauss filter with BT 0.5 (FSK only)
+ shaping0_3 - gauss filter with BT 0.3 (FSK only)
+ shapingBR - filter cut off at BR (OOK only)
+ shaping2BR - filter cut off at 2*BR (OOK only)
+ pa_ramp (FSK only)
+ ramp3400 - amp ramps up in 3.4ms
+ ramp2000 - amp ramps up in 2.0ms
+ ramp1000 - amp ramps up in 1ms
+ ramp500 - amp ramps up in 500us
+ ramp250 - amp ramps up in 250us
+ ramp125 - amp ramps up in 125us
+ ramp100 - amp ramps up in 100us
+ ramp62 - amp ramps up in 62us
+ ramp50 - amp ramps up in 50us
+ ramp40 - amp ramps up in 40us
+ ramp31 - amp ramps up in 31us
+ ramp25 - amp ramps up in 25us
+ ramp20 - amp ramps up in 20us
+ ramp15 - amp ramps up in 15us
+ ramp12 - amp ramps up in 12us
+ ramp10 - amp ramps up in 10us
+ tx_start_condition
+ fifo_level - transmission starts, if fifo is filled to
+ threshold level
+ fifo_not_empty - transmission starts, as soon as there is one
+ byte in internal fifo
+ repetitions
+ This gives the option, to send a telegram multiple times. Default: 1
+
+packet format:
+ enable_preamble
+ optionOn - a preamble will be automatically generated
+ optionOff - no preamble will be generated
+ enable_sync
+ optionOn - a sync word will be automatically added to
+ the telegram after the preamble
+ optionOff - no sync word will be added
+ Attention: While possible to generate sync without preamble, the
+ receiver won't be able to detect the sync without preamble.
+ enable_length_byte
+ optionOn - the length of the telegram will be automatically
+ added to the telegram. It's part of the payload
+ optionOff - no length information will be automatically added
+ to the telegram.
+ Attention: For telegram length over 255 bytes, this option can't be used
+ Attention: should be used in combination with sync, only
+ enable_address_byte
+ optionOn - the address byte will be automatically added to the
+ telegram. It's part of the payload
+ optionOff - the address byte will not be added to the telegram.
+ The address byte can be used for address filtering, so the receiver
+ will only receive telegrams with a given address byte.
+ Attention: should be used in combination with sync, only
+ enable_crc
+ optionOn - an crc will be automatically calculated over the
+ payload of the telegram and added to the telegram
+ after payload.
+ optionOff - no crc will be calculated
+ preamble_length
+ length of the preamble. Allowed values: 0...65536
+ sync_length
+ length of the sync word. Allowed values: 0...8
+ fixed_message_length
+ length of the payload of the telegram. Will override the length
+ given by the buffer, passed in with the write command. Will be
+ ignored if set to zero.
+ sync_pattern[8]
+ contains up to eight values, that are used as the sync pattern
+ on sync option
+ address_byte
+ one byte, used as address byte on address byte option.
+
+
+The rx configuration is transferred via struct pi433_rx_cfg, the parameterset for receiving. It is divided into two sections: rf parameters and packet format.
+
+rf params:
+ frequency
+ frequency used for transmission.
+ Allowed values: 433050000...434790000
+ bit_rate
+ bit rate used for transmission.
+ Allowed values: #####
+ dev_frequency
+ frequency deviation in case of FSK.
+ Allowed values: 600...500000
+ modulation
+ FSK - frequency shift key
+ OOK - on off key
+ rssi_threshold
+ threshold value for the signal strength on the receiver input.
+ If this value is exceeded, a reception cycle starts
+ Allowed values: 0...255
+ threshold_decrement
+ in order to adapt to different levels of singnal strength, over
+ time the receiver gets more and more sensitive. This value
+ determs, how fast the sensitivity increases.
+ step_0_5db - increase in 0,5dB steps
+ step_1_0db - increase in 1 db steps
+ step_1_5db - increase in 1,5dB steps
+ step_2_0db - increase in 2 db steps
+ step_3_0db - increase in 3 db steps
+ step_4_0db - increase in 4 db steps
+ step_5_0db - increase in 5 db steps
+ step_6_0db - increase in 6 db steps
+ antenna_impedance
+ sets the electrical adoption of the antenna
+ fifty_ohm - for antennas with an impedance of 50Ohm
+ two_hundred_ohm - for antennas with an impedance of 200Ohm
+ lna_gain
+ sets the gain of the low noise amp
+ automatic - lna gain is determined by an agc
+ max - lna gain is set to maximum
+ max_minus_6 - lna gain is set to 6db below max
+ max_minus_12 - lna gain is set to 12db below max
+ max_minus_24 - lna gain is set to 24db below max
+ max_minus_36 - lna gain is set to 36db below max
+ max_minus_48 - lna gain is set to 48db below max
+ bw_mantisse
+ sets the bandwidth of the channel filter - part one: mantisse.
+ mantisse16 - mantisse is set to 16
+ mantisse20 - mantisse is set to 20
+ mantisse24 - mantisse is set to 24
+ bw_exponent
+ sets the bandwidth of the channel filter - part two: exponent.
+ Allowd values: 0...7
+ dagc;
+ operation mode of the digital automatic gain control
+ normal_mode
+ improve
+ improve_for_low_modulation_index
+
+ packet format:
+ enable_sync
+ optionOn - sync detection is enabled. If configured sync pattern
+ isn't found, telegram will be internally discarded
+ optionOff - sync detection is disabled.
+ enable_length_byte
+ optionOn - First byte of payload will be used as a length byte,
+ regardless of the amount of bytes that were requested
+ by the read request.
+ optionOff - Number of bytes to be read will be set according to
+ amount of bytes that were requested by the read request.
+ Attention: should be used in combination with sync, only
+ enable_address_filtering;
+ filtering_off - no address filtering will take place
+ node_address - all telegrams, not matching the node
+ address will be internally discarded
+ node_or_broadcast_address - all telegrams, neither matching the
+ node, nor the broadcast address will
+ be internally discarded
+ Attention: Sync option must be enabled in order to use this feature
+ enable_crc
+ optionOn - a crc will be calculated over the payload of
+ the telegram, that was received. If the
+ calculated crc doesn't match to two bytes,
+ that follow the payload, the telegram will be
+ internally discarded.
+ Attention: This option is only operational if sync on and fixed length
+ or length byte is used
+ sync_length
+ Gives the length of the payload.
+ Attention: This setting must meet the setting of the transmitter,
+ if sync option is used.
+ fixed_message_length
+ Overrides the telegram length either given by the first byte of
+ payload or by the read request.
+ bytes_to_drop
+ gives the number of bytes, that will be dropped before transferring
+ data to the read buffer
+ This option is only useful if all packet helper are switched
+ off and the rf chip is used in raw receiving mode. This may be
+ needed, if a telegram of a third party device should be received,
+ using a protocol not compatible with the packet engine of the rf69 chip.
+ sync_pattern[8]
+ contains up to eight values, that are used as the sync pattern
+ on sync option.
+ This setting must meet the configuration of the transmitting device,
+ if sync option is enabled.
+ node_address
+ one byte, used as node address byte on address byte option.
+ broadcast_address
+ one byte, used as broadcast address byte on address byte option.
+
+
diff --git a/drivers/staging/pi433/Kconfig b/drivers/staging/pi433/Kconfig
new file mode 100644
index 000000000..dd9e4709d
--- /dev/null
+++ b/drivers/staging/pi433/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+config PI433
+ tristate "Pi433 - a 433MHz radio module for Raspberry Pi"
+ depends on SPI
+ help
+ This option allows you to enable support for the radio module Pi433.
+
+ Pi433 is a shield that fits onto the GPIO header of a Raspberry Pi
+ or compatible. It extends the Raspberry Pi with the option, to
+ send and receive data in the 433MHz ISM band - for example to
+ communicate between two systems without using ethernet or bluetooth
+ or for control or read sockets, actors, sensors, widely available
+ for low price.
+
+ For details or the option to buy, please visit https://pi433.de/en.html
+
+ If in doubt, say N here, but saying yes most probably won't hurt
diff --git a/drivers/staging/pi433/Makefile b/drivers/staging/pi433/Makefile
new file mode 100644
index 000000000..051132fe4
--- /dev/null
+++ b/drivers/staging/pi433/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PI433) += pi433.o
+
+pi433-objs := pi433_if.o rf69.o
diff --git a/drivers/staging/pi433/TODO b/drivers/staging/pi433/TODO
new file mode 100644
index 000000000..5cf3fd99d
--- /dev/null
+++ b/drivers/staging/pi433/TODO
@@ -0,0 +1,5 @@
+* currently the code introduces new IOCTLs. I'm afraid this is a bad idea.
+ -> Replace this with another interface, hints are welcome!
+* Some missing data (marked with ###) needs to be added in the documentation
+* Change (struct pi433_tx_cfg)->bit_rate to be a u32 so that we can support
+ bit rates up to 300kbps per the spec.
diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c
new file mode 100644
index 000000000..b59f6a4cb
--- /dev/null
+++ b/drivers/staging/pi433/pi433_if.c
@@ -0,0 +1,1436 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * userspace interface for pi433 radio module
+ *
+ * Pi433 is a 433MHz radio module for the Raspberry Pi.
+ * It is based on the HopeRf Module RFM69CW. Therefore inside of this
+ * driver, you'll find an abstraction of the rf69 chip.
+ *
+ * If needed, this driver could be extended, to also support other
+ * devices, basing on HopeRfs rf69.
+ *
+ * The driver can also be extended, to support other modules of
+ * HopeRf with a similar interace - e. g. RFM69HCW, RFM12, RFM95, ...
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+
+#undef DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/kfifo.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/spi/spi.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "pi433_if.h"
+#include "rf69.h"
+
+#define N_PI433_MINORS BIT(MINORBITS) /*32*/ /* ... up to 256 */
+#define MAX_MSG_SIZE 900 /* min: FIFO_SIZE! */
+#define MSG_FIFO_SIZE 65536 /* 65536 = 2^16 */
+#define NUM_DIO 2
+
+static dev_t pi433_dev;
+static DEFINE_IDR(pi433_idr);
+static DEFINE_MUTEX(minor_lock); /* Protect idr accesses */
+static struct dentry *root_dir; /* debugfs root directory for the driver */
+
+static struct class *pi433_class; /* mainly for udev to create /dev/pi433 */
+
+/*
+ * tx config is instance specific
+ * so with each open a new tx config struct is needed
+ */
+/*
+ * rx config is device specific
+ * so we have just one rx config, ebedded in device struct
+ */
+struct pi433_device {
+ /* device handling related values */
+ dev_t devt;
+ int minor;
+ struct device *dev;
+ struct cdev *cdev;
+ struct spi_device *spi;
+
+ /* irq related values */
+ struct gpio_desc *gpiod[NUM_DIO];
+ int irq_num[NUM_DIO];
+ u8 irq_state[NUM_DIO];
+
+ /* tx related values */
+ STRUCT_KFIFO_REC_1(MSG_FIFO_SIZE) tx_fifo;
+ struct mutex tx_fifo_lock; /* serialize userspace writers */
+ struct task_struct *tx_task_struct;
+ wait_queue_head_t tx_wait_queue;
+ u8 free_in_fifo;
+ char buffer[MAX_MSG_SIZE];
+
+ /* rx related values */
+ struct pi433_rx_cfg rx_cfg;
+ u8 *rx_buffer;
+ unsigned int rx_buffer_size;
+ u32 rx_bytes_to_drop;
+ u32 rx_bytes_dropped;
+ unsigned int rx_position;
+ struct mutex rx_lock; /* protects rx_* variable accesses */
+ wait_queue_head_t rx_wait_queue;
+
+ /* fifo wait queue */
+ struct task_struct *fifo_task_struct;
+ wait_queue_head_t fifo_wait_queue;
+
+ /* flags */
+ bool rx_active;
+ bool tx_active;
+ bool interrupt_rx_allowed;
+};
+
+struct pi433_instance {
+ struct pi433_device *device;
+ struct pi433_tx_cfg tx_cfg;
+
+ /* control flags */
+ bool tx_cfg_initialized;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* GPIO interrupt handlers */
+static irqreturn_t DIO0_irq_handler(int irq, void *dev_id)
+{
+ struct pi433_device *device = dev_id;
+
+ if (device->irq_state[DIO0] == DIO_PACKET_SENT) {
+ device->free_in_fifo = FIFO_SIZE;
+ dev_dbg(device->dev, "DIO0 irq: Packet sent\n");
+ wake_up_interruptible(&device->fifo_wait_queue);
+ } else if (device->irq_state[DIO0] == DIO_RSSI_DIO0) {
+ dev_dbg(device->dev, "DIO0 irq: RSSI level over threshold\n");
+ wake_up_interruptible(&device->rx_wait_queue);
+ } else if (device->irq_state[DIO0] == DIO_PAYLOAD_READY) {
+ dev_dbg(device->dev, "DIO0 irq: Payload ready\n");
+ device->free_in_fifo = 0;
+ wake_up_interruptible(&device->fifo_wait_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t DIO1_irq_handler(int irq, void *dev_id)
+{
+ struct pi433_device *device = dev_id;
+
+ if (device->irq_state[DIO1] == DIO_FIFO_NOT_EMPTY_DIO1) {
+ device->free_in_fifo = FIFO_SIZE;
+ } else if (device->irq_state[DIO1] == DIO_FIFO_LEVEL) {
+ if (device->rx_active)
+ device->free_in_fifo = FIFO_THRESHOLD - 1;
+ else
+ device->free_in_fifo = FIFO_SIZE - FIFO_THRESHOLD - 1;
+ }
+ dev_dbg(device->dev,
+ "DIO1 irq: %d bytes free in fifo\n", device->free_in_fifo);
+ wake_up_interruptible(&device->fifo_wait_queue);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
+{
+ int ret;
+ int payload_length;
+
+ /* receiver config */
+ ret = rf69_set_frequency(dev->spi, rx_cfg->frequency);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_modulation(dev->spi, rx_cfg->modulation);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_bit_rate(dev->spi, rx_cfg->bit_rate);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_antenna_impedance(dev->spi, rx_cfg->antenna_impedance);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_rssi_threshold(dev->spi, rx_cfg->rssi_threshold);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_ook_threshold_dec(dev->spi, rx_cfg->threshold_decrement);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse,
+ rx_cfg->bw_exponent);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse,
+ rx_cfg->bw_exponent);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_dagc(dev->spi, rx_cfg->dagc);
+ if (ret < 0)
+ return ret;
+
+ dev->rx_bytes_to_drop = rx_cfg->bytes_to_drop;
+
+ /* packet config */
+ /* enable */
+ if (rx_cfg->enable_sync == OPTION_ON) {
+ ret = rf69_enable_sync(dev->spi);
+ if (ret < 0)
+ return ret;
+
+ ret = rf69_set_fifo_fill_condition(dev->spi,
+ after_sync_interrupt);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_disable_sync(dev->spi);
+ if (ret < 0)
+ return ret;
+
+ ret = rf69_set_fifo_fill_condition(dev->spi, always);
+ if (ret < 0)
+ return ret;
+ }
+ if (rx_cfg->enable_length_byte == OPTION_ON) {
+ ret = rf69_set_packet_format(dev->spi, packet_length_var);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_set_packet_format(dev->spi, packet_length_fix);
+ if (ret < 0)
+ return ret;
+ }
+ ret = rf69_set_address_filtering(dev->spi,
+ rx_cfg->enable_address_filtering);
+ if (ret < 0)
+ return ret;
+
+ if (rx_cfg->enable_crc == OPTION_ON) {
+ ret = rf69_enable_crc(dev->spi);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_disable_crc(dev->spi);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* lengths */
+ ret = rf69_set_sync_size(dev->spi, rx_cfg->sync_length);
+ if (ret < 0)
+ return ret;
+ if (rx_cfg->enable_length_byte == OPTION_ON) {
+ ret = rf69_set_payload_length(dev->spi, 0xff);
+ if (ret < 0)
+ return ret;
+ } else if (rx_cfg->fixed_message_length != 0) {
+ payload_length = rx_cfg->fixed_message_length;
+ if (rx_cfg->enable_length_byte == OPTION_ON)
+ payload_length++;
+ if (rx_cfg->enable_address_filtering != filtering_off)
+ payload_length++;
+ ret = rf69_set_payload_length(dev->spi, payload_length);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_set_payload_length(dev->spi, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* values */
+ if (rx_cfg->enable_sync == OPTION_ON) {
+ ret = rf69_set_sync_values(dev->spi, rx_cfg->sync_pattern);
+ if (ret < 0)
+ return ret;
+ }
+ if (rx_cfg->enable_address_filtering != filtering_off) {
+ ret = rf69_set_node_address(dev->spi, rx_cfg->node_address);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_broadcast_address(dev->spi,
+ rx_cfg->broadcast_address);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg)
+{
+ int ret;
+
+ ret = rf69_set_frequency(dev->spi, tx_cfg->frequency);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_modulation(dev->spi, tx_cfg->modulation);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_bit_rate(dev->spi, tx_cfg->bit_rate);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_deviation(dev->spi, tx_cfg->dev_frequency);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_pa_ramp(dev->spi, tx_cfg->pa_ramp);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_modulation_shaping(dev->spi, tx_cfg->mod_shaping);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_tx_start_condition(dev->spi, tx_cfg->tx_start_condition);
+ if (ret < 0)
+ return ret;
+
+ /* packet format enable */
+ if (tx_cfg->enable_preamble == OPTION_ON) {
+ ret = rf69_set_preamble_length(dev->spi,
+ tx_cfg->preamble_length);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_set_preamble_length(dev->spi, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (tx_cfg->enable_sync == OPTION_ON) {
+ ret = rf69_set_sync_size(dev->spi, tx_cfg->sync_length);
+ if (ret < 0)
+ return ret;
+ ret = rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern);
+ if (ret < 0)
+ return ret;
+ ret = rf69_enable_sync(dev->spi);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_disable_sync(dev->spi);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (tx_cfg->enable_length_byte == OPTION_ON) {
+ ret = rf69_set_packet_format(dev->spi, packet_length_var);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_set_packet_format(dev->spi, packet_length_fix);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (tx_cfg->enable_crc == OPTION_ON) {
+ ret = rf69_enable_crc(dev->spi);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = rf69_disable_crc(dev->spi);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pi433_start_rx(struct pi433_device *dev)
+{
+ int retval;
+
+ /* return without action, if no pending read request */
+ if (!dev->rx_active)
+ return 0;
+
+ /* setup for receiving */
+ retval = rf69_set_rx_cfg(dev, &dev->rx_cfg);
+ if (retval)
+ return retval;
+
+ /* setup rssi irq */
+ retval = rf69_set_dio_mapping(dev->spi, DIO0, DIO_RSSI_DIO0);
+ if (retval < 0)
+ return retval;
+ dev->irq_state[DIO0] = DIO_RSSI_DIO0;
+ irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING);
+
+ /* setup fifo level interrupt */
+ retval = rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD);
+ if (retval < 0)
+ return retval;
+ retval = rf69_set_dio_mapping(dev->spi, DIO1, DIO_FIFO_LEVEL);
+ if (retval < 0)
+ return retval;
+ dev->irq_state[DIO1] = DIO_FIFO_LEVEL;
+ irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING);
+
+ /* set module to receiving mode */
+ retval = rf69_set_mode(dev->spi, receive);
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pi433_receive(void *data)
+{
+ struct pi433_device *dev = data;
+ struct spi_device *spi = dev->spi;
+ int bytes_to_read, bytes_total;
+ int retval;
+
+ dev->interrupt_rx_allowed = false;
+
+ /* wait for any tx to finish */
+ dev_dbg(dev->dev, "rx: going to wait for any tx to finish\n");
+ retval = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active);
+ if (retval) {
+ /* wait was interrupted */
+ dev->interrupt_rx_allowed = true;
+ wake_up_interruptible(&dev->tx_wait_queue);
+ return retval;
+ }
+
+ /* prepare status vars */
+ dev->free_in_fifo = FIFO_SIZE;
+ dev->rx_position = 0;
+ dev->rx_bytes_dropped = 0;
+
+ /* setup radio module to listen for something "in the air" */
+ retval = pi433_start_rx(dev);
+ if (retval)
+ return retval;
+
+ /* now check RSSI, if low wait for getting high (RSSI interrupt) */
+ while (!(rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_RSSI)) {
+ /* allow tx to interrupt us while waiting for high RSSI */
+ dev->interrupt_rx_allowed = true;
+ wake_up_interruptible(&dev->tx_wait_queue);
+
+ /* wait for RSSI level to become high */
+ dev_dbg(dev->dev, "rx: going to wait for high RSSI level\n");
+ retval = wait_event_interruptible(dev->rx_wait_queue,
+ rf69_read_reg(spi, REG_IRQFLAGS1) &
+ MASK_IRQFLAGS1_RSSI);
+ if (retval) /* wait was interrupted */
+ goto abort;
+ dev->interrupt_rx_allowed = false;
+
+ /* cross check for ongoing tx */
+ if (!dev->tx_active)
+ break;
+ }
+
+ /* configure payload ready irq */
+ retval = rf69_set_dio_mapping(spi, DIO0, DIO_PAYLOAD_READY);
+ if (retval < 0)
+ goto abort;
+ dev->irq_state[DIO0] = DIO_PAYLOAD_READY;
+ irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING);
+
+ /* fixed or unlimited length? */
+ if (dev->rx_cfg.fixed_message_length != 0) {
+ if (dev->rx_cfg.fixed_message_length > dev->rx_buffer_size) {
+ retval = -1;
+ goto abort;
+ }
+ bytes_total = dev->rx_cfg.fixed_message_length;
+ dev_dbg(dev->dev, "rx: msg len set to %d by fixed length\n",
+ bytes_total);
+ } else {
+ bytes_total = dev->rx_buffer_size;
+ dev_dbg(dev->dev, "rx: msg len set to %d as requested by read\n",
+ bytes_total);
+ }
+
+ /* length byte enabled? */
+ if (dev->rx_cfg.enable_length_byte == OPTION_ON) {
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) /* wait was interrupted */
+ goto abort;
+
+ rf69_read_fifo(spi, (u8 *)&bytes_total, 1);
+ if (bytes_total > dev->rx_buffer_size) {
+ retval = -1;
+ goto abort;
+ }
+ dev->free_in_fifo++;
+ dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte\n",
+ bytes_total);
+ }
+
+ /* address byte enabled? */
+ if (dev->rx_cfg.enable_address_filtering != filtering_off) {
+ u8 dummy;
+
+ bytes_total--;
+
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) /* wait was interrupted */
+ goto abort;
+
+ rf69_read_fifo(spi, &dummy, 1);
+ dev->free_in_fifo++;
+ dev_dbg(dev->dev, "rx: address byte stripped off\n");
+ }
+
+ /* get payload */
+ while (dev->rx_position < bytes_total) {
+ if (!(rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_PAYLOAD_READY)) {
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) /* wait was interrupted */
+ goto abort;
+ }
+
+ /* need to drop bytes or acquire? */
+ if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped)
+ bytes_to_read = dev->rx_bytes_to_drop -
+ dev->rx_bytes_dropped;
+ else
+ bytes_to_read = bytes_total - dev->rx_position;
+
+ /* access the fifo */
+ if (bytes_to_read > FIFO_SIZE - dev->free_in_fifo)
+ bytes_to_read = FIFO_SIZE - dev->free_in_fifo;
+ retval = rf69_read_fifo(spi,
+ &dev->rx_buffer[dev->rx_position],
+ bytes_to_read);
+ if (retval) /* read failed */
+ goto abort;
+
+ dev->free_in_fifo += bytes_to_read;
+
+ /* adjust status vars */
+ if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped)
+ dev->rx_bytes_dropped += bytes_to_read;
+ else
+ dev->rx_position += bytes_to_read;
+ }
+
+ /* rx done, wait was interrupted or error occurred */
+abort:
+ dev->interrupt_rx_allowed = true;
+ if (rf69_set_mode(dev->spi, standby))
+ pr_err("rf69_set_mode(): radio module failed to go standby\n");
+ wake_up_interruptible(&dev->tx_wait_queue);
+
+ if (retval)
+ return retval;
+ else
+ return bytes_total;
+}
+
+static int pi433_tx_thread(void *data)
+{
+ struct pi433_device *device = data;
+ struct spi_device *spi = device->spi;
+ struct pi433_tx_cfg tx_cfg;
+ size_t size;
+ bool rx_interrupted = false;
+ int position, repetitions;
+ int retval;
+
+ while (1) {
+ /* wait for fifo to be populated or for request to terminate*/
+ dev_dbg(device->dev, "thread: going to wait for new messages\n");
+ wait_event_interruptible(device->tx_wait_queue,
+ (!kfifo_is_empty(&device->tx_fifo) ||
+ kthread_should_stop()));
+ if (kthread_should_stop())
+ return 0;
+
+ /*
+ * get data from fifo in the following order:
+ * - tx_cfg
+ * - size of message
+ * - message
+ */
+ retval = kfifo_out(&device->tx_fifo, &tx_cfg, sizeof(tx_cfg));
+ if (retval != sizeof(tx_cfg)) {
+ dev_dbg(device->dev,
+ "reading tx_cfg from fifo failed: got %d byte(s), expected %d\n",
+ retval, (unsigned int)sizeof(tx_cfg));
+ continue;
+ }
+
+ retval = kfifo_out(&device->tx_fifo, &size, sizeof(size_t));
+ if (retval != sizeof(size_t)) {
+ dev_dbg(device->dev,
+ "reading msg size from fifo failed: got %d, expected %d\n",
+ retval, (unsigned int)sizeof(size_t));
+ continue;
+ }
+
+ /* use fixed message length, if requested */
+ if (tx_cfg.fixed_message_length != 0)
+ size = tx_cfg.fixed_message_length;
+
+ /* increase size, if len byte is requested */
+ if (tx_cfg.enable_length_byte == OPTION_ON)
+ size++;
+
+ /* increase size, if adr byte is requested */
+ if (tx_cfg.enable_address_byte == OPTION_ON)
+ size++;
+
+ /* prime buffer */
+ memset(device->buffer, 0, size);
+ position = 0;
+
+ /* add length byte, if requested */
+ if (tx_cfg.enable_length_byte == OPTION_ON)
+ /*
+ * according to spec, length byte itself must be
+ * excluded from the length calculation
+ */
+ device->buffer[position++] = size - 1;
+
+ /* add adr byte, if requested */
+ if (tx_cfg.enable_address_byte == OPTION_ON)
+ device->buffer[position++] = tx_cfg.address_byte;
+
+ /* finally get message data from fifo */
+ retval = kfifo_out(&device->tx_fifo, &device->buffer[position],
+ sizeof(device->buffer) - position);
+ dev_dbg(device->dev,
+ "read %d message byte(s) from fifo queue.\n", retval);
+
+ /*
+ * if rx is active, we need to interrupt the waiting for
+ * incoming telegrams, to be able to send something.
+ * We are only allowed, if currently no reception takes
+ * place otherwise we need to wait for the incoming telegram
+ * to finish
+ */
+ wait_event_interruptible(device->tx_wait_queue,
+ !device->rx_active ||
+ device->interrupt_rx_allowed);
+
+ /*
+ * prevent race conditions
+ * irq will be reenabled after tx config is set
+ */
+ disable_irq(device->irq_num[DIO0]);
+ device->tx_active = true;
+
+ /* clear fifo, set fifo threshold, set payload length */
+ retval = rf69_set_mode(spi, standby); /* this clears the fifo */
+ if (retval < 0)
+ goto abort;
+
+ if (device->rx_active && !rx_interrupted) {
+ /*
+ * rx is currently waiting for a telegram;
+ * we need to set the radio module to standby
+ */
+ rx_interrupted = true;
+ }
+
+ retval = rf69_set_fifo_threshold(spi, FIFO_THRESHOLD);
+ if (retval < 0)
+ goto abort;
+ if (tx_cfg.enable_length_byte == OPTION_ON) {
+ retval = rf69_set_payload_length(spi, size * tx_cfg.repetitions);
+ if (retval < 0)
+ goto abort;
+ } else {
+ retval = rf69_set_payload_length(spi, 0);
+ if (retval < 0)
+ goto abort;
+ }
+
+ /* configure the rf chip */
+ retval = rf69_set_tx_cfg(device, &tx_cfg);
+ if (retval < 0)
+ goto abort;
+
+ /* enable fifo level interrupt */
+ retval = rf69_set_dio_mapping(spi, DIO1, DIO_FIFO_LEVEL);
+ if (retval < 0)
+ goto abort;
+ device->irq_state[DIO1] = DIO_FIFO_LEVEL;
+ irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING);
+
+ /* enable packet sent interrupt */
+ retval = rf69_set_dio_mapping(spi, DIO0, DIO_PACKET_SENT);
+ if (retval < 0)
+ goto abort;
+ device->irq_state[DIO0] = DIO_PACKET_SENT;
+ irq_set_irq_type(device->irq_num[DIO0], IRQ_TYPE_EDGE_RISING);
+ enable_irq(device->irq_num[DIO0]); /* was disabled by rx active check */
+
+ /* enable transmission */
+ retval = rf69_set_mode(spi, transmit);
+ if (retval < 0)
+ goto abort;
+
+ /* transfer this msg (and repetitions) to chip fifo */
+ device->free_in_fifo = FIFO_SIZE;
+ position = 0;
+ repetitions = tx_cfg.repetitions;
+ while ((repetitions > 0) && (size > position)) {
+ if ((size - position) > device->free_in_fifo) {
+ /* msg to big for fifo - take a part */
+ int write_size = device->free_in_fifo;
+
+ device->free_in_fifo = 0;
+ rf69_write_fifo(spi,
+ &device->buffer[position],
+ write_size);
+ position += write_size;
+ } else {
+ /* msg fits into fifo - take all */
+ device->free_in_fifo -= size;
+ repetitions--;
+ rf69_write_fifo(spi,
+ &device->buffer[position],
+ (size - position));
+ position = 0; /* reset for next repetition */
+ }
+
+ retval = wait_event_interruptible(device->fifo_wait_queue,
+ device->free_in_fifo > 0);
+ if (retval) {
+ dev_dbg(device->dev, "ABORT\n");
+ goto abort;
+ }
+ }
+
+ /* we are done. Wait for packet to get sent */
+ dev_dbg(device->dev,
+ "thread: wait for packet to get sent/fifo to be empty\n");
+ wait_event_interruptible(device->fifo_wait_queue,
+ device->free_in_fifo == FIFO_SIZE ||
+ kthread_should_stop());
+ if (kthread_should_stop())
+ return 0;
+
+ /* STOP_TRANSMISSION */
+ dev_dbg(device->dev, "thread: Packet sent. Set mode to stby.\n");
+ retval = rf69_set_mode(spi, standby);
+ if (retval < 0)
+ goto abort;
+
+ /* everything sent? */
+ if (kfifo_is_empty(&device->tx_fifo)) {
+abort:
+ if (rx_interrupted) {
+ rx_interrupted = false;
+ pi433_start_rx(device);
+ }
+ device->tx_active = false;
+ wake_up_interruptible(&device->rx_wait_queue);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static ssize_t
+pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
+{
+ struct pi433_instance *instance;
+ struct pi433_device *device;
+ int bytes_received;
+ ssize_t retval;
+
+ /* check, whether internal buffer is big enough for requested size */
+ if (size > MAX_MSG_SIZE)
+ return -EMSGSIZE;
+
+ instance = filp->private_data;
+ device = instance->device;
+
+ /* just one read request at a time */
+ mutex_lock(&device->rx_lock);
+ if (device->rx_active) {
+ mutex_unlock(&device->rx_lock);
+ return -EAGAIN;
+ }
+
+ device->rx_active = true;
+ mutex_unlock(&device->rx_lock);
+
+ /* start receiving */
+ /* will block until something was received*/
+ device->rx_buffer_size = size;
+ bytes_received = pi433_receive(device);
+
+ /* release rx */
+ mutex_lock(&device->rx_lock);
+ device->rx_active = false;
+ mutex_unlock(&device->rx_lock);
+
+ /* if read was successful copy to user space*/
+ if (bytes_received > 0) {
+ retval = copy_to_user(buf, device->rx_buffer, bytes_received);
+ if (retval)
+ return -EFAULT;
+ }
+
+ return bytes_received;
+}
+
+static ssize_t
+pi433_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct pi433_instance *instance;
+ struct pi433_device *device;
+ int retval;
+ unsigned int required, available, copied;
+
+ instance = filp->private_data;
+ device = instance->device;
+
+ /*
+ * check, whether internal buffer (tx thread) is big enough
+ * for requested size
+ */
+ if (count > MAX_MSG_SIZE)
+ return -EMSGSIZE;
+
+ /*
+ * check if tx_cfg has been initialized otherwise we won't be able to
+ * config the RF trasmitter correctly due to invalid settings
+ */
+ if (!instance->tx_cfg_initialized) {
+ dev_notice_once(device->dev,
+ "write: failed due to unconfigured tx_cfg (see PI433_IOC_WR_TX_CFG)\n");
+ return -EINVAL;
+ }
+
+ /*
+ * write the following sequence into fifo:
+ * - tx_cfg
+ * - size of message
+ * - message
+ */
+ mutex_lock(&device->tx_fifo_lock);
+
+ required = sizeof(instance->tx_cfg) + sizeof(size_t) + count;
+ available = kfifo_avail(&device->tx_fifo);
+ if (required > available) {
+ dev_dbg(device->dev, "write to fifo failed: %d bytes required but %d available\n",
+ required, available);
+ mutex_unlock(&device->tx_fifo_lock);
+ return -EAGAIN;
+ }
+
+ retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg,
+ sizeof(instance->tx_cfg));
+ if (retval != sizeof(instance->tx_cfg))
+ goto abort;
+
+ retval = kfifo_in(&device->tx_fifo, &count, sizeof(size_t));
+ if (retval != sizeof(size_t))
+ goto abort;
+
+ retval = kfifo_from_user(&device->tx_fifo, buf, count, &copied);
+ if (retval || copied != count)
+ goto abort;
+
+ mutex_unlock(&device->tx_fifo_lock);
+
+ /* start transfer */
+ wake_up_interruptible(&device->tx_wait_queue);
+ dev_dbg(device->dev, "write: generated new msg with %d bytes.\n", copied);
+
+ return copied;
+
+abort:
+ dev_warn(device->dev,
+ "write to fifo failed, non recoverable: 0x%x\n", retval);
+ mutex_unlock(&device->tx_fifo_lock);
+ return -EAGAIN;
+}
+
+static long pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct pi433_instance *instance;
+ struct pi433_device *device;
+ struct pi433_tx_cfg tx_cfg;
+ void __user *argp = (void __user *)arg;
+
+ /* Check type and command number */
+ if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC)
+ return -ENOTTY;
+
+ instance = filp->private_data;
+ device = instance->device;
+
+ if (!device)
+ return -ESHUTDOWN;
+
+ switch (cmd) {
+ case PI433_IOC_RD_TX_CFG:
+ if (copy_to_user(argp, &instance->tx_cfg,
+ sizeof(struct pi433_tx_cfg)))
+ return -EFAULT;
+ break;
+ case PI433_IOC_WR_TX_CFG:
+ if (copy_from_user(&tx_cfg, argp, sizeof(struct pi433_tx_cfg)))
+ return -EFAULT;
+ mutex_lock(&device->tx_fifo_lock);
+ memcpy(&instance->tx_cfg, &tx_cfg, sizeof(struct pi433_tx_cfg));
+ instance->tx_cfg_initialized = true;
+ mutex_unlock(&device->tx_fifo_lock);
+ break;
+ case PI433_IOC_RD_RX_CFG:
+ if (copy_to_user(argp, &device->rx_cfg,
+ sizeof(struct pi433_rx_cfg)))
+ return -EFAULT;
+ break;
+ case PI433_IOC_WR_RX_CFG:
+ mutex_lock(&device->rx_lock);
+
+ /* during pendig read request, change of config not allowed */
+ if (device->rx_active) {
+ mutex_unlock(&device->rx_lock);
+ return -EAGAIN;
+ }
+
+ if (copy_from_user(&device->rx_cfg, argp,
+ sizeof(struct pi433_rx_cfg))) {
+ mutex_unlock(&device->rx_lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&device->rx_lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pi433_open(struct inode *inode, struct file *filp)
+{
+ struct pi433_device *device;
+ struct pi433_instance *instance;
+
+ mutex_lock(&minor_lock);
+ device = idr_find(&pi433_idr, iminor(inode));
+ mutex_unlock(&minor_lock);
+ if (!device) {
+ pr_debug("device: minor %d unknown.\n", iminor(inode));
+ return -ENODEV;
+ }
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance)
+ return -ENOMEM;
+
+ /* setup instance data*/
+ instance->device = device;
+
+ /* instance data as context */
+ filp->private_data = instance;
+ stream_open(inode, filp);
+
+ return 0;
+}
+
+static int pi433_release(struct inode *inode, struct file *filp)
+{
+ struct pi433_instance *instance;
+
+ instance = filp->private_data;
+ kfree(instance);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int setup_gpio(struct pi433_device *device)
+{
+ char name[5];
+ int retval;
+ int i;
+ const irq_handler_t DIO_irq_handler[NUM_DIO] = {
+ DIO0_irq_handler,
+ DIO1_irq_handler
+ };
+
+ for (i = 0; i < NUM_DIO; i++) {
+ /* "construct" name and get the gpio descriptor */
+ snprintf(name, sizeof(name), "DIO%d", i);
+ device->gpiod[i] = gpiod_get(&device->spi->dev, name,
+ 0 /*GPIOD_IN*/);
+
+ if (device->gpiod[i] == ERR_PTR(-ENOENT)) {
+ dev_dbg(&device->spi->dev,
+ "Could not find entry for %s. Ignoring.\n", name);
+ continue;
+ }
+
+ if (device->gpiod[i] == ERR_PTR(-EBUSY))
+ dev_dbg(&device->spi->dev, "%s is busy.\n", name);
+
+ if (IS_ERR(device->gpiod[i])) {
+ retval = PTR_ERR(device->gpiod[i]);
+ /* release already allocated gpios */
+ for (i--; i >= 0; i--) {
+ free_irq(device->irq_num[i], device);
+ gpiod_put(device->gpiod[i]);
+ }
+ return retval;
+ }
+
+ /* configure the pin */
+ gpiod_unexport(device->gpiod[i]);
+ retval = gpiod_direction_input(device->gpiod[i]);
+ if (retval)
+ return retval;
+
+ /* configure irq */
+ device->irq_num[i] = gpiod_to_irq(device->gpiod[i]);
+ if (device->irq_num[i] < 0) {
+ device->gpiod[i] = ERR_PTR(-EINVAL);
+ return device->irq_num[i];
+ }
+ retval = request_irq(device->irq_num[i],
+ DIO_irq_handler[i],
+ 0, /* flags */
+ name,
+ device);
+
+ if (retval)
+ return retval;
+
+ dev_dbg(&device->spi->dev, "%s successfully configured\n", name);
+ }
+
+ return 0;
+}
+
+static void free_gpio(struct pi433_device *device)
+{
+ int i;
+
+ for (i = 0; i < NUM_DIO; i++) {
+ /* check if gpiod is valid */
+ if (IS_ERR(device->gpiod[i]))
+ continue;
+
+ free_irq(device->irq_num[i], device);
+ gpiod_put(device->gpiod[i]);
+ }
+}
+
+static int pi433_get_minor(struct pi433_device *device)
+{
+ int retval = -ENOMEM;
+
+ mutex_lock(&minor_lock);
+ retval = idr_alloc(&pi433_idr, device, 0, N_PI433_MINORS, GFP_KERNEL);
+ if (retval >= 0) {
+ device->minor = retval;
+ retval = 0;
+ } else if (retval == -ENOSPC) {
+ dev_err(&device->spi->dev, "too many pi433 devices\n");
+ retval = -EINVAL;
+ }
+ mutex_unlock(&minor_lock);
+ return retval;
+}
+
+static void pi433_free_minor(struct pi433_device *dev)
+{
+ mutex_lock(&minor_lock);
+ idr_remove(&pi433_idr, dev->minor);
+ mutex_unlock(&minor_lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct file_operations pi433_fops = {
+ .owner = THIS_MODULE,
+ /*
+ * REVISIT switch to aio primitives, so that userspace
+ * gets more complete API coverage. It'll simplify things
+ * too, except for the locking.
+ */
+ .write = pi433_write,
+ .read = pi433_read,
+ .unlocked_ioctl = pi433_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .open = pi433_open,
+ .release = pi433_release,
+ .llseek = no_llseek,
+};
+
+static int pi433_debugfs_regs_show(struct seq_file *m, void *p)
+{
+ struct pi433_device *dev;
+ u8 reg_data[114];
+ int i;
+ char *fmt = "0x%02x, 0x%02x\n";
+ int ret;
+
+ dev = m->private;
+
+ mutex_lock(&dev->tx_fifo_lock);
+ mutex_lock(&dev->rx_lock);
+
+ // wait for on-going operations to finish
+ ret = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active);
+ if (ret)
+ goto out_unlock;
+
+ ret = wait_event_interruptible(dev->tx_wait_queue, !dev->rx_active);
+ if (ret)
+ goto out_unlock;
+
+ // skip FIFO register (0x0) otherwise this can affect some of uC ops
+ for (i = 1; i < 0x50; i++)
+ reg_data[i] = rf69_read_reg(dev->spi, i);
+
+ reg_data[REG_TESTLNA] = rf69_read_reg(dev->spi, REG_TESTLNA);
+ reg_data[REG_TESTPA1] = rf69_read_reg(dev->spi, REG_TESTPA1);
+ reg_data[REG_TESTPA2] = rf69_read_reg(dev->spi, REG_TESTPA2);
+ reg_data[REG_TESTDAGC] = rf69_read_reg(dev->spi, REG_TESTDAGC);
+ reg_data[REG_TESTAFC] = rf69_read_reg(dev->spi, REG_TESTAFC);
+
+ seq_puts(m, "# reg, val\n");
+
+ for (i = 1; i < 0x50; i++)
+ seq_printf(m, fmt, i, reg_data[i]);
+
+ seq_printf(m, fmt, REG_TESTLNA, reg_data[REG_TESTLNA]);
+ seq_printf(m, fmt, REG_TESTPA1, reg_data[REG_TESTPA1]);
+ seq_printf(m, fmt, REG_TESTPA2, reg_data[REG_TESTPA2]);
+ seq_printf(m, fmt, REG_TESTDAGC, reg_data[REG_TESTDAGC]);
+ seq_printf(m, fmt, REG_TESTAFC, reg_data[REG_TESTAFC]);
+
+out_unlock:
+ mutex_unlock(&dev->rx_lock);
+ mutex_unlock(&dev->tx_fifo_lock);
+
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(pi433_debugfs_regs);
+
+/*-------------------------------------------------------------------------*/
+
+static int pi433_probe(struct spi_device *spi)
+{
+ struct pi433_device *device;
+ int retval;
+ struct dentry *entry;
+
+ /* setup spi parameters */
+ spi->mode = 0x00;
+ spi->bits_per_word = 8;
+ /*
+ * spi->max_speed_hz = 10000000;
+ * 1MHz already set by device tree overlay
+ */
+
+ retval = spi_setup(spi);
+ if (retval) {
+ dev_dbg(&spi->dev, "configuration of SPI interface failed!\n");
+ return retval;
+ }
+
+ dev_dbg(&spi->dev,
+ "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed\n",
+ spi->mode, spi->bits_per_word, spi->max_speed_hz);
+
+ /* read chip version */
+ retval = rf69_get_version(spi);
+ if (retval < 0)
+ return retval;
+
+ switch (retval) {
+ case 0x24:
+ dev_dbg(&spi->dev, "found pi433 (ver. 0x%x)\n", retval);
+ break;
+ default:
+ dev_dbg(&spi->dev, "unknown chip version: 0x%x\n", retval);
+ return -ENODEV;
+ }
+
+ /* Allocate driver data */
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ /* Initialize the driver data */
+ device->spi = spi;
+ device->rx_active = false;
+ device->tx_active = false;
+ device->interrupt_rx_allowed = false;
+
+ /* init rx buffer */
+ device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL);
+ if (!device->rx_buffer) {
+ retval = -ENOMEM;
+ goto RX_failed;
+ }
+
+ /* init wait queues */
+ init_waitqueue_head(&device->tx_wait_queue);
+ init_waitqueue_head(&device->rx_wait_queue);
+ init_waitqueue_head(&device->fifo_wait_queue);
+
+ /* init fifo */
+ INIT_KFIFO(device->tx_fifo);
+
+ /* init mutexes and locks */
+ mutex_init(&device->tx_fifo_lock);
+ mutex_init(&device->rx_lock);
+
+ /* setup GPIO (including irq_handler) for the different DIOs */
+ retval = setup_gpio(device);
+ if (retval) {
+ dev_dbg(&spi->dev, "setup of GPIOs failed\n");
+ goto GPIO_failed;
+ }
+
+ /* setup the radio module */
+ retval = rf69_set_mode(spi, standby);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_set_data_mode(spi, DATAMODUL_MODE_PACKET);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_enable_amplifier(spi, MASK_PALEVEL_PA0);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA1);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA2);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_set_output_power_level(spi, 13);
+ if (retval < 0)
+ goto minor_failed;
+ retval = rf69_set_antenna_impedance(spi, fifty_ohm);
+ if (retval < 0)
+ goto minor_failed;
+
+ /* determ minor number */
+ retval = pi433_get_minor(device);
+ if (retval) {
+ dev_dbg(&spi->dev, "get of minor number failed\n");
+ goto minor_failed;
+ }
+
+ /* create device */
+ device->devt = MKDEV(MAJOR(pi433_dev), device->minor);
+ device->dev = device_create(pi433_class,
+ &spi->dev,
+ device->devt,
+ device,
+ "pi433.%d",
+ device->minor);
+ if (IS_ERR(device->dev)) {
+ pr_err("pi433: device register failed\n");
+ retval = PTR_ERR(device->dev);
+ goto device_create_failed;
+ } else {
+ dev_dbg(device->dev,
+ "created device for major %d, minor %d\n",
+ MAJOR(pi433_dev),
+ device->minor);
+ }
+
+ /* start tx thread */
+ device->tx_task_struct = kthread_run(pi433_tx_thread,
+ device,
+ "pi433.%d_tx_task",
+ device->minor);
+ if (IS_ERR(device->tx_task_struct)) {
+ dev_dbg(device->dev, "start of send thread failed\n");
+ retval = PTR_ERR(device->tx_task_struct);
+ goto send_thread_failed;
+ }
+
+ /* create cdev */
+ device->cdev = cdev_alloc();
+ if (!device->cdev) {
+ dev_dbg(device->dev, "allocation of cdev failed\n");
+ retval = -ENOMEM;
+ goto cdev_failed;
+ }
+ device->cdev->owner = THIS_MODULE;
+ cdev_init(device->cdev, &pi433_fops);
+ retval = cdev_add(device->cdev, device->devt, 1);
+ if (retval) {
+ dev_dbg(device->dev, "register of cdev failed\n");
+ goto del_cdev;
+ }
+
+ /* spi setup */
+ spi_set_drvdata(spi, device);
+
+ entry = debugfs_create_dir(dev_name(device->dev), root_dir);
+ debugfs_create_file("regs", 0400, entry, device, &pi433_debugfs_regs_fops);
+
+ return 0;
+
+del_cdev:
+ cdev_del(device->cdev);
+cdev_failed:
+ kthread_stop(device->tx_task_struct);
+send_thread_failed:
+ device_destroy(pi433_class, device->devt);
+device_create_failed:
+ pi433_free_minor(device);
+minor_failed:
+ free_gpio(device);
+GPIO_failed:
+ kfree(device->rx_buffer);
+RX_failed:
+ kfree(device);
+
+ return retval;
+}
+
+static void pi433_remove(struct spi_device *spi)
+{
+ struct pi433_device *device = spi_get_drvdata(spi);
+
+ debugfs_lookup_and_remove(dev_name(device->dev), root_dir);
+
+ /* free GPIOs */
+ free_gpio(device);
+
+ /* make sure ops on existing fds can abort cleanly */
+ device->spi = NULL;
+
+ kthread_stop(device->tx_task_struct);
+
+ device_destroy(pi433_class, device->devt);
+
+ cdev_del(device->cdev);
+
+ pi433_free_minor(device);
+
+ kfree(device->rx_buffer);
+ kfree(device);
+}
+
+static const struct of_device_id pi433_dt_ids[] = {
+ { .compatible = "Smarthome-Wolf,pi433" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, pi433_dt_ids);
+
+static struct spi_driver pi433_spi_driver = {
+ .driver = {
+ .name = "pi433",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pi433_dt_ids),
+ },
+ .probe = pi433_probe,
+ .remove = pi433_remove,
+
+ /*
+ * NOTE: suspend/resume methods are not necessary here.
+ * We don't do anything except pass the requests to/from
+ * the underlying controller. The refrigerator handles
+ * most issues; the controller driver handles the rest.
+ */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init pi433_init(void)
+{
+ int status;
+
+ /*
+ * If MAX_MSG_SIZE is smaller then FIFO_SIZE, the driver won't
+ * work stable - risk of buffer overflow
+ */
+ if (MAX_MSG_SIZE < FIFO_SIZE)
+ return -EINVAL;
+
+ /*
+ * Claim device numbers. Then register a class
+ * that will key udev/mdev to add/remove /dev nodes.
+ * Last, register the driver which manages those device numbers.
+ */
+ status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433");
+ if (status < 0)
+ return status;
+
+ pi433_class = class_create(THIS_MODULE, "pi433");
+ if (IS_ERR(pi433_class)) {
+ unregister_chrdev(MAJOR(pi433_dev),
+ pi433_spi_driver.driver.name);
+ return PTR_ERR(pi433_class);
+ }
+
+ root_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+
+ status = spi_register_driver(&pi433_spi_driver);
+ if (status < 0) {
+ class_destroy(pi433_class);
+ unregister_chrdev(MAJOR(pi433_dev),
+ pi433_spi_driver.driver.name);
+ }
+
+ return status;
+}
+
+module_init(pi433_init);
+
+static void __exit pi433_exit(void)
+{
+ spi_unregister_driver(&pi433_spi_driver);
+ class_destroy(pi433_class);
+ unregister_chrdev(MAJOR(pi433_dev), pi433_spi_driver.driver.name);
+ debugfs_remove(root_dir);
+}
+module_exit(pi433_exit);
+
+MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>");
+MODULE_DESCRIPTION("Driver for Pi433");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:pi433");
diff --git a/drivers/staging/pi433/pi433_if.h b/drivers/staging/pi433/pi433_if.h
new file mode 100644
index 000000000..25ee0b77a
--- /dev/null
+++ b/drivers/staging/pi433/pi433_if.h
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * userspace interface for pi433 radio module
+ *
+ * Pi433 is a 433MHz radio module for the Raspberry Pi.
+ * It is based on the HopeRf Module RFM69CW. Therefore, inside of this
+ * driver you'll find an abstraction of the rf69 chip.
+ *
+ * If needed this driver could also be extended to support other
+ * devices based on HopeRf rf69 as well as HopeRf modules with a similar
+ * interface such as RFM69HCW, RFM12, RFM95 and so on.
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+
+#ifndef PI433_H
+#define PI433_H
+
+#include <linux/types.h>
+#include "rf69_enum.h"
+
+/*---------------------------------------------------------------------------*/
+
+enum option_on_off {
+ OPTION_OFF,
+ OPTION_ON
+};
+
+/* IOCTL structs and commands */
+
+/**
+ * struct pi433_tx_cfg
+ * describes the configuration of the radio module for sending data
+ * @frequency:
+ * @bit_rate:
+ * @modulation:
+ * @data_mode:
+ * @preamble_length:
+ * @sync_pattern:
+ * @tx_start_condition:
+ * @payload_length:
+ * @repetitions:
+ *
+ * ATTENTION:
+ * If the contents of 'pi433_tx_cfg' ever change
+ * incompatibly, then the ioctl number (see define below) must change.
+ *
+ * NOTE: struct layout is the same in 64bit and 32bit userspace.
+ */
+#define PI433_TX_CFG_IOCTL_NR 0
+struct pi433_tx_cfg {
+ __u32 frequency;
+ __u16 bit_rate;
+ __u32 dev_frequency;
+ enum modulation modulation;
+ enum mod_shaping mod_shaping;
+
+ enum pa_ramp pa_ramp;
+
+ enum tx_start_condition tx_start_condition;
+
+ __u16 repetitions;
+
+ /* packet format */
+ enum option_on_off enable_preamble;
+ enum option_on_off enable_sync;
+ enum option_on_off enable_length_byte;
+ enum option_on_off enable_address_byte;
+ enum option_on_off enable_crc;
+
+ __u16 preamble_length;
+ __u8 sync_length;
+ __u8 fixed_message_length;
+
+ __u8 sync_pattern[8];
+ __u8 address_byte;
+};
+
+/**
+ * struct pi433_rx_cfg
+ * describes the configuration of the radio module for receiving data
+ * @frequency:
+ * @bit_rate:
+ * @modulation:
+ * @data_mode:
+ * @preamble_length:
+ * @sync_pattern:
+ * @tx_start_condition:
+ * @payload_length:
+ * @repetitions:
+ *
+ * ATTENTION:
+ * If the contents of 'pi433_rx_cfg' ever change
+ * incompatibly, then the ioctl number (see define below) must change
+ *
+ * NOTE: struct layout is the same in 64bit and 32bit userspace.
+ */
+#define PI433_RX_CFG_IOCTL_NR 1
+struct pi433_rx_cfg {
+ __u32 frequency;
+ __u16 bit_rate;
+ __u32 dev_frequency;
+
+ enum modulation modulation;
+
+ __u8 rssi_threshold;
+ enum threshold_decrement threshold_decrement;
+ enum antenna_impedance antenna_impedance;
+ enum lna_gain lna_gain;
+ enum mantisse bw_mantisse; /* normal: 0x50 */
+ __u8 bw_exponent; /* during AFC: 0x8b */
+ enum dagc dagc;
+
+ /* packet format */
+ enum option_on_off enable_sync;
+
+ /* should be used in combination with sync, only */
+ enum option_on_off enable_length_byte;
+
+ /* operational with sync, only */
+ enum address_filtering enable_address_filtering;
+
+ /* only operational, if sync on and fixed length or length byte is used */
+ enum option_on_off enable_crc;
+
+ __u8 sync_length;
+ __u8 fixed_message_length;
+ __u32 bytes_to_drop;
+
+ __u8 sync_pattern[8];
+ __u8 node_address;
+ __u8 broadcast_address;
+};
+
+#define PI433_IOC_MAGIC 'r'
+
+#define PI433_IOC_RD_TX_CFG \
+ _IOR(PI433_IOC_MAGIC, PI433_TX_CFG_IOCTL_NR, char[sizeof(struct pi433_tx_cfg)])
+#define PI433_IOC_WR_TX_CFG \
+ _IOW(PI433_IOC_MAGIC, PI433_TX_CFG_IOCTL_NR, char[sizeof(struct pi433_tx_cfg)])
+
+#define PI433_IOC_RD_RX_CFG \
+ _IOR(PI433_IOC_MAGIC, PI433_RX_CFG_IOCTL_NR, char[sizeof(struct pi433_rx_cfg)])
+#define PI433_IOC_WR_RX_CFG \
+ _IOW(PI433_IOC_MAGIC, PI433_RX_CFG_IOCTL_NR, char[sizeof(struct pi433_rx_cfg)])
+
+#endif /* PI433_H */
diff --git a/drivers/staging/pi433/rf69.c b/drivers/staging/pi433/rf69.c
new file mode 100644
index 000000000..8c7fab6a4
--- /dev/null
+++ b/drivers/staging/pi433/rf69.c
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * abstraction of the spi interface of HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+
+#include "rf69.h"
+#include "rf69_registers.h"
+
+#define F_OSC 32000000 /* in Hz */
+#define FIFO_SIZE 66 /* in byte */
+
+/*-------------------------------------------------------------------------*/
+
+u8 rf69_read_reg(struct spi_device *spi, u8 addr)
+{
+ return spi_w8r8(spi, addr);
+}
+
+static int rf69_write_reg(struct spi_device *spi, u8 addr, u8 value)
+{
+ char buffer[2];
+
+ buffer[0] = addr | WRITE_BIT;
+ buffer[1] = value;
+
+ return spi_write(spi, &buffer, ARRAY_SIZE(buffer));
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rf69_set_bit(struct spi_device *spi, u8 reg, u8 mask)
+{
+ u8 tmp;
+
+ tmp = rf69_read_reg(spi, reg);
+ tmp = tmp | mask;
+ return rf69_write_reg(spi, reg, tmp);
+}
+
+static int rf69_clear_bit(struct spi_device *spi, u8 reg, u8 mask)
+{
+ u8 tmp;
+
+ tmp = rf69_read_reg(spi, reg);
+ tmp = tmp & ~mask;
+ return rf69_write_reg(spi, reg, tmp);
+}
+
+static inline int rf69_read_mod_write(struct spi_device *spi, u8 reg,
+ u8 mask, u8 value)
+{
+ u8 tmp;
+
+ tmp = rf69_read_reg(spi, reg);
+ tmp = (tmp & ~mask) | value;
+ return rf69_write_reg(spi, reg, tmp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+int rf69_get_version(struct spi_device *spi)
+{
+ return rf69_read_reg(spi, REG_VERSION);
+}
+
+int rf69_set_mode(struct spi_device *spi, enum mode mode)
+{
+ static const u8 mode_map[] = {
+ [transmit] = OPMODE_MODE_TRANSMIT,
+ [receive] = OPMODE_MODE_RECEIVE,
+ [synthesizer] = OPMODE_MODE_SYNTHESIZER,
+ [standby] = OPMODE_MODE_STANDBY,
+ [mode_sleep] = OPMODE_MODE_SLEEP,
+ };
+
+ if (unlikely(mode >= ARRAY_SIZE(mode_map))) {
+ dev_dbg(&spi->dev, "set: illegal mode %u\n", mode);
+ return -EINVAL;
+ }
+
+ return rf69_read_mod_write(spi, REG_OPMODE, MASK_OPMODE_MODE,
+ mode_map[mode]);
+
+ /*
+ * we are using packet mode, so this check is not really needed
+ * but waiting for mode ready is necessary when going from sleep
+ * because the FIFO may not be immediately available from previous mode
+ * while (_mode == RF69_MODE_SLEEP && (READ_REG(REG_IRQFLAGS1) &
+ RF_IRQFLAGS1_MODEREADY) == 0x00); // Wait for ModeReady
+ */
+}
+
+int rf69_set_data_mode(struct spi_device *spi, u8 data_mode)
+{
+ return rf69_read_mod_write(spi, REG_DATAMODUL, MASK_DATAMODUL_MODE,
+ data_mode);
+}
+
+int rf69_set_modulation(struct spi_device *spi, enum modulation modulation)
+{
+ static const u8 modulation_map[] = {
+ [OOK] = DATAMODUL_MODULATION_TYPE_OOK,
+ [FSK] = DATAMODUL_MODULATION_TYPE_FSK,
+ };
+
+ if (unlikely(modulation >= ARRAY_SIZE(modulation_map))) {
+ dev_dbg(&spi->dev, "set: illegal modulation %u\n", modulation);
+ return -EINVAL;
+ }
+
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_TYPE,
+ modulation_map[modulation]);
+}
+
+static enum modulation rf69_get_modulation(struct spi_device *spi)
+{
+ u8 modulation_reg;
+
+ modulation_reg = rf69_read_reg(spi, REG_DATAMODUL);
+
+ switch (modulation_reg & MASK_DATAMODUL_MODULATION_TYPE) {
+ case DATAMODUL_MODULATION_TYPE_OOK:
+ return OOK;
+ case DATAMODUL_MODULATION_TYPE_FSK:
+ return FSK;
+ default:
+ return UNDEF;
+ }
+}
+
+int rf69_set_modulation_shaping(struct spi_device *spi,
+ enum mod_shaping mod_shaping)
+{
+ switch (rf69_get_modulation(spi)) {
+ case FSK:
+ switch (mod_shaping) {
+ case SHAPING_OFF:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_NONE);
+ case SHAPING_1_0:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_1_0);
+ case SHAPING_0_5:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_0_5);
+ case SHAPING_0_3:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_0_3);
+ default:
+ dev_dbg(&spi->dev, "set: illegal mod shaping for FSK %u\n", mod_shaping);
+ return -EINVAL;
+ }
+ case OOK:
+ switch (mod_shaping) {
+ case SHAPING_OFF:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_NONE);
+ case SHAPING_BR:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_BR);
+ case SHAPING_2BR:
+ return rf69_read_mod_write(spi, REG_DATAMODUL,
+ MASK_DATAMODUL_MODULATION_SHAPE,
+ DATAMODUL_MODULATION_SHAPE_2BR);
+ default:
+ dev_dbg(&spi->dev, "set: illegal mod shaping for OOK %u\n", mod_shaping);
+ return -EINVAL;
+ }
+ default:
+ dev_dbg(&spi->dev, "set: modulation undefined\n");
+ return -EINVAL;
+ }
+}
+
+int rf69_set_bit_rate(struct spi_device *spi, u16 bit_rate)
+{
+ int retval;
+ u32 bit_rate_reg;
+ u8 msb;
+ u8 lsb;
+ enum modulation mod;
+
+ // check if modulation is configured
+ mod = rf69_get_modulation(spi);
+ if (mod == UNDEF) {
+ dev_dbg(&spi->dev, "setBitRate: modulation is undefined\n");
+ return -EINVAL;
+ }
+
+ // check input value
+ if (bit_rate < 1200 || (mod == OOK && bit_rate > 32768)) {
+ dev_dbg(&spi->dev, "setBitRate: illegal input param\n");
+ return -EINVAL;
+ }
+
+ // calculate reg settings
+ bit_rate_reg = (F_OSC / bit_rate);
+
+ msb = (bit_rate_reg & 0xff00) >> 8;
+ lsb = (bit_rate_reg & 0xff);
+
+ // transmit to RF 69
+ retval = rf69_write_reg(spi, REG_BITRATE_MSB, msb);
+ if (retval)
+ return retval;
+ retval = rf69_write_reg(spi, REG_BITRATE_LSB, lsb);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+int rf69_set_deviation(struct spi_device *spi, u32 deviation)
+{
+ int retval;
+ u64 f_reg;
+ u64 f_step;
+ u32 bit_rate_reg;
+ u32 bit_rate;
+ u8 msb;
+ u8 lsb;
+ u64 factor = 1000000; // to improve precision of calculation
+
+ // calculate bit rate
+ bit_rate_reg = rf69_read_reg(spi, REG_BITRATE_MSB) << 8;
+ bit_rate_reg |= rf69_read_reg(spi, REG_BITRATE_LSB);
+ bit_rate = F_OSC / bit_rate_reg;
+
+ /*
+ * frequency deviation must exceed 600 Hz but not exceed
+ * 500kHz when taking bitrate dependency into consideration
+ * to ensure proper modulation
+ */
+ if (deviation < 600 || (deviation + (bit_rate / 2)) > 500000) {
+ dev_dbg(&spi->dev,
+ "set_deviation: illegal input param: %u\n", deviation);
+ return -EINVAL;
+ }
+
+ // calculat f step
+ f_step = F_OSC * factor;
+ do_div(f_step, 524288); // 524288 = 2^19
+
+ // calculate register settings
+ f_reg = deviation * factor;
+ do_div(f_reg, f_step);
+
+ msb = (f_reg & 0xff00) >> 8;
+ lsb = (f_reg & 0xff);
+
+ // check msb
+ if (msb & ~FDEVMASB_MASK) {
+ dev_dbg(&spi->dev, "set_deviation: err in calc of msb\n");
+ return -EINVAL;
+ }
+
+ // write to chip
+ retval = rf69_write_reg(spi, REG_FDEV_MSB, msb);
+ if (retval)
+ return retval;
+ retval = rf69_write_reg(spi, REG_FDEV_LSB, lsb);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+int rf69_set_frequency(struct spi_device *spi, u32 frequency)
+{
+ int retval;
+ u32 f_max;
+ u64 f_reg;
+ u64 f_step;
+ u8 msb;
+ u8 mid;
+ u8 lsb;
+ u64 factor = 1000000; // to improve precision of calculation
+
+ // calculat f step
+ f_step = F_OSC * factor;
+ do_div(f_step, 524288); // 524288 = 2^19
+
+ // check input value
+ f_max = div_u64(f_step * 8388608, factor);
+ if (frequency > f_max) {
+ dev_dbg(&spi->dev, "setFrequency: illegal input param\n");
+ return -EINVAL;
+ }
+
+ // calculate reg settings
+ f_reg = frequency * factor;
+ do_div(f_reg, f_step);
+
+ msb = (f_reg & 0xff0000) >> 16;
+ mid = (f_reg & 0xff00) >> 8;
+ lsb = (f_reg & 0xff);
+
+ // write to chip
+ retval = rf69_write_reg(spi, REG_FRF_MSB, msb);
+ if (retval)
+ return retval;
+ retval = rf69_write_reg(spi, REG_FRF_MID, mid);
+ if (retval)
+ return retval;
+ retval = rf69_write_reg(spi, REG_FRF_LSB, lsb);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+int rf69_enable_amplifier(struct spi_device *spi, u8 amplifier_mask)
+{
+ return rf69_set_bit(spi, REG_PALEVEL, amplifier_mask);
+}
+
+int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask)
+{
+ return rf69_clear_bit(spi, REG_PALEVEL, amplifier_mask);
+}
+
+int rf69_set_output_power_level(struct spi_device *spi, u8 power_level)
+{
+ u8 pa_level, ocp, test_pa1, test_pa2;
+ bool pa0, pa1, pa2, high_power;
+ u8 min_power_level;
+
+ // check register pa_level
+ pa_level = rf69_read_reg(spi, REG_PALEVEL);
+ pa0 = pa_level & MASK_PALEVEL_PA0;
+ pa1 = pa_level & MASK_PALEVEL_PA1;
+ pa2 = pa_level & MASK_PALEVEL_PA2;
+
+ // check high power mode
+ ocp = rf69_read_reg(spi, REG_OCP);
+ test_pa1 = rf69_read_reg(spi, REG_TESTPA1);
+ test_pa2 = rf69_read_reg(spi, REG_TESTPA2);
+ high_power = (ocp == 0x0f) && (test_pa1 == 0x5d) && (test_pa2 == 0x7c);
+
+ if (pa0 && !pa1 && !pa2) {
+ power_level += 18;
+ min_power_level = 0;
+ } else if (!pa0 && pa1 && !pa2) {
+ power_level += 18;
+ min_power_level = 16;
+ } else if (!pa0 && pa1 && pa2) {
+ if (high_power)
+ power_level += 11;
+ else
+ power_level += 14;
+ min_power_level = 16;
+ } else {
+ goto failed;
+ }
+
+ // check input value
+ if (power_level > 0x1f)
+ goto failed;
+
+ if (power_level < min_power_level)
+ goto failed;
+
+ // write value
+ return rf69_read_mod_write(spi, REG_PALEVEL, MASK_PALEVEL_OUTPUT_POWER,
+ power_level);
+failed:
+ dev_dbg(&spi->dev, "set: illegal power level %u\n", power_level);
+ return -EINVAL;
+}
+
+int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp)
+{
+ static const u8 pa_ramp_map[] = {
+ [ramp3400] = PARAMP_3400,
+ [ramp2000] = PARAMP_2000,
+ [ramp1000] = PARAMP_1000,
+ [ramp500] = PARAMP_500,
+ [ramp250] = PARAMP_250,
+ [ramp125] = PARAMP_125,
+ [ramp100] = PARAMP_100,
+ [ramp62] = PARAMP_62,
+ [ramp50] = PARAMP_50,
+ [ramp40] = PARAMP_40,
+ [ramp31] = PARAMP_31,
+ [ramp25] = PARAMP_25,
+ [ramp20] = PARAMP_20,
+ [ramp15] = PARAMP_15,
+ [ramp10] = PARAMP_10,
+ };
+
+ if (unlikely(pa_ramp >= ARRAY_SIZE(pa_ramp_map))) {
+ dev_dbg(&spi->dev, "set: illegal pa_ramp %u\n", pa_ramp);
+ return -EINVAL;
+ }
+
+ return rf69_write_reg(spi, REG_PARAMP, pa_ramp_map[pa_ramp]);
+}
+
+int rf69_set_antenna_impedance(struct spi_device *spi,
+ enum antenna_impedance antenna_impedance)
+{
+ switch (antenna_impedance) {
+ case fifty_ohm:
+ return rf69_clear_bit(spi, REG_LNA, MASK_LNA_ZIN);
+ case two_hundred_ohm:
+ return rf69_set_bit(spi, REG_LNA, MASK_LNA_ZIN);
+ default:
+ dev_dbg(&spi->dev, "set: illegal antenna impedance %u\n", antenna_impedance);
+ return -EINVAL;
+ }
+}
+
+int rf69_set_lna_gain(struct spi_device *spi, enum lna_gain lna_gain)
+{
+ static const u8 lna_gain_map[] = {
+ [automatic] = LNA_GAIN_AUTO,
+ [max] = LNA_GAIN_MAX,
+ [max_minus_6] = LNA_GAIN_MAX_MINUS_6,
+ [max_minus_12] = LNA_GAIN_MAX_MINUS_12,
+ [max_minus_24] = LNA_GAIN_MAX_MINUS_24,
+ [max_minus_36] = LNA_GAIN_MAX_MINUS_36,
+ [max_minus_48] = LNA_GAIN_MAX_MINUS_48,
+ };
+
+ if (unlikely(lna_gain >= ARRAY_SIZE(lna_gain_map))) {
+ dev_dbg(&spi->dev, "set: illegal lna gain %u\n", lna_gain);
+ return -EINVAL;
+ }
+
+ return rf69_read_mod_write(spi, REG_LNA, MASK_LNA_GAIN,
+ lna_gain_map[lna_gain]);
+}
+
+static int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg,
+ enum mantisse mantisse, u8 exponent)
+{
+ u8 bandwidth;
+
+ // check value for mantisse and exponent
+ if (exponent > 7) {
+ dev_dbg(&spi->dev, "set: illegal bandwidth exponent %u\n", exponent);
+ return -EINVAL;
+ }
+
+ if (mantisse != mantisse16 &&
+ mantisse != mantisse20 &&
+ mantisse != mantisse24) {
+ dev_dbg(&spi->dev, "set: illegal bandwidth mantisse %u\n", mantisse);
+ return -EINVAL;
+ }
+
+ // read old value
+ bandwidth = rf69_read_reg(spi, reg);
+
+ // "delete" mantisse and exponent = just keep the DCC setting
+ bandwidth = bandwidth & MASK_BW_DCC_FREQ;
+
+ // add new mantisse
+ switch (mantisse) {
+ case mantisse16:
+ bandwidth = bandwidth | BW_MANT_16;
+ break;
+ case mantisse20:
+ bandwidth = bandwidth | BW_MANT_20;
+ break;
+ case mantisse24:
+ bandwidth = bandwidth | BW_MANT_24;
+ break;
+ }
+
+ // add new exponent
+ bandwidth = bandwidth | exponent;
+
+ // write back
+ return rf69_write_reg(spi, reg, bandwidth);
+}
+
+int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse,
+ u8 exponent)
+{
+ return rf69_set_bandwidth_intern(spi, REG_RXBW, mantisse, exponent);
+}
+
+int rf69_set_bandwidth_during_afc(struct spi_device *spi,
+ enum mantisse mantisse,
+ u8 exponent)
+{
+ return rf69_set_bandwidth_intern(spi, REG_AFCBW, mantisse, exponent);
+}
+
+int rf69_set_ook_threshold_dec(struct spi_device *spi,
+ enum threshold_decrement threshold_decrement)
+{
+ static const u8 td_map[] = {
+ [dec_every8th] = OOKPEAK_THRESHDEC_EVERY_8TH,
+ [dec_every4th] = OOKPEAK_THRESHDEC_EVERY_4TH,
+ [dec_every2nd] = OOKPEAK_THRESHDEC_EVERY_2ND,
+ [dec_once] = OOKPEAK_THRESHDEC_ONCE,
+ [dec_twice] = OOKPEAK_THRESHDEC_TWICE,
+ [dec_4times] = OOKPEAK_THRESHDEC_4_TIMES,
+ [dec_8times] = OOKPEAK_THRESHDEC_8_TIMES,
+ [dec_16times] = OOKPEAK_THRESHDEC_16_TIMES,
+ };
+
+ if (unlikely(threshold_decrement >= ARRAY_SIZE(td_map))) {
+ dev_dbg(&spi->dev, "set: illegal OOK threshold decrement %u\n",
+ threshold_decrement);
+ return -EINVAL;
+ }
+
+ return rf69_read_mod_write(spi, REG_OOKPEAK, MASK_OOKPEAK_THRESDEC,
+ td_map[threshold_decrement]);
+}
+
+int rf69_set_dio_mapping(struct spi_device *spi, u8 dio_number, u8 value)
+{
+ u8 mask;
+ u8 shift;
+ u8 dio_addr;
+ u8 dio_value;
+
+ switch (dio_number) {
+ case 0:
+ mask = MASK_DIO0;
+ shift = SHIFT_DIO0;
+ dio_addr = REG_DIOMAPPING1;
+ break;
+ case 1:
+ mask = MASK_DIO1;
+ shift = SHIFT_DIO1;
+ dio_addr = REG_DIOMAPPING1;
+ break;
+ case 2:
+ mask = MASK_DIO2;
+ shift = SHIFT_DIO2;
+ dio_addr = REG_DIOMAPPING1;
+ break;
+ case 3:
+ mask = MASK_DIO3;
+ shift = SHIFT_DIO3;
+ dio_addr = REG_DIOMAPPING1;
+ break;
+ case 4:
+ mask = MASK_DIO4;
+ shift = SHIFT_DIO4;
+ dio_addr = REG_DIOMAPPING2;
+ break;
+ case 5:
+ mask = MASK_DIO5;
+ shift = SHIFT_DIO5;
+ dio_addr = REG_DIOMAPPING2;
+ break;
+ default:
+ dev_dbg(&spi->dev, "set: illegal dio number %u\n", dio_number);
+ return -EINVAL;
+ }
+
+ // read reg
+ dio_value = rf69_read_reg(spi, dio_addr);
+ // delete old value
+ dio_value = dio_value & ~mask;
+ // add new value
+ dio_value = dio_value | value << shift;
+ // write back
+ return rf69_write_reg(spi, dio_addr, dio_value);
+}
+
+int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold)
+{
+ /* no value check needed - u8 exactly matches register size */
+
+ return rf69_write_reg(spi, REG_RSSITHRESH, threshold);
+}
+
+int rf69_set_preamble_length(struct spi_device *spi, u16 preamble_length)
+{
+ int retval;
+ u8 msb, lsb;
+
+ /* no value check needed - u16 exactly matches register size */
+
+ /* calculate reg settings */
+ msb = (preamble_length & 0xff00) >> 8;
+ lsb = (preamble_length & 0xff);
+
+ /* transmit to chip */
+ retval = rf69_write_reg(spi, REG_PREAMBLE_MSB, msb);
+ if (retval)
+ return retval;
+ return rf69_write_reg(spi, REG_PREAMBLE_LSB, lsb);
+}
+
+int rf69_enable_sync(struct spi_device *spi)
+{
+ return rf69_set_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_ON);
+}
+
+int rf69_disable_sync(struct spi_device *spi)
+{
+ return rf69_clear_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_ON);
+}
+
+int rf69_set_fifo_fill_condition(struct spi_device *spi,
+ enum fifo_fill_condition fifo_fill_condition)
+{
+ switch (fifo_fill_condition) {
+ case always:
+ return rf69_set_bit(spi, REG_SYNC_CONFIG,
+ MASK_SYNC_CONFIG_FIFO_FILL_CONDITION);
+ case after_sync_interrupt:
+ return rf69_clear_bit(spi, REG_SYNC_CONFIG,
+ MASK_SYNC_CONFIG_FIFO_FILL_CONDITION);
+ default:
+ dev_dbg(&spi->dev, "set: illegal fifo fill condition %u\n", fifo_fill_condition);
+ return -EINVAL;
+ }
+}
+
+int rf69_set_sync_size(struct spi_device *spi, u8 sync_size)
+{
+ // check input value
+ if (sync_size > 0x07) {
+ dev_dbg(&spi->dev, "set: illegal sync size %u\n", sync_size);
+ return -EINVAL;
+ }
+
+ // write value
+ return rf69_read_mod_write(spi, REG_SYNC_CONFIG,
+ MASK_SYNC_CONFIG_SYNC_SIZE,
+ (sync_size << 3));
+}
+
+int rf69_set_sync_values(struct spi_device *spi, u8 sync_values[8])
+{
+ int retval = 0;
+
+ retval += rf69_write_reg(spi, REG_SYNCVALUE1, sync_values[0]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE2, sync_values[1]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE3, sync_values[2]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE4, sync_values[3]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE5, sync_values[4]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE6, sync_values[5]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE7, sync_values[6]);
+ retval += rf69_write_reg(spi, REG_SYNCVALUE8, sync_values[7]);
+
+ return retval;
+}
+
+int rf69_set_packet_format(struct spi_device *spi,
+ enum packet_format packet_format)
+{
+ switch (packet_format) {
+ case packet_length_var:
+ return rf69_set_bit(spi, REG_PACKETCONFIG1,
+ MASK_PACKETCONFIG1_PACKET_FORMAT_VARIABLE);
+ case packet_length_fix:
+ return rf69_clear_bit(spi, REG_PACKETCONFIG1,
+ MASK_PACKETCONFIG1_PACKET_FORMAT_VARIABLE);
+ default:
+ dev_dbg(&spi->dev, "set: illegal packet format %u\n", packet_format);
+ return -EINVAL;
+ }
+}
+
+int rf69_enable_crc(struct spi_device *spi)
+{
+ return rf69_set_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_CRC_ON);
+}
+
+int rf69_disable_crc(struct spi_device *spi)
+{
+ return rf69_clear_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_CRC_ON);
+}
+
+int rf69_set_address_filtering(struct spi_device *spi,
+ enum address_filtering address_filtering)
+{
+ static const u8 af_map[] = {
+ [filtering_off] = PACKETCONFIG1_ADDRESSFILTERING_OFF,
+ [node_address] = PACKETCONFIG1_ADDRESSFILTERING_NODE,
+ [node_or_broadcast_address] =
+ PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST,
+ };
+
+ if (unlikely(address_filtering >= ARRAY_SIZE(af_map))) {
+ dev_dbg(&spi->dev, "set: illegal address filtering %u\n", address_filtering);
+ return -EINVAL;
+ }
+
+ return rf69_read_mod_write(spi, REG_PACKETCONFIG1,
+ MASK_PACKETCONFIG1_ADDRESSFILTERING,
+ af_map[address_filtering]);
+}
+
+int rf69_set_payload_length(struct spi_device *spi, u8 payload_length)
+{
+ return rf69_write_reg(spi, REG_PAYLOAD_LENGTH, payload_length);
+}
+
+int rf69_set_node_address(struct spi_device *spi, u8 node_address)
+{
+ return rf69_write_reg(spi, REG_NODEADRS, node_address);
+}
+
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcast_address)
+{
+ return rf69_write_reg(spi, REG_BROADCASTADRS, broadcast_address);
+}
+
+int rf69_set_tx_start_condition(struct spi_device *spi,
+ enum tx_start_condition tx_start_condition)
+{
+ switch (tx_start_condition) {
+ case fifo_level:
+ return rf69_clear_bit(spi, REG_FIFO_THRESH,
+ MASK_FIFO_THRESH_TXSTART);
+ case fifo_not_empty:
+ return rf69_set_bit(spi, REG_FIFO_THRESH,
+ MASK_FIFO_THRESH_TXSTART);
+ default:
+ dev_dbg(&spi->dev, "set: illegal tx start condition %u\n", tx_start_condition);
+ return -EINVAL;
+ }
+}
+
+int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold)
+{
+ int retval;
+
+ /* check input value */
+ if (threshold & ~MASK_FIFO_THRESH_VALUE) {
+ dev_dbg(&spi->dev, "set: illegal fifo threshold %u\n", threshold);
+ return -EINVAL;
+ }
+
+ /* write value */
+ retval = rf69_read_mod_write(spi, REG_FIFO_THRESH,
+ MASK_FIFO_THRESH_VALUE,
+ threshold);
+ if (retval)
+ return retval;
+
+ /*
+ * access the fifo to activate new threshold
+ * retval (mis-) used as buffer here
+ */
+ return rf69_read_fifo(spi, (u8 *)&retval, 1);
+}
+
+int rf69_set_dagc(struct spi_device *spi, enum dagc dagc)
+{
+ static const u8 dagc_map[] = {
+ [normal_mode] = DAGC_NORMAL,
+ [improve] = DAGC_IMPROVED_LOWBETA0,
+ [improve_for_low_modulation_index] = DAGC_IMPROVED_LOWBETA1,
+ };
+
+ if (unlikely(dagc >= ARRAY_SIZE(dagc_map))) {
+ dev_dbg(&spi->dev, "set: illegal dagc %u\n", dagc);
+ return -EINVAL;
+ }
+
+ return rf69_write_reg(spi, REG_TESTDAGC, dagc_map[dagc]);
+}
+
+/*-------------------------------------------------------------------------*/
+
+int rf69_read_fifo(struct spi_device *spi, u8 *buffer, unsigned int size)
+{
+ int i;
+ struct spi_transfer transfer;
+ u8 local_buffer[FIFO_SIZE + 1] = {};
+ int retval;
+
+ if (size > FIFO_SIZE) {
+ dev_dbg(&spi->dev,
+ "read fifo: passed in buffer bigger then internal buffer\n");
+ return -EMSGSIZE;
+ }
+
+ /* prepare a bidirectional transfer */
+ local_buffer[0] = REG_FIFO;
+ memset(&transfer, 0, sizeof(transfer));
+ transfer.tx_buf = local_buffer;
+ transfer.rx_buf = local_buffer;
+ transfer.len = size + 1;
+
+ retval = spi_sync_transfer(spi, &transfer, 1);
+
+ /* print content read from fifo for debugging purposes */
+ for (i = 0; i < size; i++)
+ dev_dbg(&spi->dev, "%d - 0x%x\n", i, local_buffer[i + 1]);
+
+ memcpy(buffer, &local_buffer[1], size);
+
+ return retval;
+}
+
+int rf69_write_fifo(struct spi_device *spi, u8 *buffer, unsigned int size)
+{
+ int i;
+ u8 local_buffer[FIFO_SIZE + 1];
+
+ if (size > FIFO_SIZE) {
+ dev_dbg(&spi->dev,
+ "write fifo: passed in buffer bigger then internal buffer\n");
+ return -EMSGSIZE;
+ }
+
+ local_buffer[0] = REG_FIFO | WRITE_BIT;
+ memcpy(&local_buffer[1], buffer, size);
+
+ /* print content written from fifo for debugging purposes */
+ for (i = 0; i < size; i++)
+ dev_dbg(&spi->dev, "%d - 0x%x\n", i, buffer[i]);
+
+ return spi_write(spi, local_buffer, size + 1);
+}
+
diff --git a/drivers/staging/pi433/rf69.h b/drivers/staging/pi433/rf69.h
new file mode 100644
index 000000000..78fa0b8ba
--- /dev/null
+++ b/drivers/staging/pi433/rf69.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * hardware abstraction/register access for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+#ifndef RF69_H
+#define RF69_H
+
+#include "rf69_enum.h"
+#include "rf69_registers.h"
+
+/* NOTE: Modifying FREQUENCY value impacts CE certification */
+#define F_OSC 32000000 /* Hz */
+#define FREQUENCY 433920000 /* Hz */
+#define FIFO_SIZE 66 /* bytes */
+#define FIFO_THRESHOLD 15 /* bytes */
+
+u8 rf69_read_reg(struct spi_device *spi, u8 addr);
+int rf69_get_version(struct spi_device *spi);
+int rf69_set_mode(struct spi_device *spi, enum mode mode);
+int rf69_set_data_mode(struct spi_device *spi, u8 data_mode);
+int rf69_set_modulation(struct spi_device *spi, enum modulation modulation);
+int rf69_set_modulation_shaping(struct spi_device *spi,
+ enum mod_shaping mod_shaping);
+int rf69_set_bit_rate(struct spi_device *spi, u16 bit_rate);
+int rf69_set_deviation(struct spi_device *spi, u32 deviation);
+int rf69_set_frequency(struct spi_device *spi, u32 frequency);
+int rf69_enable_amplifier(struct spi_device *spi, u8 amplifier_mask);
+int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask);
+int rf69_set_output_power_level(struct spi_device *spi, u8 power_level);
+int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp);
+int rf69_set_antenna_impedance(struct spi_device *spi,
+ enum antenna_impedance antenna_impedance);
+int rf69_set_lna_gain(struct spi_device *spi, enum lna_gain lna_gain);
+int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse,
+ u8 exponent);
+int rf69_set_bandwidth_during_afc(struct spi_device *spi,
+ enum mantisse mantisse,
+ u8 exponent);
+int rf69_set_ook_threshold_dec(struct spi_device *spi,
+ enum threshold_decrement threshold_decrement);
+int rf69_set_dio_mapping(struct spi_device *spi, u8 dio_number, u8 value);
+int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold);
+int rf69_set_preamble_length(struct spi_device *spi, u16 preamble_length);
+int rf69_enable_sync(struct spi_device *spi);
+int rf69_disable_sync(struct spi_device *spi);
+int rf69_set_fifo_fill_condition(struct spi_device *spi,
+ enum fifo_fill_condition fifo_fill_condition);
+int rf69_set_sync_size(struct spi_device *spi, u8 sync_size);
+int rf69_set_sync_values(struct spi_device *spi, u8 sync_values[8]);
+int rf69_set_packet_format(struct spi_device *spi,
+ enum packet_format packet_format);
+int rf69_enable_crc(struct spi_device *spi);
+int rf69_disable_crc(struct spi_device *spi);
+int rf69_set_address_filtering(struct spi_device *spi,
+ enum address_filtering address_filtering);
+int rf69_set_payload_length(struct spi_device *spi, u8 payload_length);
+int rf69_set_node_address(struct spi_device *spi, u8 node_address);
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcast_address);
+int rf69_set_tx_start_condition(struct spi_device *spi,
+ enum tx_start_condition tx_start_condition);
+int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold);
+int rf69_set_dagc(struct spi_device *spi, enum dagc dagc);
+
+int rf69_read_fifo(struct spi_device *spi, u8 *buffer, unsigned int size);
+int rf69_write_fifo(struct spi_device *spi, u8 *buffer, unsigned int size);
+
+#endif
diff --git a/drivers/staging/pi433/rf69_enum.h b/drivers/staging/pi433/rf69_enum.h
new file mode 100644
index 000000000..9dc906124
--- /dev/null
+++ b/drivers/staging/pi433/rf69_enum.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * enumerations for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+
+#ifndef RF69_ENUM_H
+#define RF69_ENUM_H
+
+enum mode {
+ mode_sleep,
+ standby,
+ synthesizer,
+ transmit,
+ receive
+};
+
+enum modulation {
+ OOK,
+ FSK,
+ UNDEF
+};
+
+enum mod_shaping {
+ SHAPING_OFF,
+ SHAPING_1_0,
+ SHAPING_0_5,
+ SHAPING_0_3,
+ SHAPING_BR,
+ SHAPING_2BR
+};
+
+enum pa_ramp {
+ ramp3400,
+ ramp2000,
+ ramp1000,
+ ramp500,
+ ramp250,
+ ramp125,
+ ramp100,
+ ramp62,
+ ramp50,
+ ramp40,
+ ramp31,
+ ramp25,
+ ramp20,
+ ramp15,
+ ramp12,
+ ramp10
+};
+
+enum antenna_impedance {
+ fifty_ohm,
+ two_hundred_ohm
+};
+
+enum lna_gain {
+ automatic,
+ max,
+ max_minus_6,
+ max_minus_12,
+ max_minus_24,
+ max_minus_36,
+ max_minus_48,
+ undefined
+};
+
+enum mantisse {
+ mantisse16,
+ mantisse20,
+ mantisse24
+};
+
+enum threshold_decrement {
+ dec_every8th,
+ dec_every4th,
+ dec_every2nd,
+ dec_once,
+ dec_twice,
+ dec_4times,
+ dec_8times,
+ dec_16times
+};
+
+enum fifo_fill_condition {
+ after_sync_interrupt,
+ always
+};
+
+enum packet_format {
+ /*
+ * Used when the size of payload is fixed in advance. This mode of
+ * operation may be of interest to minimize RF overhead by 1 byte as
+ * no length byte field is required
+ */
+ packet_length_fix,
+ /*
+ * Used when the size of payload isn't known in advance. It requires the
+ * transmitter to send the length byte in each packet so the receiver
+ * would know how to operate properly
+ */
+ packet_length_var
+};
+
+enum tx_start_condition {
+ /* the number of bytes in the FIFO exceeds FIFO_THRESHOLD */
+ fifo_level,
+ /* at least one byte in the FIFO */
+ fifo_not_empty
+};
+
+enum address_filtering {
+ filtering_off,
+ node_address,
+ node_or_broadcast_address
+};
+
+enum dagc {
+ normal_mode,
+ improve,
+ improve_for_low_modulation_index
+};
+
+#endif
diff --git a/drivers/staging/pi433/rf69_registers.h b/drivers/staging/pi433/rf69_registers.h
new file mode 100644
index 000000000..0d6737738
--- /dev/null
+++ b/drivers/staging/pi433/rf69_registers.h
@@ -0,0 +1,478 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * register description for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ */
+
+/*******************************************/
+/* RF69 register addresses */
+/*******************************************/
+#define REG_FIFO 0x00
+#define REG_OPMODE 0x01
+#define REG_DATAMODUL 0x02
+#define REG_BITRATE_MSB 0x03
+#define REG_BITRATE_LSB 0x04
+#define REG_FDEV_MSB 0x05
+#define REG_FDEV_LSB 0x06
+#define REG_FRF_MSB 0x07
+#define REG_FRF_MID 0x08
+#define REG_FRF_LSB 0x09
+#define REG_OSC1 0x0A
+#define REG_AFCCTRL 0x0B
+#define REG_LOWBAT 0x0C
+#define REG_LISTEN1 0x0D
+#define REG_LISTEN2 0x0E
+#define REG_LISTEN3 0x0F
+#define REG_VERSION 0x10
+#define REG_PALEVEL 0x11
+#define REG_PARAMP 0x12
+#define REG_OCP 0x13
+#define REG_AGCREF 0x14 /* not available on RF69 */
+#define REG_AGCTHRESH1 0x15 /* not available on RF69 */
+#define REG_AGCTHRESH2 0x16 /* not available on RF69 */
+#define REG_AGCTHRESH3 0x17 /* not available on RF69 */
+#define REG_LNA 0x18
+#define REG_RXBW 0x19
+#define REG_AFCBW 0x1A
+#define REG_OOKPEAK 0x1B
+#define REG_OOKAVG 0x1C
+#define REG_OOKFIX 0x1D
+#define REG_AFCFEI 0x1E
+#define REG_AFCMSB 0x1F
+#define REG_AFCLSB 0x20
+#define REG_FEIMSB 0x21
+#define REG_FEILSB 0x22
+#define REG_RSSICONFIG 0x23
+#define REG_RSSIVALUE 0x24
+#define REG_DIOMAPPING1 0x25
+#define REG_DIOMAPPING2 0x26
+#define REG_IRQFLAGS1 0x27
+#define REG_IRQFLAGS2 0x28
+#define REG_RSSITHRESH 0x29
+#define REG_RXTIMEOUT1 0x2A
+#define REG_RXTIMEOUT2 0x2B
+#define REG_PREAMBLE_MSB 0x2C
+#define REG_PREAMBLE_LSB 0x2D
+#define REG_SYNC_CONFIG 0x2E
+#define REG_SYNCVALUE1 0x2F
+#define REG_SYNCVALUE2 0x30
+#define REG_SYNCVALUE3 0x31
+#define REG_SYNCVALUE4 0x32
+#define REG_SYNCVALUE5 0x33
+#define REG_SYNCVALUE6 0x34
+#define REG_SYNCVALUE7 0x35
+#define REG_SYNCVALUE8 0x36
+#define REG_PACKETCONFIG1 0x37
+#define REG_PAYLOAD_LENGTH 0x38
+#define REG_NODEADRS 0x39
+#define REG_BROADCASTADRS 0x3A
+#define REG_AUTOMODES 0x3B
+#define REG_FIFO_THRESH 0x3C
+#define REG_PACKETCONFIG2 0x3D
+#define REG_AESKEY1 0x3E
+#define REG_AESKEY2 0x3F
+#define REG_AESKEY3 0x40
+#define REG_AESKEY4 0x41
+#define REG_AESKEY5 0x42
+#define REG_AESKEY6 0x43
+#define REG_AESKEY7 0x44
+#define REG_AESKEY8 0x45
+#define REG_AESKEY9 0x46
+#define REG_AESKEY10 0x47
+#define REG_AESKEY11 0x48
+#define REG_AESKEY12 0x49
+#define REG_AESKEY13 0x4A
+#define REG_AESKEY14 0x4B
+#define REG_AESKEY15 0x4C
+#define REG_AESKEY16 0x4D
+#define REG_TEMP1 0x4E
+#define REG_TEMP2 0x4F
+#define REG_TESTLNA 0x58
+#define REG_TESTPA1 0x5A /* only present on RFM69HW */
+#define REG_TESTPA2 0x5C /* only present on RFM69HW */
+#define REG_TESTDAGC 0x6F
+#define REG_TESTAFC 0x71
+
+/******************************************************/
+/* RF69/SX1231 bit definition */
+/******************************************************/
+/* write bit */
+#define WRITE_BIT 0x80
+
+/* RegOpMode */
+#define MASK_OPMODE_SEQUENCER_OFF 0x80
+#define MASK_OPMODE_LISTEN_ON 0x40
+#define MASK_OPMODE_LISTEN_ABORT 0x20
+#define MASK_OPMODE_MODE 0x1C
+
+#define OPMODE_MODE_SLEEP 0x00
+#define OPMODE_MODE_STANDBY 0x04 /* default */
+#define OPMODE_MODE_SYNTHESIZER 0x08
+#define OPMODE_MODE_TRANSMIT 0x0C
+#define OPMODE_MODE_RECEIVE 0x10
+
+/* RegDataModul */
+#define MASK_DATAMODUL_MODE 0x06
+#define MASK_DATAMODUL_MODULATION_TYPE 0x18
+#define MASK_DATAMODUL_MODULATION_SHAPE 0x03
+
+#define DATAMODUL_MODE_PACKET 0x00 /* default */
+#define DATAMODUL_MODE_CONTINUOUS 0x40
+#define DATAMODUL_MODE_CONTINUOUS_NOSYNC 0x60
+
+#define DATAMODUL_MODULATION_TYPE_FSK 0x00 /* default */
+#define DATAMODUL_MODULATION_TYPE_OOK 0x08
+
+#define DATAMODUL_MODULATION_SHAPE_NONE 0x00 /* default */
+#define DATAMODUL_MODULATION_SHAPE_1_0 0x01
+#define DATAMODUL_MODULATION_SHAPE_0_5 0x02
+#define DATAMODUL_MODULATION_SHAPE_0_3 0x03
+#define DATAMODUL_MODULATION_SHAPE_BR 0x01
+#define DATAMODUL_MODULATION_SHAPE_2BR 0x02
+
+/* RegFDevMsb (0x05)*/
+#define FDEVMASB_MASK 0x3f
+
+/*
+ * // RegOsc1
+ * #define OSC1_RCCAL_START 0x80
+ * #define OSC1_RCCAL_DONE 0x40
+ *
+ * // RegLowBat
+ * #define LOWBAT_MONITOR 0x10
+ * #define LOWBAT_ON 0x08
+ * #define LOWBAT_OFF 0x00 // Default
+ *
+ * #define LOWBAT_TRIM_1695 0x00
+ * #define LOWBAT_TRIM_1764 0x01
+ * #define LOWBAT_TRIM_1835 0x02 // Default
+ * #define LOWBAT_TRIM_1905 0x03
+ * #define LOWBAT_TRIM_1976 0x04
+ * #define LOWBAT_TRIM_2045 0x05
+ * #define LOWBAT_TRIM_2116 0x06
+ * #define LOWBAT_TRIM_2185 0x07
+ *
+ *
+ * // RegListen1
+ * #define LISTEN1_RESOL_64 0x50
+ * #define LISTEN1_RESOL_4100 0xA0 // Default
+ * #define LISTEN1_RESOL_262000 0xF0
+ *
+ * #define LISTEN1_CRITERIA_RSSI 0x00 // Default
+ * #define LISTEN1_CRITERIA_RSSIANDSYNC 0x08
+ *
+ * #define LISTEN1_END_00 0x00
+ * #define LISTEN1_END_01 0x02 // Default
+ * #define LISTEN1_END_10 0x04
+ *
+ *
+ * // RegListen2
+ * #define LISTEN2_COEFIDLE_VALUE 0xF5 // Default
+ *
+ * // RegListen3
+ * #define LISTEN3_COEFRX_VALUE 0x20 // Default
+ */
+
+// RegPaLevel
+#define MASK_PALEVEL_PA0 0x80
+#define MASK_PALEVEL_PA1 0x40
+#define MASK_PALEVEL_PA2 0x20
+#define MASK_PALEVEL_OUTPUT_POWER 0x1F
+
+// RegPaRamp
+#define PARAMP_3400 0x00
+#define PARAMP_2000 0x01
+#define PARAMP_1000 0x02
+#define PARAMP_500 0x03
+#define PARAMP_250 0x04
+#define PARAMP_125 0x05
+#define PARAMP_100 0x06
+#define PARAMP_62 0x07
+#define PARAMP_50 0x08
+#define PARAMP_40 0x09 /* default */
+#define PARAMP_31 0x0A
+#define PARAMP_25 0x0B
+#define PARAMP_20 0x0C
+#define PARAMP_15 0x0D
+#define PARAMP_12 0x0E
+#define PARAMP_10 0x0F
+
+#define MASK_PARAMP 0x0F
+
+/*
+ * // RegOcp
+ * #define OCP_OFF 0x0F
+ * #define OCP_ON 0x1A // Default
+ *
+ * #define OCP_TRIM_45 0x00
+ * #define OCP_TRIM_50 0x01
+ * #define OCP_TRIM_55 0x02
+ * #define OCP_TRIM_60 0x03
+ * #define OCP_TRIM_65 0x04
+ * #define OCP_TRIM_70 0x05
+ * #define OCP_TRIM_75 0x06
+ * #define OCP_TRIM_80 0x07
+ * #define OCP_TRIM_85 0x08
+ * #define OCP_TRIM_90 0x09
+ * #define OCP_TRIM_95 0x0A
+ * #define OCP_TRIM_100 0x0B // Default
+ * #define OCP_TRIM_105 0x0C
+ * #define OCP_TRIM_110 0x0D
+ * #define OCP_TRIM_115 0x0E
+ * #define OCP_TRIM_120 0x0F
+ */
+
+/* RegLna (0x18) */
+#define MASK_LNA_ZIN 0x80
+#define MASK_LNA_CURRENT_GAIN 0x38
+#define MASK_LNA_GAIN 0x07
+
+#define LNA_GAIN_AUTO 0x00 /* default */
+#define LNA_GAIN_MAX 0x01
+#define LNA_GAIN_MAX_MINUS_6 0x02
+#define LNA_GAIN_MAX_MINUS_12 0x03
+#define LNA_GAIN_MAX_MINUS_24 0x04
+#define LNA_GAIN_MAX_MINUS_36 0x05
+#define LNA_GAIN_MAX_MINUS_48 0x06
+
+/* RegRxBw (0x19) and RegAfcBw (0x1A) */
+#define MASK_BW_DCC_FREQ 0xE0
+#define MASK_BW_MANTISSE 0x18
+#define MASK_BW_EXPONENT 0x07
+
+#define BW_DCC_16_PERCENT 0x00
+#define BW_DCC_8_PERCENT 0x20
+#define BW_DCC_4_PERCENT 0x40 /* default */
+#define BW_DCC_2_PERCENT 0x60
+#define BW_DCC_1_PERCENT 0x80
+#define BW_DCC_0_5_PERCENT 0xA0
+#define BW_DCC_0_25_PERCENT 0xC0
+#define BW_DCC_0_125_PERCENT 0xE0
+
+#define BW_MANT_16 0x00
+#define BW_MANT_20 0x08
+#define BW_MANT_24 0x10 /* default */
+
+/* RegOokPeak (0x1B) */
+#define MASK_OOKPEAK_THRESTYPE 0xc0
+#define MASK_OOKPEAK_THRESSTEP 0x38
+#define MASK_OOKPEAK_THRESDEC 0x07
+
+#define OOKPEAK_THRESHTYPE_FIXED 0x00
+#define OOKPEAK_THRESHTYPE_PEAK 0x40 /* default */
+#define OOKPEAK_THRESHTYPE_AVERAGE 0x80
+
+#define OOKPEAK_THRESHSTEP_0_5_DB 0x00 /* default */
+#define OOKPEAK_THRESHSTEP_1_0_DB 0x08
+#define OOKPEAK_THRESHSTEP_1_5_DB 0x10
+#define OOKPEAK_THRESHSTEP_2_0_DB 0x18
+#define OOKPEAK_THRESHSTEP_3_0_DB 0x20
+#define OOKPEAK_THRESHSTEP_4_0_DB 0x28
+#define OOKPEAK_THRESHSTEP_5_0_DB 0x30
+#define OOKPEAK_THRESHSTEP_6_0_DB 0x38
+
+#define OOKPEAK_THRESHDEC_ONCE 0x00 /* default */
+#define OOKPEAK_THRESHDEC_EVERY_2ND 0x01
+#define OOKPEAK_THRESHDEC_EVERY_4TH 0x02
+#define OOKPEAK_THRESHDEC_EVERY_8TH 0x03
+#define OOKPEAK_THRESHDEC_TWICE 0x04
+#define OOKPEAK_THRESHDEC_4_TIMES 0x05
+#define OOKPEAK_THRESHDEC_8_TIMES 0x06
+#define OOKPEAK_THRESHDEC_16_TIMES 0x07
+
+/*
+ * // RegOokAvg
+ * #define OOKAVG_AVERAGETHRESHFILT_00 0x00
+ * #define OOKAVG_AVERAGETHRESHFILT_01 0x40
+ * #define OOKAVG_AVERAGETHRESHFILT_10 0x80 // Default
+ * #define OOKAVG_AVERAGETHRESHFILT_11 0xC0
+ *
+ *
+ * // RegAfcFei
+ * #define AFCFEI_FEI_DONE 0x40
+ * #define AFCFEI_FEI_START 0x20
+ * #define AFCFEI_AFC_DONE 0x10
+ * #define AFCFEI_AFCAUTOCLEAR_ON 0x08
+ * #define AFCFEI_AFCAUTOCLEAR_OFF 0x00 // Default
+ *
+ * #define AFCFEI_AFCAUTO_ON 0x04
+ * #define AFCFEI_AFCAUTO_OFF 0x00 // Default
+ *
+ * #define AFCFEI_AFC_CLEAR 0x02
+ * #define AFCFEI_AFC_START 0x01
+ *
+ * // RegRssiConfig
+ * #define RSSI_FASTRX_ON 0x08
+ * #define RSSI_FASTRX_OFF 0x00 // Default
+ * #define RSSI_DONE 0x02
+ * #define RSSI_START 0x01
+ */
+
+/* RegDioMapping1 */
+#define MASK_DIO0 0xC0
+#define MASK_DIO1 0x30
+#define MASK_DIO2 0x0C
+#define MASK_DIO3 0x03
+#define SHIFT_DIO0 6
+#define SHIFT_DIO1 4
+#define SHIFT_DIO2 2
+#define SHIFT_DIO3 0
+
+/* RegDioMapping2 */
+#define MASK_DIO4 0xC0
+#define MASK_DIO5 0x30
+#define SHIFT_DIO4 6
+#define SHIFT_DIO5 4
+
+/* DIO numbers */
+#define DIO0 0
+#define DIO1 1
+#define DIO2 2
+#define DIO3 3
+#define DIO4 4
+#define DIO5 5
+
+/* DIO Mapping values (packet mode) */
+#define DIO_MODE_READY_DIO4 0x00
+#define DIO_MODE_READY_DIO5 0x03
+#define DIO_CLK_OUT 0x00
+#define DIO_DATA 0x01
+#define DIO_TIMEOUT_DIO1 0x03
+#define DIO_TIMEOUT_DIO4 0x00
+#define DIO_RSSI_DIO0 0x03
+#define DIO_RSSI_DIO3_4 0x01
+#define DIO_RX_READY 0x02
+#define DIO_PLL_LOCK 0x03
+#define DIO_TX_READY 0x01
+#define DIO_FIFO_FULL_DIO1 0x01
+#define DIO_FIFO_FULL_DIO3 0x00
+#define DIO_SYNC_ADDRESS 0x02
+#define DIO_FIFO_NOT_EMPTY_DIO1 0x02
+#define DIO_FIFO_NOT_EMPTY_FIO2 0x00
+#define DIO_AUTOMODE 0x04
+#define DIO_FIFO_LEVEL 0x00
+#define DIO_CRC_OK 0x00
+#define DIO_PAYLOAD_READY 0x01
+#define DIO_PACKET_SENT 0x00
+#define DIO_DCLK 0x00
+
+/* RegDioMapping2 CLK_OUT part */
+#define MASK_DIOMAPPING2_CLK_OUT 0x07
+
+#define DIOMAPPING2_CLK_OUT_NO_DIV 0x00
+#define DIOMAPPING2_CLK_OUT_DIV_2 0x01
+#define DIOMAPPING2_CLK_OUT_DIV_4 0x02
+#define DIOMAPPING2_CLK_OUT_DIV_8 0x03
+#define DIOMAPPING2_CLK_OUT_DIV_16 0x04
+#define DIOMAPPING2_CLK_OUT_DIV_32 0x05
+#define DIOMAPPING2_CLK_OUT_RC 0x06
+#define DIOMAPPING2_CLK_OUT_OFF 0x07 /* default */
+
+/* RegIrqFlags1 */
+#define MASK_IRQFLAGS1_MODE_READY 0x80
+#define MASK_IRQFLAGS1_RX_READY 0x40
+#define MASK_IRQFLAGS1_TX_READY 0x20
+#define MASK_IRQFLAGS1_PLL_LOCK 0x10
+#define MASK_IRQFLAGS1_RSSI 0x08
+#define MASK_IRQFLAGS1_TIMEOUT 0x04
+#define MASK_IRQFLAGS1_AUTOMODE 0x02
+#define MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH 0x01
+
+/* RegIrqFlags2 */
+#define MASK_IRQFLAGS2_FIFO_FULL 0x80
+#define MASK_IRQFLAGS2_FIFO_NOT_EMPTY 0x40
+#define MASK_IRQFLAGS2_FIFO_LEVEL 0x20
+#define MASK_IRQFLAGS2_FIFO_OVERRUN 0x10
+#define MASK_IRQFLAGS2_PACKET_SENT 0x08
+#define MASK_IRQFLAGS2_PAYLOAD_READY 0x04
+#define MASK_IRQFLAGS2_CRC_OK 0x02
+#define MASK_IRQFLAGS2_LOW_BAT 0x01
+
+/* RegSyncConfig */
+#define MASK_SYNC_CONFIG_SYNC_ON 0x80 /* default */
+#define MASK_SYNC_CONFIG_FIFO_FILL_CONDITION 0x40
+#define MASK_SYNC_CONFIG_SYNC_SIZE 0x38
+#define MASK_SYNC_CONFIG_SYNC_TOLERANCE 0x07
+
+/* RegPacketConfig1 */
+#define MASK_PACKETCONFIG1_PACKET_FORMAT_VARIABLE 0x80
+#define MASK_PACKETCONFIG1_DCFREE 0x60
+#define MASK_PACKETCONFIG1_CRC_ON 0x10 /* default */
+#define MASK_PACKETCONFIG1_CRCAUTOCLEAR_OFF 0x08
+#define MASK_PACKETCONFIG1_ADDRESSFILTERING 0x06
+
+#define PACKETCONFIG1_DCFREE_OFF 0x00 /* default */
+#define PACKETCONFIG1_DCFREE_MANCHESTER 0x20
+#define PACKETCONFIG1_DCFREE_WHITENING 0x40
+#define PACKETCONFIG1_ADDRESSFILTERING_OFF 0x00 /* default */
+#define PACKETCONFIG1_ADDRESSFILTERING_NODE 0x02
+#define PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST 0x04
+
+/*
+ * // RegAutoModes
+ * #define AUTOMODES_ENTER_OFF 0x00 // Default
+ * #define AUTOMODES_ENTER_FIFONOTEMPTY 0x20
+ * #define AUTOMODES_ENTER_FIFOLEVEL 0x40
+ * #define AUTOMODES_ENTER_CRCOK 0x60
+ * #define AUTOMODES_ENTER_PAYLOADREADY 0x80
+ * #define AUTOMODES_ENTER_SYNCADRSMATCH 0xA0
+ * #define AUTOMODES_ENTER_PACKETSENT 0xC0
+ * #define AUTOMODES_ENTER_FIFOEMPTY 0xE0
+ *
+ * #define AUTOMODES_EXIT_OFF 0x00 // Default
+ * #define AUTOMODES_EXIT_FIFOEMPTY 0x04
+ * #define AUTOMODES_EXIT_FIFOLEVEL 0x08
+ * #define AUTOMODES_EXIT_CRCOK 0x0C
+ * #define AUTOMODES_EXIT_PAYLOADREADY 0x10
+ * #define AUTOMODES_EXIT_SYNCADRSMATCH 0x14
+ * #define AUTOMODES_EXIT_PACKETSENT 0x18
+ * #define AUTOMODES_EXIT_RXTIMEOUT 0x1C
+ *
+ * #define AUTOMODES_INTERMEDIATE_SLEEP 0x00 // Default
+ * #define AUTOMODES_INTERMEDIATE_STANDBY 0x01
+ * #define AUTOMODES_INTERMEDIATE_RECEIVER 0x02
+ * #define AUTOMODES_INTERMEDIATE_TRANSMITTER 0x03
+ *
+ */
+/* RegFifoThresh (0x3c) */
+#define MASK_FIFO_THRESH_TXSTART 0x80
+#define MASK_FIFO_THRESH_VALUE 0x7F
+
+/*
+ *
+ * // RegPacketConfig2
+ * #define PACKET2_RXRESTARTDELAY_1BIT 0x00 // Default
+ * #define PACKET2_RXRESTARTDELAY_2BITS 0x10
+ * #define PACKET2_RXRESTARTDELAY_4BITS 0x20
+ * #define PACKET2_RXRESTARTDELAY_8BITS 0x30
+ * #define PACKET2_RXRESTARTDELAY_16BITS 0x40
+ * #define PACKET2_RXRESTARTDELAY_32BITS 0x50
+ * #define PACKET2_RXRESTARTDELAY_64BITS 0x60
+ * #define PACKET2_RXRESTARTDELAY_128BITS 0x70
+ * #define PACKET2_RXRESTARTDELAY_256BITS 0x80
+ * #define PACKET2_RXRESTARTDELAY_512BITS 0x90
+ * #define PACKET2_RXRESTARTDELAY_1024BITS 0xA0
+ * #define PACKET2_RXRESTARTDELAY_2048BITS 0xB0
+ * #define PACKET2_RXRESTARTDELAY_NONE 0xC0
+ * #define PACKET2_RXRESTART 0x04
+ *
+ * #define PACKET2_AUTORXRESTART_ON 0x02 // Default
+ * #define PACKET2_AUTORXRESTART_OFF 0x00
+ *
+ * #define PACKET2_AES_ON 0x01
+ * #define PACKET2_AES_OFF 0x00 // Default
+ *
+ *
+ * // RegTemp1
+ * #define TEMP1_MEAS_START 0x08
+ * #define TEMP1_MEAS_RUNNING 0x04
+ * #define TEMP1_ADCLOWPOWER_ON 0x01 // Default
+ * #define TEMP1_ADCLOWPOWER_OFF 0x00
+ */
+
+// RegTestDagc (0x6F)
+#define DAGC_NORMAL 0x00 /* Reset value */
+#define DAGC_IMPROVED_LOWBETA1 0x20
+#define DAGC_IMPROVED_LOWBETA0 0x30 /* Recommended val */