source "drivers/staging/typec/Kconfig"
+source "drivers/staging/pi433/Kconfig"
+
endif # STAGING
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
+obj-$(CONFIG_PI433) += pi433/
--- /dev/null
+// Definitions for Pi433
+/dts-v1/;
+/plugin/;
+
+/ {
+ compatible = "bcm,bcm2835", "bcm,bcm2708", "bcm,bcm2709";
+
+ fragment@0 {
+ target = <&spi0>;
+ __overlay__ {
+ status = "okay";
+
+ spidev@0{
+ status = "disabled";
+ };
+
+ spidev@1{
+ status = "disabled";
+ };
+ };
+ };
+
+ fragment@1 {
+ target = <&gpio>;
+ __overlay__ {
+ pi433_pins: pi433_pins {
+ brcm,pins = <7 25 24>;
+ brcm,function = <0 0 0>; // in in in
+ };
+ };
+ };
+
+ fragment@2 {
+ target = <&spi0>;
+ __overlay__ {
+ #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>;
+ };
+ };
+ };
+};
--- /dev/null
+* 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 and overlay, instead
+of editing your boards device tree.
+For using the overlay, you need to compile the file pi433-overlay.dts you can
+find aside to 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.
+
+
--- /dev/null
+=====
+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 were queued and process 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 then hardware fifo size.
+
+Discription of driver operation
+===============================
+
+a) transmission
+
+Each transmission can take place with a different configuration of the rf
+module. Therfore 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 then hardware fifo size. If the telegram is send 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 an 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 meat, 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 then 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 transfered via struct pi433_tx_cfg, the parameterset for transmission.
+It is devided 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)
+ paRamp (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
+ fifoLevel - transmission starts, if fifo is filled to
+ threshold level
+ fifoNotEmpty - 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 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
+ telgram. 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 transfered via struct pi433_rx_cfg, the parameterset for receiving. It is devided 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 exeeded, a reception cycle starts
+ Allowed values: 0...255
+ thresholdDecrement
+ 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
+ antennaImpedance
+ sets the electrical adoption of the antenna
+ fiftyOhm - for antennas with an impedance of 50Ohm
+ twohundretOhm - for antennas with an impedance of 200Ohm
+ lnaGain
+ sets the gain of the low noise amp
+ automatic - lna gain is determed by an agc
+ max - lna gain is set to maximum
+ maxMinus6 - lna gain is set to 6db below max
+ maxMinus12 - lna gain is set to 12db below max
+ maxMinus24 - lna gain is set to 24db below max
+ maxMinus36 - lna gain is set to 36db below max
+ maxMinus48 - 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
+ normalMode
+ improve
+ improve4LowModulationIndex
+
+ 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 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;
+ filteringOff - no adress filtering will take place
+ nodeAddress - all telegrams, not matching the node
+ address will be internally discarded
+ nodeOrBroadcastAddress - 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 transfering
+ data to the read buffer
+ This option is only usefull, 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.
+
+
--- /dev/null
+config PI433
+ tristate "Pi433 - a 433MHz radio module for Raspberry Pi"
+ default n
+ ---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
--- /dev/null
+obj-$(CONFIG_PI433) += pi433.o
+
+pi433-objs := pi433_if.o rf69.o
--- /dev/null
+* coding style does not fully comply with the kernel style guide.
+* still TODOs, annotated in the code
+* 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
--- /dev/null
+/*
+ * 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#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 <asm/compat.h>
+#endif
+
+#include "pi433_if.h"
+#include "rf69.h"
+
+
+#define N_PI433_MINORS (1U << 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 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;
+ unsigned users;
+
+ /* 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; // TODO: check, whether necessary or obsolete
+ struct task_struct *tx_task_struct;
+ wait_queue_head_t tx_wait_queue;
+ u8 free_in_fifo;
+
+ /* 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;
+ 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;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* macro for checked access of registers of radio module */
+#define SET_CHECKED(retval) \
+ if (retval < 0) \
+ return retval;
+
+/*-------------------------------------------------------------------------*/
+
+/* GPIO interrupt handlers */
+static irq_handler_t
+DIO0_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pi433_device *device = dev_id;
+
+ if (device->irq_state[DIO0] == DIO_PacketSent)
+ {
+ device->free_in_fifo = FIFO_SIZE;
+ printk("DIO0 irq: Packet sent\n"); // TODO: printk() should include KERN_ facility level
+ wake_up_interruptible(&device->fifo_wait_queue);
+ }
+ else if (device->irq_state[DIO0] == DIO_Rssi_DIO0)
+ {
+ printk("DIO0 irq: RSSI level over threshold\n");
+ wake_up_interruptible(&device->rx_wait_queue);
+ }
+ else if (device->irq_state[DIO0] == DIO_PayloadReady)
+ {
+ printk("DIO0 irq: PayloadReady\n");
+ device->free_in_fifo = 0;
+ wake_up_interruptible(&device->fifo_wait_queue);
+ }
+
+ return (irq_handler_t) IRQ_HANDLED;
+}
+
+static irq_handler_t
+DIO1_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pi433_device *device = dev_id;
+
+ if (device->irq_state[DIO1] == DIO_FifoNotEmpty_DIO1)
+ {
+ device->free_in_fifo = FIFO_SIZE;
+ }
+ else if (device->irq_state[DIO1] == DIO_FifoLevel)
+ {
+ if (device->rx_active) device->free_in_fifo = FIFO_THRESHOLD - 1;
+ else device->free_in_fifo = FIFO_SIZE - FIFO_THRESHOLD - 1;
+ }
+ printk("DIO1 irq: %d bytes free in fifo\n", device->free_in_fifo); // TODO: printk() should include KERN_ facility level
+ wake_up_interruptible(&device->fifo_wait_queue);
+
+ return (irq_handler_t) IRQ_HANDLED;
+}
+
+static void *DIO_irq_handler[NUM_DIO] = {
+ DIO0_irq_handler,
+ DIO1_irq_handler
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg)
+{
+ int payload_length;
+
+ /* receiver config */
+ SET_CHECKED(rf69_set_frequency (dev->spi, rx_cfg->frequency));
+ SET_CHECKED(rf69_set_bit_rate (dev->spi, rx_cfg->bit_rate));
+ SET_CHECKED(rf69_set_modulation (dev->spi, rx_cfg->modulation));
+ SET_CHECKED(rf69_set_antenna_impedance (dev->spi, rx_cfg->antenna_impedance));
+ SET_CHECKED(rf69_set_rssi_threshold (dev->spi, rx_cfg->rssi_threshold));
+ SET_CHECKED(rf69_set_ook_threshold_dec (dev->spi, rx_cfg->thresholdDecrement));
+ SET_CHECKED(rf69_set_bandwidth (dev->spi, rx_cfg->bw_mantisse, rx_cfg->bw_exponent));
+ SET_CHECKED(rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse, rx_cfg->bw_exponent));
+ SET_CHECKED(rf69_set_dagc (dev->spi, rx_cfg->dagc));
+
+ dev->rx_bytes_to_drop = rx_cfg->bytes_to_drop;
+
+ /* packet config */
+ /* enable */
+ SET_CHECKED(rf69_set_sync_enable(dev->spi, rx_cfg->enable_sync));
+ if (rx_cfg->enable_sync == optionOn)
+ {
+ SET_CHECKED(rf69_set_fifo_fill_condition(dev->spi, afterSyncInterrupt));
+ }
+ else
+ {
+ SET_CHECKED(rf69_set_fifo_fill_condition(dev->spi, always));
+ }
+ SET_CHECKED(rf69_set_packet_format (dev->spi, rx_cfg->enable_length_byte));
+ SET_CHECKED(rf69_set_adressFiltering(dev->spi, rx_cfg->enable_address_filtering));
+ SET_CHECKED(rf69_set_crc_enable (dev->spi, rx_cfg->enable_crc));
+
+ /* lengths */
+ SET_CHECKED(rf69_set_sync_size(dev->spi, rx_cfg->sync_length));
+ if (rx_cfg->enable_length_byte == optionOn)
+ {
+ SET_CHECKED(rf69_set_payload_length(dev->spi, 0xff));
+ }
+ else if (rx_cfg->fixed_message_length != 0)
+ {
+ payload_length = rx_cfg->fixed_message_length;
+ if (rx_cfg->enable_length_byte == optionOn) payload_length++;
+ if (rx_cfg->enable_address_filtering != filteringOff) payload_length++;
+ SET_CHECKED(rf69_set_payload_length(dev->spi, payload_length));
+ }
+ else
+ {
+ SET_CHECKED(rf69_set_payload_length(dev->spi, 0));
+ }
+
+ /* values */
+ if (rx_cfg->enable_sync == optionOn)
+ {
+ SET_CHECKED(rf69_set_sync_values(dev->spi, rx_cfg->sync_pattern));
+ }
+ if (rx_cfg->enable_address_filtering != filteringOff)
+ {
+ SET_CHECKED(rf69_set_node_address (dev->spi, rx_cfg->node_address));
+ SET_CHECKED(rf69_set_broadcast_address(dev->spi, rx_cfg->broadcast_address));
+ }
+
+ return 0;
+}
+
+static int
+rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg)
+{
+ SET_CHECKED(rf69_set_frequency (dev->spi, tx_cfg->frequency));
+ SET_CHECKED(rf69_set_bit_rate (dev->spi, tx_cfg->bit_rate));
+ SET_CHECKED(rf69_set_modulation (dev->spi, tx_cfg->modulation));
+ SET_CHECKED(rf69_set_deviation (dev->spi, tx_cfg->dev_frequency));
+ SET_CHECKED(rf69_set_pa_ramp (dev->spi, tx_cfg->pa_ramp));
+ SET_CHECKED(rf69_set_modulation_shaping(dev->spi, tx_cfg->modShaping));
+ SET_CHECKED(rf69_set_tx_start_condition(dev->spi, tx_cfg->tx_start_condition));
+
+ /* packet format enable */
+ if (tx_cfg->enable_preamble == optionOn)
+ {
+ SET_CHECKED(rf69_set_preamble_length(dev->spi, tx_cfg->preamble_length));
+ }
+ else
+ {
+ SET_CHECKED(rf69_set_preamble_length(dev->spi, 0));
+ }
+ SET_CHECKED(rf69_set_sync_enable (dev->spi, tx_cfg->enable_sync));
+ SET_CHECKED(rf69_set_packet_format(dev->spi, tx_cfg->enable_length_byte));
+ SET_CHECKED(rf69_set_crc_enable (dev->spi, tx_cfg->enable_crc));
+
+ /* configure sync, if enabled */
+ if (tx_cfg->enable_sync == optionOn)
+ {
+ SET_CHECKED(rf69_set_sync_size(dev->spi, tx_cfg->sync_length));
+ SET_CHECKED(rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern));
+ }
+
+ 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 */
+ SET_CHECKED(rf69_set_dio_mapping(dev->spi, DIO0, DIO_Rssi_DIO0));
+ dev->irq_state[DIO0] = DIO_Rssi_DIO0;
+ irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING);
+
+ /* setup fifo level interrupt */
+ SET_CHECKED(rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD));
+ SET_CHECKED(rf69_set_dio_mapping(dev->spi, DIO1, DIO_FifoLevel));
+ dev->irq_state[DIO1] = DIO_FifoLevel;
+ irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING);
+
+ /* set module to receiving mode */
+ SET_CHECKED(rf69_set_mode(dev->spi, receive));
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int
+pi433_receive(void *data)
+{
+ struct pi433_device *dev = data;
+ struct spi_device *spi = dev->spi; /* needed for SET_CHECKED */
+ 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");
+ 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_get_flag(dev->spi, rssiExceededThreshold) )
+ {
+ /* 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");
+ retval = wait_event_interruptible(dev->rx_wait_queue,
+ rf69_get_flag(dev->spi,
+ rssiExceededThreshold));
+ if (retval) goto abort; /* wait was interrupted */
+ dev->interrupt_rx_allowed = false;
+
+ /* cross check for ongoing tx */
+ if (!dev->tx_active) break;
+ }
+
+ /* configure payload ready irq */
+ SET_CHECKED(rf69_set_dio_mapping(spi, DIO0, DIO_PayloadReady));
+ dev->irq_state[DIO0] = DIO_PayloadReady;
+ 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", bytes_total);
+ }
+ else
+ {
+ bytes_total = dev->rx_buffer_size;
+ dev_dbg(dev->dev, "rx: msg len set to %d as requested by read", bytes_total);
+ }
+
+ /* length byte enabled? */
+ if (dev->rx_cfg.enable_length_byte == optionOn)
+ {
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) goto abort; /* wait was interrupted */
+
+ 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", bytes_total);
+ }
+
+ /* address byte enabled? */
+ if (dev->rx_cfg.enable_address_filtering != filteringOff)
+ {
+ u8 dummy;
+
+ bytes_total--;
+
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) goto abort; /* wait was interrupted */
+
+ rf69_read_fifo(spi, &dummy, 1);
+ dev->free_in_fifo++;
+ dev_dbg(dev->dev, "rx: address byte stripped off");
+ }
+
+ /* get payload */
+ while (dev->rx_position < bytes_total)
+ {
+ if ( !rf69_get_flag(dev->spi, payloadReady) )
+ {
+ retval = wait_event_interruptible(dev->fifo_wait_queue,
+ dev->free_in_fifo < FIFO_SIZE);
+ if (retval) goto abort; /* wait was interrupted */
+ }
+
+ /* 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) goto abort; /* read failed */
+ 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 occured */
+abort:
+ dev->interrupt_rx_allowed = true;
+ SET_CHECKED(rf69_set_mode(dev->spi, standby));
+ wake_up_interruptible(&dev->tx_wait_queue);
+
+ if (retval)
+ return retval;
+ else
+ return bytes_total;
+}
+
+int
+pi433_tx_thread(void *data)
+{
+ struct pi433_device *device = data;
+ struct spi_device *spi = device->spi; /* needed for SET_CHECKED */
+ struct pi433_tx_cfg tx_cfg;
+ u8 buffer[MAX_MSG_SIZE];
+ 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");
+ 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 */
+ mutex_lock(&device->tx_fifo_lock);
+
+ 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", retval, (unsigned int)sizeof(tx_cfg) );
+ mutex_unlock(&device->tx_fifo_lock);
+ 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", retval, (unsigned int)sizeof(size_t) );
+ mutex_unlock(&device->tx_fifo_lock);
+ 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 == optionOn)
+ size++;
+
+ /* increase size, if adr byte is requested */
+ if (tx_cfg.enable_address_byte == optionOn)
+ size++;
+
+ /* prime buffer */
+ memset(buffer, 0, size);
+ position = 0;
+
+ /* add length byte, if requested */
+ if (tx_cfg.enable_length_byte == optionOn)
+ buffer[position++] = size-1; /* according to spec length byte itself must be excluded from the length calculation */
+
+ /* add adr byte, if requested */
+ if (tx_cfg.enable_address_byte == optionOn)
+ buffer[position++] = tx_cfg.address_byte;
+
+ /* finally get message data from fifo */
+ retval = kfifo_out(&device->tx_fifo, &buffer[position], sizeof(buffer)-position );
+ dev_dbg(device->dev, "read %d message byte(s) from fifo queue.", retval);
+ mutex_unlock(&device->tx_fifo_lock);
+
+ /* 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 == true);
+
+ /* prevent race conditions
+ irq will be reenabled after tx config is set */
+ disable_irq(device->irq_num[DIO0]);
+ device->tx_active = true;
+
+ if (device->rx_active && rx_interrupted == false)
+ {
+ /* rx is currently waiting for a telegram;
+ we need to set the radio module to standby */
+ SET_CHECKED(rf69_set_mode(device->spi, standby));
+ rx_interrupted = true;
+ }
+
+ /* clear fifo, set fifo threshold, set payload length */
+ SET_CHECKED(rf69_set_mode(spi, standby)); /* this clears the fifo */
+ SET_CHECKED(rf69_set_fifo_threshold(spi, FIFO_THRESHOLD));
+ if (tx_cfg.enable_length_byte == optionOn)
+ {
+ SET_CHECKED(rf69_set_payload_length(spi, size * tx_cfg.repetitions));
+ }
+ else
+ {
+ SET_CHECKED(rf69_set_payload_length(spi, 0));
+ }
+
+ /* configure the rf chip */
+ rf69_set_tx_cfg(device, &tx_cfg);
+
+ /* enable fifo level interrupt */
+ SET_CHECKED(rf69_set_dio_mapping(spi, DIO1, DIO_FifoLevel));
+ device->irq_state[DIO1] = DIO_FifoLevel;
+ irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING);
+
+ /* enable packet sent interrupt */
+ SET_CHECKED(rf69_set_dio_mapping(spi, DIO0, DIO_PacketSent));
+ device->irq_state[DIO0] = DIO_PacketSent;
+ 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 */
+ SET_CHECKED(rf69_set_mode(spi, transmit));
+
+ /* 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 temp = device->free_in_fifo;
+ device->free_in_fifo = 0;
+ rf69_write_fifo(spi,
+ &buffer[position],
+ temp);
+ position +=temp;
+ }
+ else
+ { /* msg fits into fifo - take all */
+ device->free_in_fifo -= size;
+ repetitions--;
+ rf69_write_fifo(spi,
+ &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) { printk("ABORT\n"); goto abort; }
+ }
+
+ /* we are done. Wait for packet to get sent */
+ dev_dbg(device->dev, "thread: wiat for packet to get sent/fifo to be empty");
+ wait_event_interruptible(device->fifo_wait_queue,
+ device->free_in_fifo == FIFO_SIZE ||
+ kthread_should_stop() );
+ if ( kthread_should_stop() ) printk("ABORT\n");
+
+
+ /* STOP_TRANSMISSION */
+ dev_dbg(device->dev, "thread: Packet sent. Set mode to stby.");
+ SET_CHECKED(rf69_set_mode(spi, standby));
+
+ /* 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;
+ }
+ else
+ {
+ 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 retval;
+ }
+
+ 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 copied, retval;
+
+ 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;
+
+ /* write the following sequence into fifo:
+ - tx_cfg
+ - size of message
+ - message */
+ mutex_lock(&device->tx_fifo_lock);
+ 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.", copied);
+
+ return 0;
+
+abort:
+ dev_dbg(device->dev, "write to fifo failed: 0x%x", retval);
+ kfifo_reset(&device->tx_fifo); // TODO: maybe find a solution, not to discard already stored, valid entries
+ mutex_unlock(&device->tx_fifo_lock);
+ return -EAGAIN;
+}
+
+
+static long
+pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ int retval = 0;
+ struct pi433_instance *instance;
+ struct pi433_device *device;
+ u32 tmp;
+
+ /* Check type and command number */
+ if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC)
+ return -ENOTTY;
+
+ /* Check access direction once here; don't repeat below.
+ * IOC_DIR is from the user perspective, while access_ok is
+ * from the kernel perspective; so they look reversed.
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err = !access_ok(VERIFY_WRITE,
+ (void __user *)arg,
+ _IOC_SIZE(cmd));
+
+ if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
+ err = !access_ok(VERIFY_READ,
+ (void __user *)arg,
+ _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ /* TODO? guard against device removal before, or while,
+ * we issue this ioctl. --> device_get()
+ */
+ instance = filp->private_data;
+ device = instance->device;
+
+ if (device == NULL)
+ return -ESHUTDOWN;
+
+ switch (cmd) {
+ case PI433_IOC_RD_TX_CFG:
+ tmp = _IOC_SIZE(cmd);
+ if ( (tmp == 0) || ((tmp % sizeof(struct pi433_tx_cfg)) != 0) )
+ {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (__copy_to_user((void __user *)arg,
+ &instance->tx_cfg,
+ tmp))
+ {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+ case PI433_IOC_WR_TX_CFG:
+ tmp = _IOC_SIZE(cmd);
+ if ( (tmp == 0) || ((tmp % sizeof(struct pi433_tx_cfg)) != 0) )
+ {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (__copy_from_user(&instance->tx_cfg,
+ (void __user *)arg,
+ tmp))
+ {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+
+ case PI433_IOC_RD_RX_CFG:
+ tmp = _IOC_SIZE(cmd);
+ if ( (tmp == 0) || ((tmp % sizeof(struct pi433_rx_cfg)) != 0) ) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (__copy_to_user((void __user *)arg,
+ &device->rx_cfg,
+ tmp))
+ {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+ case PI433_IOC_WR_RX_CFG:
+ tmp = _IOC_SIZE(cmd);
+ mutex_lock(&device->rx_lock);
+
+ /* during pendig read request, change of config not allowed */
+ if (device->rx_active) {
+ retval = -EAGAIN;
+ mutex_unlock(&device->rx_lock);
+ break;
+ }
+
+ if ( (tmp == 0) || ((tmp % sizeof(struct pi433_rx_cfg)) != 0) ) {
+ retval = -EINVAL;
+ mutex_unlock(&device->rx_lock);
+ break;
+ }
+
+ if (__copy_from_user(&device->rx_cfg,
+ (void __user *)arg,
+ tmp))
+ {
+ retval = -EFAULT;
+ mutex_unlock(&device->rx_lock);
+ break;
+ }
+
+ mutex_unlock(&device->rx_lock);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long
+pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#else
+#define pi433_compat_ioctl NULL
+#endif /* CONFIG_COMPAT */
+
+/*-------------------------------------------------------------------------*/
+
+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;
+ }
+
+ if (!device->rx_buffer) {
+ device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL);
+ if (!device->rx_buffer)
+ {
+ dev_dbg(device->dev, "open/ENOMEM\n");
+ return -ENOMEM;
+ }
+ }
+
+ device->users++;
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance)
+ {
+ kfree(device->rx_buffer);
+ device->rx_buffer = NULL;
+ return -ENOMEM;
+ }
+
+ /* setup instance data*/
+ instance->device = device;
+ instance->tx_cfg.bit_rate = 4711;
+ // TODO: fill instance->tx_cfg;
+
+ /* instance data as context */
+ filp->private_data = instance;
+ nonseekable_open(inode, filp);
+
+ return 0;
+}
+
+static int pi433_release(struct inode *inode, struct file *filp)
+{
+ struct pi433_instance *instance;
+ struct pi433_device *device;
+
+ instance = filp->private_data;
+ device = instance->device;
+ kfree(instance);
+ filp->private_data = NULL;
+
+ /* last close? */
+ device->users--;
+
+ if (!device->users) {
+ kfree(device->rx_buffer);
+ device->rx_buffer = NULL;
+ if (device->spi == NULL)
+ kfree(device);
+ }
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int setup_GPIOs(struct pi433_device *device)
+{
+ char name[5];
+ int retval;
+ int i;
+
+ 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.", name);
+ continue;
+ }
+
+ if (device->gpiod[i] == ERR_PTR(-EBUSY))
+ dev_dbg(&device->spi->dev, "%s is busy.", 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);//(struct gpio_desc *)device->irq_num[i];
+ 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 succesfully configured", name);
+ }
+
+ return 0;
+}
+
+static void free_GPIOs(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]);
+ }
+ return;
+}
+
+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->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 = pi433_compat_ioctl,
+ .open = pi433_open,
+ .release = pi433_release,
+ .llseek = no_llseek,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int pi433_probe(struct spi_device *spi)
+{
+ struct pi433_device *device;
+ int retval;
+
+ /* 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;
+ }
+ else
+ {
+ dev_dbg(&spi->dev,
+ "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed",
+ spi->mode, spi->bits_per_word, spi->max_speed_hz);
+ }
+
+ /* Ping the chip by reading the version register */
+ retval = spi_w8r8(spi, 0x10);
+ if (retval < 0)
+ return retval;
+
+ switch(retval)
+ {
+ case 0x24:
+ dev_dbg(&spi->dev, "fonud pi433 (ver. 0x%x)", retval);
+ break;
+ default:
+ dev_dbg(&spi->dev, "unknown chip version: 0x%x", 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 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_GPIOs(device);
+ if (retval)
+ {
+ dev_dbg(&spi->dev, "setup of GPIOs failed");
+ goto GPIO_failed;
+ }
+
+ /* setup the radio module */
+ SET_CHECKED(rf69_set_mode (spi, standby));
+ SET_CHECKED(rf69_set_data_mode (spi, packet));
+ SET_CHECKED(rf69_set_amplifier_0 (spi, optionOn));
+ SET_CHECKED(rf69_set_amplifier_1 (spi, optionOff));
+ SET_CHECKED(rf69_set_amplifier_2 (spi, optionOff));
+ SET_CHECKED(rf69_set_output_power_level (spi, 13));
+ SET_CHECKED(rf69_set_antenna_impedance (spi, fiftyOhm));
+
+ /* start tx thread */
+ device->tx_task_struct = kthread_run(pi433_tx_thread,
+ device,
+ "pi433_tx_task");
+ if (device->tx_task_struct < 0)
+ {
+ dev_dbg(device->dev, "start of send thread failed");
+ goto send_thread_failed;
+ }
+
+ /* determ minor number */
+ retval = pi433_get_minor(device);
+ if (retval)
+ {
+ dev_dbg(device->dev, "get of minor number failed");
+ 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");
+ 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);
+ }
+
+ /* create cdev */
+ device->cdev = cdev_alloc();
+ 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");
+ goto cdev_failed;
+ }
+
+ /* spi setup */
+ spi_set_drvdata(spi, device);
+
+ return 0;
+
+cdev_failed:
+ device_destroy(pi433_class, device->devt);
+device_create_failed:
+ pi433_free_minor(device);
+minor_failed:
+ kthread_stop(device->tx_task_struct);
+send_thread_failed:
+ free_GPIOs(device);
+GPIO_failed:
+ kfree(device);
+
+ return retval;
+}
+
+static int pi433_remove(struct spi_device *spi)
+{
+ struct pi433_device *device = spi_get_drvdata(spi);
+
+ /* free GPIOs */
+ free_GPIOs(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);
+
+ if (device->users == 0)
+ kfree(device);
+
+ return 0;
+}
+
+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
+ * Last, register the driver which manages those device numbers.
+ */
+ status = alloc_chrdev_region(&pi433_dev, 0 /*firstminor*/, N_PI433_MINORS /*count*/, "pi433" /*name*/);
+ 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);
+ }
+
+ 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);
+}
+module_exit(pi433_exit);
+
+MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>");
+MODULE_DESCRIPTION("Driver for Pi433");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:pi433");
--- /dev/null
+/*
+ * include/linux/TODO
+ *
+ * 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PI433_H
+#define PI433_H
+
+#include <linux/types.h>
+#include "rf69_enum.h"
+
+/*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*/
+
+/* IOCTL structs and commands */
+
+/**
+ * struct pi433_tx_config - describes the configuration of the radio module for sending
+ * @frequency:
+ * @bit_rate:
+ * @modulation:
+ * @data_mode:
+ * @preamble_length:
+ * @sync_pattern:
+ * @tx_start_condition:
+ * @payload_length:
+ * @repetitions:
+ *
+ * ATTENTION:
+ * If the contents of 'pi433_tx_config' 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 modShaping modShaping;
+
+ enum paRamp pa_ramp;
+
+ enum txStartCondition tx_start_condition;
+
+ __u16 repetitions;
+
+
+ /* packet format */
+ enum optionOnOff enable_preamble;
+ enum optionOnOff enable_sync;
+ enum optionOnOff enable_length_byte;
+ enum optionOnOff enable_address_byte;
+ enum optionOnOff enable_crc;
+
+ __u16 preamble_length;
+ __u8 sync_length;
+ __u8 fixed_message_length;
+
+ __u8 sync_pattern[8];
+ __u8 address_byte;
+};
+
+
+/**
+ * struct pi433_rx_config - describes the configuration of the radio module for sending
+ * @frequency:
+ * @bit_rate:
+ * @modulation:
+ * @data_mode:
+ * @preamble_length:
+ * @sync_pattern:
+ * @tx_start_condition:
+ * @payload_length:
+ * @repetitions:
+ *
+ * ATTENTION:
+ * If the contents of 'pi433_rx_config' 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 thresholdDecrement thresholdDecrement;
+ enum antennaImpedance antenna_impedance;
+ enum lnaGain lna_gain;
+ enum mantisse bw_mantisse; /* normal: 0x50 */
+ __u8 bw_exponent; /* during AFC: 0x8b */
+ enum dagc dagc;
+
+
+
+ /* packet format */
+ enum optionOnOff enable_sync;
+ enum optionOnOff enable_length_byte; /* should be used in combination with sync, only */
+ enum addressFiltering enable_address_filtering; /* operational with sync, only */
+ enum optionOnOff enable_crc; /* only operational, if sync on and fixed length or length byte is used */
+
+ __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 */
--- /dev/null
+/*
+ * abstraction of the spi interface of HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* enable prosa debug info */
+#undef DEBUG
+/* enable print of values on reg access */
+#undef DEBUG_VALUES
+/* enable print of values on fifo access */
+#undef DEBUG_FIFO_ACCESS
+
+#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 */
+
+/*-------------------------------------------------------------------------*/
+
+#define READ_REG(x) rf69_read_reg (spi, x)
+#define WRITE_REG(x,y) rf69_write_reg(spi, x, y)
+#define INVALID_PARAM \
+ { \
+ dev_dbg(&spi->dev, "set: illegal input param"); \
+ return -EINVAL; \
+ }
+
+/*-------------------------------------------------------------------------*/
+
+int rf69_set_mode(struct spi_device *spi, enum mode mode)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: mode");
+ #endif
+
+ switch (mode){
+ case transmit: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_TRANSMIT);
+ case receive: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_RECEIVE);
+ case synthesizer: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_SYNTHESIZER);
+ case standby: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_STANDBY);
+ case mode_sleep: return WRITE_REG(REG_OPMODE, (READ_REG(REG_OPMODE) & ~MASK_OPMODE_MODE) | OPMODE_MODE_SLEEP);
+ default: INVALID_PARAM;
+ }
+
+ // 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, enum dataMode dataMode)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: data mode");
+ #endif
+
+ switch (dataMode) {
+ case packet: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_PACKET);
+ case continuous: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_CONTINUOUS);
+ case continuousNoSync: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODE) | DATAMODUL_MODE_CONTINUOUS_NOSYNC);
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_modulation(struct spi_device *spi, enum modulation modulation)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: modulation");
+ #endif
+
+ switch (modulation) {
+ case OOK: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_TYPE) | DATAMODUL_MODULATION_TYPE_OOK);
+ case FSK: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_TYPE) | DATAMODUL_MODULATION_TYPE_FSK);
+ default: INVALID_PARAM;
+ }
+}
+
+enum modulation rf69_get_modulation(struct spi_device *spi)
+{
+ u8 currentValue;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "get: mode");
+ #endif
+
+ currentValue = READ_REG(REG_DATAMODUL);
+
+ switch (currentValue & MASK_DATAMODUL_MODULATION_TYPE >> 3) // TODO improvement: change 3 to define
+ {
+ case DATAMODUL_MODULATION_TYPE_OOK: return OOK;
+ case DATAMODUL_MODULATION_TYPE_FSK: return FSK;
+ default: return undefined;
+ }
+}
+
+int rf69_set_modulation_shaping(struct spi_device *spi, enum modShaping modShaping)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: mod shaping");
+ #endif
+
+ if (rf69_get_modulation(spi) == FSK)
+ {
+ switch (modShaping) {
+ case shapingOff: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_NONE);
+ case shaping1_0: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_1_0);
+ case shaping0_5: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_0_3);
+ case shaping0_3: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_0_5);
+ default: INVALID_PARAM;
+ }
+ }
+ else
+ {
+ switch (modShaping) {
+ case shapingOff: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_NONE);
+ case shapingBR: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_BR);
+ case shaping2BR: return WRITE_REG(REG_DATAMODUL, (READ_REG(REG_DATAMODUL) & ~MASK_DATAMODUL_MODULATION_SHAPE) | DATAMODUL_MODULATION_SHAPE_2BR);
+ default: INVALID_PARAM;
+ }
+ }
+}
+
+int rf69_set_bit_rate(struct spi_device *spi, u16 bitRate)
+{
+ int retval;
+ u32 bitRate_min;
+ u32 bitRate_reg;
+ u8 msb;
+ u8 lsb;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: bit rate");
+ #endif
+
+ // check input value
+ bitRate_min = F_OSC / 8388608; // 8388608 = 2^23;
+ if (bitRate < bitRate_min)
+ {
+ dev_dbg(&spi->dev, "setBitRate: illegal input param");
+ INVALID_PARAM;
+ }
+
+ // calculate reg settings
+ bitRate_reg = (F_OSC / bitRate);
+
+ msb = (bitRate_reg&0xff00) >> 8;
+ lsb = (bitRate_reg&0xff);
+
+ // transmit to RF 69
+ retval = WRITE_REG(REG_BITRATE_MSB, msb);
+ if (retval) return retval;
+ retval = WRITE_REG(REG_BITRATE_LSB, lsb);
+ if (retval) return retval;
+
+ return 0;
+}
+
+int rf69_set_deviation(struct spi_device *spi, u32 deviation)
+{
+ int retval;
+// u32 f_max; TODO: Abhängigkeit von Bitrate beachten!!
+ u64 f_reg;
+ u64 f_step;
+ u8 msb;
+ u8 lsb;
+ u64 factor = 1000000; // to improve precision of calculation
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: deviation");
+ #endif
+
+ if (deviation < 600 || deviation > 500000) //TODO: Abhängigkeit von Bitrate beachten!!
+ {
+ dev_dbg(&spi->dev, "set_deviation: illegal input param");
+ INVALID_PARAM;
+ }
+
+ // 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");
+ INVALID_PARAM;
+ }
+
+ // write to chip
+ retval = WRITE_REG(REG_FDEV_MSB, msb);
+ if (retval) return retval;
+ retval = WRITE_REG(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
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: frequency");
+ #endif
+
+ // calculat f step
+ f_step = F_OSC * factor;
+ do_div(f_step, 524288); // 524288 = 2^19
+
+ // check input value
+ f_max = f_step * 8388608 / factor;
+ if (frequency > f_max)
+ {
+ dev_dbg(&spi->dev, "setFrequency: illegal input param");
+ INVALID_PARAM;
+ }
+
+ // 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 = WRITE_REG(REG_FRF_MSB, msb);
+ if (retval) return retval;
+ retval = WRITE_REG(REG_FRF_MID, mid);
+ if (retval) return retval;
+ retval = WRITE_REG(REG_FRF_LSB, lsb);
+ if (retval) return retval;
+
+ return 0;
+}
+
+int rf69_set_amplifier_0(struct spi_device *spi, enum optionOnOff optionOnOff)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: amp #0");
+ #endif
+
+ switch(optionOnOff) {
+ case optionOn: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) | MASK_PALEVEL_PA0) );
+ case optionOff: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA0) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_amplifier_1(struct spi_device *spi, enum optionOnOff optionOnOff)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: amp #1");
+ #endif
+
+ switch(optionOnOff) {
+ case optionOn: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) | MASK_PALEVEL_PA1) );
+ case optionOff: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA1) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_amplifier_2(struct spi_device *spi, enum optionOnOff optionOnOff)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: amp #2");
+ #endif
+
+ switch(optionOnOff) {
+ case optionOn: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) | MASK_PALEVEL_PA2) );
+ case optionOff: return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_PA2) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_output_power_level(struct spi_device *spi, u8 powerLevel)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: power level");
+ #endif
+
+ powerLevel +=18; // TODO Abhängigkeit von PA0,1,2 setting
+
+ // check input value
+ if (powerLevel > 0x1f)
+ INVALID_PARAM;
+
+ // write value
+ return WRITE_REG(REG_PALEVEL, (READ_REG(REG_PALEVEL) & ~MASK_PALEVEL_OUTPUT_POWER) | powerLevel);
+}
+
+int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: pa ramp");
+ #endif
+
+ switch(paRamp) {
+ case ramp3400: return WRITE_REG(REG_PARAMP, PARAMP_3400);
+ case ramp2000: return WRITE_REG(REG_PARAMP, PARAMP_2000);
+ case ramp1000: return WRITE_REG(REG_PARAMP, PARAMP_1000);
+ case ramp500: return WRITE_REG(REG_PARAMP, PARAMP_500);
+ case ramp250: return WRITE_REG(REG_PARAMP, PARAMP_250);
+ case ramp125: return WRITE_REG(REG_PARAMP, PARAMP_125);
+ case ramp100: return WRITE_REG(REG_PARAMP, PARAMP_100);
+ case ramp62: return WRITE_REG(REG_PARAMP, PARAMP_62);
+ case ramp50: return WRITE_REG(REG_PARAMP, PARAMP_50);
+ case ramp40: return WRITE_REG(REG_PARAMP, PARAMP_40);
+ case ramp31: return WRITE_REG(REG_PARAMP, PARAMP_31);
+ case ramp25: return WRITE_REG(REG_PARAMP, PARAMP_25);
+ case ramp20: return WRITE_REG(REG_PARAMP, PARAMP_20);
+ case ramp15: return WRITE_REG(REG_PARAMP, PARAMP_15);
+ case ramp12: return WRITE_REG(REG_PARAMP, PARAMP_12);
+ case ramp10: return WRITE_REG(REG_PARAMP, PARAMP_10);
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance antennaImpedance)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: antenna impedance");
+ #endif
+
+ switch(antennaImpedance) {
+ case fiftyOhm: return WRITE_REG(REG_LNA, (READ_REG(REG_LNA) & ~MASK_LNA_ZIN) );
+ case twohundretOhm: return WRITE_REG(REG_LNA, (READ_REG(REG_LNA) | MASK_LNA_ZIN) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: lna gain");
+ #endif
+
+ switch(lnaGain) {
+ case automatic: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_AUTO) );
+ case max: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX) );
+ case maxMinus6: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_6) );
+ case maxMinus12: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_12) );
+ case maxMinus24: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_24) );
+ case maxMinus36: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_36) );
+ case maxMinus48: return WRITE_REG(REG_LNA, ( (READ_REG(REG_LNA) & ~MASK_LNA_GAIN) & LNA_GAIN_MAX_MINUS_48) );
+ default: INVALID_PARAM;
+ }
+}
+
+enum lnaGain rf69_get_lna_gain(struct spi_device *spi)
+{
+ u8 currentValue;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "get: lna gain");
+ #endif
+
+ currentValue = READ_REG(REG_LNA);
+
+ switch (currentValue & MASK_LNA_CURRENT_GAIN >> 3) // improvement: change 3 to define
+ {
+ case LNA_GAIN_AUTO: return automatic;
+ case LNA_GAIN_MAX: return max;
+ case LNA_GAIN_MAX_MINUS_6: return maxMinus6;
+ case LNA_GAIN_MAX_MINUS_12: return maxMinus12;
+ case LNA_GAIN_MAX_MINUS_24: return maxMinus24;
+ case LNA_GAIN_MAX_MINUS_36: return maxMinus36;
+ case LNA_GAIN_MAX_MINUS_48: return maxMinus48;
+ default: return undefined;
+ }
+}
+
+int rf69_set_dc_cut_off_frequency_intern(struct spi_device *spi ,u8 reg, enum dccPercent dccPercent)
+{
+ switch (dccPercent) {
+ case dcc16Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_16_PERCENT) );
+ case dcc8Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_8_PERCENT) );
+ case dcc4Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_4_PERCENT) );
+ case dcc2Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_2_PERCENT) );
+ case dcc1Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_1_PERCENT) );
+ case dcc0_5Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_5_PERCENT) );
+ case dcc0_25Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_25_PERCENT) );
+ case dcc0_125Percent: return WRITE_REG(reg, ( (READ_REG(reg) & ~MASK_BW_DCC_FREQ) | BW_DCC_0_125_PERCENT) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_dc_cut_off_frequency(struct spi_device *spi, enum dccPercent dccPercent)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: cut off freq");
+ #endif
+
+ return rf69_set_dc_cut_off_frequency_intern(spi, REG_RXBW, dccPercent);
+}
+
+int rf69_set_dc_cut_off_frequency_during_afc(struct spi_device *spi, enum dccPercent dccPercent)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: cut off freq during afc");
+ #endif
+
+ return rf69_set_dc_cut_off_frequency_intern(spi, REG_AFCBW, dccPercent);
+}
+
+int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg, enum mantisse mantisse, u8 exponent)
+{
+ u8 newValue;
+
+ // check value for mantisse and exponent
+ if (exponent > 7) INVALID_PARAM;
+ if ( (mantisse!=mantisse16) &&
+ (mantisse!=mantisse20) &&
+ (mantisse!=mantisse24) ) INVALID_PARAM;
+
+ // read old value
+ newValue = READ_REG(reg);
+
+ // "delete" mantisse and exponent = just keep the DCC setting
+ newValue = newValue & MASK_BW_DCC_FREQ;
+
+ // add new mantisse
+ switch(mantisse) {
+ case mantisse16: newValue = newValue | BW_MANT_16; break;
+ case mantisse20: newValue = newValue | BW_MANT_20; break;
+ case mantisse24: newValue = newValue | BW_MANT_24; break;
+ }
+
+ // add new exponent
+ newValue = newValue | exponent;
+
+ // write back
+ return WRITE_REG(reg, newValue);
+}
+
+int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse, u8 exponent)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: band width");
+ #endif
+
+ 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)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: band width during afc");
+ #endif
+
+ return rf69_set_bandwidth_intern(spi, REG_AFCBW, mantisse, exponent);
+}
+
+int rf69_set_ook_threshold_type(struct spi_device *spi, enum thresholdType thresholdType)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: threshold type");
+ #endif
+
+ switch (thresholdType)
+ {
+ case fixed: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_FIXED) );
+ case peak: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_PEAK) );
+ case average: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESTYPE) | OOKPEAK_THRESHTYPE_AVERAGE) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_ook_threshold_step(struct spi_device *spi, enum thresholdStep thresholdStep)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: threshold step");
+ #endif
+
+ switch (thresholdStep) {
+ case step_0_5db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_0_5_DB) );
+ case step_1_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_1_0_DB) );
+ case step_1_5db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_1_5_DB) );
+ case step_2_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_2_0_DB) );
+ case step_3_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_3_0_DB) );
+ case step_4_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_4_0_DB) );
+ case step_5_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_5_0_DB) );
+ case step_6_0db: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESSTEP) | OOKPEAK_THRESHSTEP_6_0_DB) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement thresholdDecrement)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: threshold decrement");
+ #endif
+
+ switch (thresholdDecrement) {
+ case dec_every8th: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_8TH) );
+ case dec_every4th: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_4TH) );
+ case dec_every2nd: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_EVERY_2ND) );
+ case dec_once: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_ONCE) );
+ case dec_twice: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_TWICE) );
+ case dec_4times: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_4_TIMES) );
+ case dec_8times: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_8_TIMES) );
+ case dec_16times: return WRITE_REG(REG_OOKPEAK, ( (READ_REG(REG_OOKPEAK) & ~MASK_OOKPEAK_THRESDEC) | OOKPEAK_THRESHDEC_16_TIMES) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value)
+{
+ u8 mask;
+ u8 shift;
+ u8 regaddr;
+ u8 regValue;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: DIO mapping");
+ #endif
+
+ // check DIO number
+ if (DIONumber > 5) INVALID_PARAM;
+
+ switch (DIONumber) {
+ case 0: mask=MASK_DIO0; shift=SHIFT_DIO0; regaddr=REG_DIOMAPPING1; break;
+ case 1: mask=MASK_DIO1; shift=SHIFT_DIO1; regaddr=REG_DIOMAPPING1; break;
+ case 2: mask=MASK_DIO2; shift=SHIFT_DIO2; regaddr=REG_DIOMAPPING1; break;
+ case 3: mask=MASK_DIO3; shift=SHIFT_DIO3; regaddr=REG_DIOMAPPING1; break;
+ case 4: mask=MASK_DIO4; shift=SHIFT_DIO4; regaddr=REG_DIOMAPPING2; break;
+ case 5: mask=MASK_DIO5; shift=SHIFT_DIO5; regaddr=REG_DIOMAPPING2; break;
+ }
+
+ // read reg
+ regValue=READ_REG(regaddr);
+ // delete old value
+ regValue = regValue & ~mask;
+ // add new value
+ regValue = regValue | value << shift;
+ // write back
+ return WRITE_REG(regaddr,regValue);
+}
+
+bool rf69_get_flag(struct spi_device *spi, enum flag flag)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "get: flag");
+ #endif
+
+ switch(flag) {
+ case modeSwitchCompleted: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_MODE_READY);
+ case readyToReceive: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_RX_READY);
+ case readyToSend: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_TX_READY);
+ case pllLocked: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_PLL_LOCK);
+ case rssiExceededThreshold: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_RSSI);
+ case timeout: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_TIMEOUT);
+ case automode: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_AUTOMODE);
+ case syncAddressMatch: return (READ_REG(REG_IRQFLAGS1) & MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH);
+ case fifoFull: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_FULL);
+/* case fifoNotEmpty: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_NOT_EMPTY); */
+ case fifoEmpty: return !(READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_NOT_EMPTY);
+ case fifoLevelBelowThreshold: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_LEVEL);
+ case fifoOverrun: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_FIFO_OVERRUN);
+ case packetSent: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_PACKET_SENT);
+ case payloadReady: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_PAYLOAD_READY);
+ case crcOk: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_CRC_OK);
+ case batteryLow: return (READ_REG(REG_IRQFLAGS2) & MASK_IRQFLAGS2_LOW_BAT);
+ default: return false;
+ }
+}
+
+int rf69_reset_flag(struct spi_device *spi, enum flag flag)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "reset: flag");
+ #endif
+
+ switch(flag) {
+ case rssiExceededThreshold: return WRITE_REG(REG_IRQFLAGS1, MASK_IRQFLAGS1_RSSI);
+ case syncAddressMatch: return WRITE_REG(REG_IRQFLAGS1, MASK_IRQFLAGS1_SYNC_ADDRESS_MATCH);
+ case fifoOverrun: return WRITE_REG(REG_IRQFLAGS2, MASK_IRQFLAGS2_FIFO_OVERRUN);
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: rssi threshold");
+ #endif
+
+ /* no value check needed - u8 exactly matches register size */
+
+ return WRITE_REG(REG_RSSITHRESH, threshold);
+}
+
+int rf69_set_rx_start_timeout(struct spi_device *spi, u8 timeout)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: start timeout");
+ #endif
+
+ /* no value check needed - u8 exactly matches register size */
+
+ return WRITE_REG(REG_RXTIMEOUT1, timeout);
+}
+
+int rf69_set_rssi_timeout(struct spi_device *spi, u8 timeout)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: rssi timeout");
+ #endif
+
+ /* no value check needed - u8 exactly matches register size */
+
+ return WRITE_REG(REG_RXTIMEOUT2, timeout);
+}
+
+int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength)
+{
+ int retval;
+ u8 msb, lsb;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: preample length");
+ #endif
+
+ /* no value check needed - u16 exactly matches register size */
+
+ /* calculate reg settings */
+ msb = (preambleLength&0xff00) >> 8;
+ lsb = (preambleLength&0xff);
+
+ /* transmit to chip */
+ retval = WRITE_REG(REG_PREAMBLE_MSB, msb);
+ if (retval) return retval;
+ retval = WRITE_REG(REG_PREAMBLE_LSB, lsb);
+
+ return retval;
+}
+
+int rf69_set_sync_enable(struct spi_device *spi, enum optionOnOff optionOnOff)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: sync enable");
+ #endif
+
+ switch(optionOnOff) {
+ case optionOn: return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) | MASK_SYNC_CONFIG_SYNC_ON) );
+ case optionOff: return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_ON) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifoFillCondition fifoFillCondition)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: fifo fill condition");
+ #endif
+
+ switch(fifoFillCondition) {
+ case always: return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) | MASK_SYNC_CONFIG_FIFO_FILL_CONDITION) );
+ case afterSyncInterrupt: return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_FIFO_FILL_CONDITION) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_sync_size(struct spi_device *spi, u8 syncSize)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: sync size");
+ #endif
+
+ // check input value
+ if (syncSize > 0x07)
+ INVALID_PARAM;
+
+ // write value
+ return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_SIZE) | (syncSize << 3) );
+}
+
+int rf69_set_sync_tolerance(struct spi_device *spi, u8 syncTolerance)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: sync tolerance");
+ #endif
+
+ // check input value
+ if (syncTolerance > 0x07)
+ INVALID_PARAM;
+
+ // write value
+ return WRITE_REG(REG_SYNC_CONFIG, (READ_REG(REG_SYNC_CONFIG) & ~MASK_SYNC_CONFIG_SYNC_SIZE) | syncTolerance);
+}
+
+int rf69_set_sync_values(struct spi_device *spi, u8 syncValues[8])
+{
+ int retval = 0;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: sync values");
+ #endif
+
+ retval += WRITE_REG(REG_SYNCVALUE1, syncValues[0]);
+ retval += WRITE_REG(REG_SYNCVALUE2, syncValues[1]);
+ retval += WRITE_REG(REG_SYNCVALUE3, syncValues[2]);
+ retval += WRITE_REG(REG_SYNCVALUE4, syncValues[3]);
+ retval += WRITE_REG(REG_SYNCVALUE5, syncValues[4]);
+ retval += WRITE_REG(REG_SYNCVALUE6, syncValues[5]);
+ retval += WRITE_REG(REG_SYNCVALUE7, syncValues[6]);
+ retval += WRITE_REG(REG_SYNCVALUE8, syncValues[7]);
+
+ return retval;
+}
+
+int rf69_set_packet_format(struct spi_device * spi, enum packetFormat packetFormat)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: packet format");
+ #endif
+
+ switch(packetFormat) {
+ case packetLengthVar: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) | MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE) );
+ case packetLengthFix: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_PAKET_FORMAT_VARIABLE) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_crc_enable(struct spi_device *spi, enum optionOnOff optionOnOff)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: crc enable");
+ #endif
+
+ switch(optionOnOff) {
+ case optionOn: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) | MASK_PACKETCONFIG1_CRC_ON) );
+ case optionOff: return WRITE_REG(REG_PACKETCONFIG1, (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_CRC_ON) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_adressFiltering(struct spi_device *spi, enum addressFiltering addressFiltering)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: address filtering");
+ #endif
+
+ switch (addressFiltering) {
+ case filteringOff: return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_OFF) );
+ case nodeAddress: return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_NODE) );
+ case nodeOrBroadcastAddress: return WRITE_REG(REG_PACKETCONFIG1, ( (READ_REG(REG_PACKETCONFIG1) & ~MASK_PACKETCONFIG1_ADDRESSFILTERING) | PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_payload_length(struct spi_device *spi, u8 payloadLength)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: payload length");
+ #endif
+
+ return WRITE_REG(REG_PAYLOAD_LENGTH, payloadLength);
+}
+
+u8 rf69_get_payload_length(struct spi_device *spi)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "get: payload length");
+ #endif
+
+ return (u8) READ_REG(REG_PAYLOAD_LENGTH);
+}
+
+int rf69_set_node_address(struct spi_device *spi, u8 nodeAddress)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: node address");
+ #endif
+
+ return WRITE_REG(REG_NODEADRS, nodeAddress);
+}
+
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcastAddress)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: broadcast address");
+ #endif
+
+ return WRITE_REG(REG_BROADCASTADRS, broadcastAddress);
+}
+
+int rf69_set_tx_start_condition(struct spi_device *spi, enum txStartCondition txStartCondition)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: start condition");
+ #endif
+
+ switch(txStartCondition) {
+ case fifoLevel: return WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) & ~MASK_FIFO_THRESH_TXSTART) );
+ case fifoNotEmpty: return WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) | MASK_FIFO_THRESH_TXSTART) );
+ default: INVALID_PARAM;
+ }
+}
+
+int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold)
+{
+ int retval;
+
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: fifo threshold");
+ #endif
+
+ // check input value
+ if (threshold & 0x80)
+ INVALID_PARAM;
+
+ // write value
+ retval = WRITE_REG(REG_FIFO_THRESH, (READ_REG(REG_FIFO_THRESH) & ~MASK_FIFO_THRESH_VALUE) | threshold);
+ if (retval)
+ return retval;
+
+ // access the fifo to activate new threshold
+ return rf69_read_fifo (spi, (u8*) &retval, 1); // retval used as buffer
+}
+
+int rf69_set_dagc(struct spi_device *spi, enum dagc dagc)
+{
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "set: dagc");
+ #endif
+
+ switch(dagc) {
+ case normalMode: return WRITE_REG(REG_TESTDAGC, DAGC_NORMAL);
+ case improve: return WRITE_REG(REG_TESTDAGC, DAGC_IMPROVED_LOWBETA0);
+ case improve4LowModulationIndex: return WRITE_REG(REG_TESTDAGC, DAGC_IMPROVED_LOWBETA1);
+ default: INVALID_PARAM;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+int rf69_read_fifo (struct spi_device *spi, u8 *buffer, unsigned int size)
+{
+ #ifdef DEBUG_FIFO_ACCESS
+ int i;
+ #endif
+ struct spi_transfer transfer;
+ u8 local_buffer[FIFO_SIZE + 1];
+ int retval;
+
+ if (size > FIFO_SIZE)
+ {
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "read fifo: passed in buffer bigger then internal buffer \n");
+ #endif
+ 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);
+
+ #ifdef DEBUG_FIFO_ACCESS
+ for (i=0; i<size; i++)
+ dev_dbg(&spi->dev, "%d - 0x%x\n", i, local_buffer[i+1]);
+ #endif
+
+ memcpy(buffer, &local_buffer[1], size); // TODO: ohne memcopy wäre schöner
+
+ return retval;
+}
+
+int rf69_write_fifo(struct spi_device *spi, u8 *buffer, unsigned int size)
+{
+ #ifdef DEBUG_FIFO_ACCESS
+ int i;
+ #endif
+ char spi_address = REG_FIFO | WRITE_BIT;
+ u8 local_buffer[FIFO_SIZE + 1];
+
+ if (size > FIFO_SIZE)
+ {
+ #ifdef DEBUG
+ dev_dbg(&spi->dev, "read fifo: passed in buffer bigger then internal buffer \n");
+ #endif
+ return -EMSGSIZE;
+ }
+
+ local_buffer[0] = spi_address;
+ memcpy(&local_buffer[1], buffer, size); // TODO: ohne memcopy wäre schöner
+
+ #ifdef DEBUG_FIFO_ACCESS
+ for (i=0; i<size; i++)
+ dev_dbg(&spi->dev, "0x%x\n",buffer[i]);
+ #endif
+
+ return spi_write (spi, local_buffer, size + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+u8 rf69_read_reg(struct spi_device *spi, u8 addr)
+{
+ int retval;
+
+ retval = spi_w8r8(spi, addr);
+
+ #ifdef DEBUG_VALUES
+ if (retval < 0)
+ /* should never happen, since we already checked,
+ that module is connected. Therefore no error
+ handling, just an optional error message... */
+ dev_dbg(&spi->dev, "read 0x%x FAILED\n",
+ addr);
+ else
+ dev_dbg(&spi->dev, "read 0x%x from reg 0x%x\n",
+ retval,
+ addr);
+ #endif
+
+ return retval;
+}
+
+int rf69_write_reg(struct spi_device *spi, u8 addr, u8 value)
+{
+ int retval;
+ char buffer[2];
+
+ buffer[0] = addr | WRITE_BIT;
+ buffer[1] = value;
+
+ retval = spi_write(spi, &buffer, 2);
+
+ #ifdef DEBUG_VALUES
+ if (retval < 0)
+ /* should never happen, since we already checked,
+ that module is connected. Therefore no error
+ handling, just an optional error message... */
+ dev_dbg(&spi->dev, "write 0x%x to 0x%x FAILED\n",
+ value,
+ addr);
+ else
+ dev_dbg(&spi->dev, "wrote 0x%x to reg 0x%x\n",
+ value,
+ addr);
+ #endif
+
+ return retval;
+}
+
+
--- /dev/null
+/*
+ * hardware abstraction/register access for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef RF69_H
+#define RF69_H
+
+#include "rf69_enum.h"
+#include "rf69_registers.h"
+
+#define F_OSC 32000000 /* in Hz */
+#define FREQUENCY 433920000 /* in Hz, modifying this value impacts CE certification */
+#define FIFO_SIZE 66 /* in byte */
+#define FIFO_THRESHOLD 15 /* in byte */
+
+int rf69_set_mode(struct spi_device *spi, enum mode mode);
+int rf69_set_data_mode(struct spi_device *spi, enum dataMode dataMode);
+int rf69_set_modulation(struct spi_device *spi, enum modulation modulation);
+enum modulation rf69_get_modulation(struct spi_device *spi);
+int rf69_set_modulation_shaping(struct spi_device *spi, enum modShaping modShaping);
+int rf69_set_bit_rate(struct spi_device *spi, u16 bitRate);
+int rf69_set_deviation(struct spi_device *spi, u32 deviation);
+int rf69_set_frequency(struct spi_device *spi, u32 frequency);
+int rf69_set_amplifier_0(struct spi_device *spi, enum optionOnOff optionOnOff);
+int rf69_set_amplifier_1(struct spi_device *spi, enum optionOnOff optionOnOff);
+int rf69_set_amplifier_2(struct spi_device *spi, enum optionOnOff optionOnOff);
+int rf69_set_output_power_level(struct spi_device *spi, u8 powerLevel);
+int rf69_set_pa_ramp(struct spi_device *spi, enum paRamp paRamp);
+int rf69_set_antenna_impedance(struct spi_device *spi, enum antennaImpedance antennaImpedance);
+int rf69_set_lna_gain(struct spi_device *spi, enum lnaGain lnaGain);
+enum lnaGain rf69_get_lna_gain(struct spi_device *spi);
+int rf69_set_dc_cut_off_frequency_intern(struct spi_device *spi, u8 reg, enum dccPercent dccPercent);
+int rf69_set_dc_cut_off_frequency(struct spi_device *spi, enum dccPercent dccPercent);
+int rf69_set_dc_cut_off_frequency_during_afc(struct spi_device *spi, enum dccPercent dccPercent);
+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_type(struct spi_device *spi, enum thresholdType thresholdType);
+int rf69_set_ook_threshold_step(struct spi_device *spi, enum thresholdStep thresholdStep);
+int rf69_set_ook_threshold_dec(struct spi_device *spi, enum thresholdDecrement thresholdDecrement);
+int rf69_set_dio_mapping(struct spi_device *spi, u8 DIONumber, u8 value);
+bool rf69_get_flag(struct spi_device *spi, enum flag flag);
+int rf69_reset_flag(struct spi_device *spi, enum flag flag);
+int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold);
+int rf69_set_rx_start_timeout(struct spi_device *spi, u8 timeout);
+int rf69_set_rssi_timeout(struct spi_device *spi, u8 timeout);
+int rf69_set_preamble_length(struct spi_device *spi, u16 preambleLength);
+int rf69_set_sync_enable(struct spi_device *spi, enum optionOnOff optionOnOff);
+int rf69_set_fifo_fill_condition(struct spi_device *spi, enum fifoFillCondition fifoFillCondition);
+int rf69_set_sync_size(struct spi_device *spi, u8 sync_size);
+int rf69_set_sync_tolerance(struct spi_device *spi, u8 syncTolerance);
+int rf69_set_sync_values(struct spi_device *spi, u8 syncValues[8]);
+int rf69_set_packet_format(struct spi_device * spi, enum packetFormat packetFormat);
+int rf69_set_crc_enable(struct spi_device *spi, enum optionOnOff optionOnOff);
+int rf69_set_adressFiltering(struct spi_device *spi, enum addressFiltering addressFiltering);
+int rf69_set_payload_length(struct spi_device *spi, u8 payloadLength);
+u8 rf69_get_payload_length(struct spi_device *spi);
+int rf69_set_node_address(struct spi_device *spi, u8 nodeAddress);
+int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcastAddress);
+int rf69_set_tx_start_condition(struct spi_device *spi, enum txStartCondition txStartCondition);
+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);
+
+u8 rf69_read_reg (struct spi_device *spi, u8 addr);
+int rf69_write_reg(struct spi_device *spi, u8 addr, u8 value);
+
+
+#endif
--- /dev/null
+/*
+ * enumerations for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef RF69_ENUM_H
+#define RF69_ENUM_H
+
+enum optionOnOff
+{
+ optionOff,
+ optionOn
+};
+
+enum mode
+{
+ mode_sleep,
+ standby,
+ synthesizer,
+ transmit,
+ receive
+};
+
+enum dataMode
+{
+ packet,
+ continuous,
+ continuousNoSync
+};
+
+enum modulation
+{
+ OOK,
+ FSK
+};
+
+enum modShaping
+{
+ shapingOff,
+ shaping1_0,
+ shaping0_5,
+ shaping0_3,
+ shapingBR,
+ shaping2BR
+};
+
+enum paRamp
+{
+ ramp3400,
+ ramp2000,
+ ramp1000,
+ ramp500,
+ ramp250,
+ ramp125,
+ ramp100,
+ ramp62,
+ ramp50,
+ ramp40,
+ ramp31,
+ ramp25,
+ ramp20,
+ ramp15,
+ ramp12,
+ ramp10
+};
+
+enum antennaImpedance
+{
+ fiftyOhm,
+ twohundretOhm
+};
+
+enum lnaGain
+{
+ automatic,
+ max,
+ maxMinus6,
+ maxMinus12,
+ maxMinus24,
+ maxMinus36,
+ maxMinus48,
+ undefined
+};
+
+enum dccPercent
+{
+ dcc16Percent,
+ dcc8Percent,
+ dcc4Percent,
+ dcc2Percent,
+ dcc1Percent,
+ dcc0_5Percent,
+ dcc0_25Percent,
+ dcc0_125Percent
+};
+
+enum mantisse
+{
+ mantisse16,
+ mantisse20,
+ mantisse24
+};
+
+enum thresholdType
+{
+ fixed,
+ peak,
+ average
+};
+
+enum thresholdStep
+{
+ step_0_5db,
+ step_1_0db,
+ step_1_5db,
+ step_2_0db,
+ step_3_0db,
+ step_4_0db,
+ step_5_0db,
+ step_6_0db
+};
+
+enum thresholdDecrement
+{
+ dec_every8th,
+ dec_every4th,
+ dec_every2nd,
+ dec_once,
+ dec_twice,
+ dec_4times,
+ dec_8times,
+ dec_16times
+};
+
+enum flag
+{
+ modeSwitchCompleted,
+ readyToReceive,
+ readyToSend,
+ pllLocked,
+ rssiExceededThreshold,
+ timeout,
+ automode,
+ syncAddressMatch,
+ fifoFull,
+// fifoNotEmpty, collision with next enum; replaced by following enum...
+ fifoEmpty,
+ fifoLevelBelowThreshold,
+ fifoOverrun,
+ packetSent,
+ payloadReady,
+ crcOk,
+ batteryLow
+};
+
+enum fifoFillCondition
+{
+ afterSyncInterrupt,
+ always
+};
+
+enum packetFormat
+{
+ packetLengthFix,
+ packetLengthVar
+};
+
+enum txStartCondition
+{
+ fifoLevel,
+ fifoNotEmpty
+};
+
+enum addressFiltering
+{
+ filteringOff,
+ nodeAddress,
+ nodeOrBroadcastAddress
+};
+
+enum dagc
+{
+ normalMode,
+ improve,
+ improve4LowModulationIndex
+};
+
+
+#endif
--- /dev/null
+/*
+ * register description for HopeRf rf69 radio module
+ *
+ * Copyright (C) 2016 Wolf-Entwicklungen
+ * Marcus Wolf <linux@wolf-entwicklungen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*******************************************/
+/* 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_TESTPA1 0x5A /* only present on RFM69HW */
+#define REG_TESTPA2 0x5C /* only present on RFM69HW */
+#define REG_TESTDAGC 0x6F
+
+/******************************************************/
+/* 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_ModeReady_DIO4 0x00
+#define DIO_ModeReady_DIO5 0x03
+#define DIO_ClkOut 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_RxReady 0x02
+#define DIO_PLLLock 0x03
+#define DIO_TxReady 0x01
+#define DIO_FifoFull_DIO1 0x01
+#define DIO_FifoFull_DIO3 0x00
+#define DIO_SyncAddress 0x02
+#define DIO_FifoNotEmpty_DIO1 0x02
+#define DIO_FifoNotEmpty_FIO2 0x00
+#define DIO_Automode 0x04
+#define DIO_FifoLevel 0x00
+#define DIO_CrcOk 0x00
+#define DIO_PayloadReady 0x01
+#define DIO_PacketSent 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_PAKET_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 */