[PATCH 2/2] misc: add support for the cc1101 RF transceiver chip from TI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This driver provides support for the Low-Power Sub-1 GHz RF
Transceiver chip from Texas Instruments. It provides a
simple message based protocol to set chip registers, send
and receive packets to/from the chip. For more details
see Documentation/misc-devices/cc1101.txt and
Documentation/devicetree/bindings/misc/cc1101.txt

Signed-off-by: Heiko Schocher <hs@xxxxxxx>
---

 Documentation/misc-devices/cc1101.txt |  446 ++++++
 drivers/misc/Kconfig                  |   11 +
 drivers/misc/Makefile                 |    1 +
 drivers/misc/cc1101.c                 | 2004 +++++++++++++++++++++++++
 drivers/misc/cc1101.h                 |   89 ++
 include/uapi/linux/cc1101_user.h      |  255 ++++
 6 files changed, 2806 insertions(+)
 create mode 100644 Documentation/misc-devices/cc1101.txt
 create mode 100644 drivers/misc/cc1101.c
 create mode 100644 drivers/misc/cc1101.h
 create mode 100644 include/uapi/linux/cc1101_user.h

diff --git a/Documentation/misc-devices/cc1101.txt b/Documentation/misc-devices/cc1101.txt
new file mode 100644
index 0000000000000..61d11baa70dad
--- /dev/null
+++ b/Documentation/misc-devices/cc1101.txt
@@ -0,0 +1,446 @@
+=============
+cc1101 driver
+=============
+
+abstract
+========
+
+This driver add support for the cc1101 Low-Power Sub-1 GHz RF Transceiver
+chip from Texas Instruments [1].
+
+The driver do not know anything about the data which is received
+or transmitted, beside the first byte is always a length byte, as
+this driver supports only variable packet length mode, see [1]
+chapter "0x08: PKTCTRL0 – Packet Automation Control"
+
+driver supports:
+
+- define different cc1101 register configurations. Each configuration
+  is referenced through a name.
+
+- define delay tables which are used for CSMA delay timeouts. Each
+  delay table is referenced through a name.
+
+- receive data from the cc1101 chip.
+
+- send data to the cc1101 chip
+
+  - optionally check if channel is free (CSMA). It is configurable
+    which delay table is used for the current packet, and how many
+    times the driver tries to get a free band.
+
+  - optionally send a preamble before the data, length of the preamble
+    is configurable through module parameter preamble_timeout in msec.
+
+- communication with userspace is message based
+
+- userspace can define a job, which contains more than one message
+  the driver executes each message.
+
+- driver can generate events (if enabled in register configuration)
+  which means the driver calculates all 100 ms the current rssi value
+  and send it to userspace.
+
+module parameter
+================
+
+preamble_timeout: timeout for sending preamble in msec
+                  default: 360
+
+DTS
+===
+
+see:
+Documentation/devicetree/bindings/misc/cc1101.txt
+
+cc1101 register settings
+========================
+
+For correct working the driver needs some register settings,
+you cannot change. Without them it makes no sense to use the
+driver (for example, the GDOx pins are used as IRQ pins, and
+if they are not setup in IRQ mode you get unexpected behaviour.
+For example on GDO0 outputs a 24MHz clock ... which will stall
+your cpu when the driver enables the GDO0 pin as irq).
+
+Following the register settings the driver checks:
+
+GDO0 setting
+............
+
+IOCFG0 (0x2) mode 0x6
+Asserts when sync word has been sent / received, and de-asserts
+at the end of the packet. In RX, the pin will also deassert when
+a packet is discarded due to address or maximum length filtering
+or when the radio enters RXFIFO_OVERFLOW state. In TX the pin
+will de-assert if the TX FIFO underflows.
+
+GDO2 setting
+............
+
+IOCFG2 (0x0) mode 0xe
+Carrier sense. High if RSSI level is above threshold.
+Cleared when entering IDLE mode.
+
+set automatic rx mode
+.....................
+
+MCSM1 (0x17): RXOFF_MODE und TXOFF_MODE must be 0xf
+so the cc1101 chip go into rx mode after tx or rx finished.
+
+If this bits are not set, driver tries to set rx mode after
+successful rx or tx. But this is slow and may leads in missing
+rx packets.
+
+variable packet length mode
+...........................
+
+This driver supports variable packet length mode only,
+see [1] chapter "0x08: PKTCTRL0 – Packet Automation Control"
+
+packet format
+.............
+
+only "normal" mode (use FIFOs for RX and TX) is supported.
+
+append status
+.............
+
+This setting is optional.
+
+PKTCTRL1 (0x07) APPEND_STATUS
+-> cc1101 appends 2 bytes to each received packet
+
+
+userspace interface
+===================
+
+The driver interface is message based, which means the Userspace
+has to setup messages and send them to the driver. Also, the driver
+use messages to communicate with the Userspace process.
+
+It could be needed to send more than one message to the driver
+at once, so messages can be grouped into one job, give the job an ID
+and send it to the driver, see "send a job". The driver than go through
+the single messages in the job and executes them. For example it is
+possible to set a register configuration A, send some data, switch than
+to register configuration B and send there again some data, switch than
+back to register setting A and switch to rx mode. If the driver has
+finsihed the job, or an error occured, the driver sends back
+a message to the userspace, see "infos for a finished job".
+
+common message format
+---------------------
+
+all messages in read and write direction start with:
+
++----------+...
+| int type |
++----------+...
+
+The content of type differs between read and write direction.
+
+write to driver
+---------------
+
+The following message types are possible:
+
+- CC1101_MSG_RCV
+- CC1101_MSG_DEFINE_CONFIG
+- CC1101_MSG_DEL_CONFIG
+- CC1101_MSG_JOB
+- CC1101_MSG_DEFINE_DELAY
+- CC1101_MSG_SET_PATABLE
+- CC1101_MSG_GET_CARRIER_SENSE
+
+set receive mode
+................
+
+Use this type for setting up the cc1101 registers with
+a register configuration, defined with CC1101_MSG_DEFINE_CONFIG
+and put the cc1101 chip into rx mode.
+
+setup the struct msg_queue_user_rcv
+
++----------------+-------------------------------+
+| CC1101_MSG_RCV | config_name[CFG_NAME_MAX_LEN] |
++----------------+-------------------------------+
+
+config_name: 0 terminated string, contains the name of a register setting.
+
+define a register configuration
+...............................
+
+type: CC1101_MSG_DEFINE_CONFIG
+
+define a register setting for the cc1101 and give it a name.
+This register setting can than be used for setting a specific
+register setting. Before a register setting is applied to the
+cc1101 chip, the driver resets the cc1101 chip, so all registers
+have default values, which means, you only have to define registers
+which have different values from the default value.
+
+If you use SmartRFstudio [2] you can export a register table,
+in c friendly format which exactly contains only the differences,
+and you can use it in your userspace program.
+
+setup the struct msg_queue_user_config_data
+
++--------------------------+-------------------------------+------------+-----------+----------+      +---+---+
+| CC1101_MSG_DEFINE_CONFIG | config_name[CFG_NAME_MAX_LEN] | char flags | char addr | char val | .... | 0 | 0 |
++--------------------------+-------------------------------+------------+-----------+----------+      +---+---+
+
+flags : CC1101_FLAG_CFG_*
+config_name: 0 terminated string, contains the name of a register setting.
+followed by a list of "addr | value" elements, terminated with "0 0"
+
+Userspace example:
+
+::
+
+  static const struct config_param demo_rx[] =
+  {
+  {CCxxx0_IOCFG0, 0x06},
+  {CCxxx0_FIFOTHR, 0x47},
+  {CCxxx0_PKTCTRL0, 0x05},
+  {CCxxx0_FSCTRL1, 0x06},
+  {CCxxx0_FREQ2, 0x21},
+  {CCxxx0_FREQ1, 0x62},
+  {CCxxx0_FREQ0, 0x76},
+  {CCxxx0_MDMCFG4, 0xC8},
+  {CCxxx0_MDMCFG3, 0x93},
+  {CCxxx0_MDMCFG2, 0x13},
+  {CCxxx0_DEVIATN, 0x34},
+  {CCxxx0_MCSM0, 0x18},
+  {CCxxx0_FOCCFG, 0x16},
+  {CCxxx0_AGCCTRL2, 0x43},
+  {CCxxx0_WORCTRL, 0xFB},
+  {CCxxx0_FSCAL3, 0xE9},
+  {CCxxx0_FSCAL2, 0x2A},
+  {CCxxx0_FSCAL1, 0x00},
+  {CCxxx0_FSCAL0, 0x1F},
+  {CCxxx0_TEST2, 0x81},
+  {CCxxx0_TEST1, 0x35},
+  {CCxxx0_TEST0, 0x09},
+  {CFG_END, CFG_END},
+  };
+
+  #define CONFIG_MAX_EL	30
+  struct config_demo {
+	int		type;
+	char		config_name[CFG_NAME_MAX_LEN];
+	struct		config_param cfg[CONFIG_MAX_EL];
+  };
+
+  struct config_demo cfg1;
+
+  memset(&cfg1, 0x0, sizeof(cfg1));
+  cfg1.type = CC1101_MSG_DEFINE_CONFIG;
+  memcpy(cfg1.cfg, demo_rx, sizeof(demo_rx));
+  sprintf(cfg1.config_name, "%s", "demo");
+  ret = write(fd, &cfg1, sizeof(cfg1));
+
+delete a register configuration
+...............................
+
+not implemented yet.
+
+type = CC1101_MSG_DEL_CONFIG
+
+define a delay timing configuration
+...................................
+
+With this message userspace can define a table of count min and max
+timeout values and give this table a name. Later when sending a
+packet with csma checking activated, this table can be selected from
+the CC1101_MSG_USER_TYPE_SEND and the timeouts from this table are
+used.
+
+type: CC1101_MSG_DEFINE_DELAY
+
+setup the struct msg_queue_user_delay_data
+
++-------------------------+------------------------------+-----------+---------------+---------------+      +---+
+| CC1101_MSG_DEFINE_DELAY | delay_name[CFG_NAME_MAX_LEN] | int count | int delay_min | int delay_max | .... |   |
++-------------------------+------------------------------+-----------+---------------+---------------+      +---+
+
+config_name: 0 terminated string, contains the name of a delay table.
+count: maximal delays
+delay: list of count delays (min/max pairs) in ms
+
+example:
+
+::
+
+  static const struct delay_param delay_table_first[] =
+  {
+     {1, 4},
+     {5, 9},
+     {16, 32},
+  };
+
+  struct delay_table {
+        struct  msg_queue_user msg_user;
+        struct  msg_delay_data_user delay_data_user;
+        struct  delay_param delays[MAX_DELAYS];
+  };
+
+  struct delay_table dt;
+  memset(&dt, 0x0, sizeof(dt));
+  dt.msg_user.type = CC1101_MSG_DEFINE_DELAY;
+  dt.delay_data_user.count = sizeof(delay_table_first) / sizeof(struct delay_param);
+  sprintf(dt.delay_data_user.delay_name, "%s", "delay_first");
+  memcpy(&dt.delays, &delay_table_first, sizeof(delay_table_first));
+  ret = write(fd, &dt, sizeof(dt));
+
+Now you can select this delay table under the name "delay_first" in the
+driver.
+
+set patable
+...........
+
+Use this type for setting up the cc1101 registers 0x3e
+(PATABLE)
+
+setup the struct msg_queue_user_rcv
+
++------------------------+------------+
+| CC1101_MSG_SET_PATABLE | char value |
++------------------------+------------+
+
+value : contains the patable value
+
+send a job
+..........
+
+use this message format:
+
++----------------+------------+         ...         +
+| CC1101_MSG_JOB | int job id | n * struct msg_user |
++----------------+------------+         ...         +
+
+job id:
+  int, give the job a unique id. The driver sends a CC1101_READ_TYPE_ID
+  message with this ID back to the userspace, when the job is finished.
+
+struct msg_user:
+
++----------+--------------+----------+--------------------------+
+| int type | int preamble | int csma | buf[CC1101_MAX_DATA_LEN] |
++----------+--------------+----------+--------------------------+
+
+with
+
+type    : CC1101_MSG_USER_TYPE_
+preamble: 0 = short other long preamble
+csma    : -1 no csma,
+           0 send only
+          >0 try n times to get free channel
+buf     : dependend on type contains different content
+
+
+possible message types:
+
+CC1101_MSG_USER_TYPE_SEND
+
+send data in buf, with preamble and csma settings
+buf[0] is the length of the data
+
+CC1101_MSG_USER_TYPE_SET_CFG
+
+set a register configuration with name in buf
+buf contains a 0 terminated string.
+preamble and csma not used, should be 0
+
+CC1101_MSG_USER_TYPE_SET_DELAY
+
+set a delay table with name in buf. This table is used
+when a CC1101_MSG_USER_TYPE_SEND message with csma > 0
+value runs later.
+
+CC1101_MSG_USER_TYPE_SET_PATABLE
+
+patable value in buf[0]
+
+get carrier sense event
+.......................
+
+message format:
+
++------------------------------+
+| CC1101_MSG_GET_CARRIER_SENSE |
++------------------------------+
+
+You get back an event message:
+
+type    : CC1101_READ_TYPE_EVENT
+eventid : CC1101_READ_EVENT_ID_CARRIER
+value   : value
+reason  : CC1101_READ_EVENT_REASON_CALCULATED
+
+see events.
+
+Only enabled, if eventgeneration (CC1101_FLAG_CFG_TRIGGER) is set
+in configuration flags of the current register configuration.
+
+read from driver
+----------------
+
+all messages in read direction start with:
+
++----------+...
+| int type |
++----------+...
+
+types:
+
+rx data
+.......
+
++----------+--------+-----------+----------+-------------------------------+
+| int type | int id | char rssi | char len | char buf[CC1101_MAX_DATA_LEN] |
++----------+--------+-----------+----------+-------------------------------+
+
+  type:  CC1101_READ_TYPE_RX
+  id   : not used should be 0
+  rssi : contain rssi value
+  len  : len of received bytes
+  buf  : received data + 2 additional data bytes RSSI and LQI
+
+infos for a finished job
+........................
+
++----------+--------+---------------+-----------------------------------+
+| int type | int id | int errorcode | char data[CC1101_MAX_DATA_LEN -2] |
++----------+--------+---------------+-----------------------------------+
+
+  type      : C1101_READ_TYPE_ID
+  id        : job id
+  errorcode : CC1101_ECA_*
+
+Error                       Errorcode  data
+all fine                    0          none
+channel busy                -1         string: config name
+SPI communication error     -2         none
+unknown config name         -3         string: name of unknown config
+error setting config        -4         string: name of config
+unknown delay name          -5         string: name of unknown table
+error setting patable       -6         none
+
+events
+......
+
++----------+-------------+-----------+-----------+--------------------------------+
+| int type | int eventid | u8 value  | u8 reason | char data[CC1101_MAX_DATA_LEN] |
++----------+-------------+-----------+-----------+--------------------------------+
+
+type    : CC1101_READ_TYPE_EVENT
+eventid : CC1101_READ_EVENT_ID_*
+value   : value
+reason  : CC1101_READ_EVENT_REASON_*
+
+links
+=====
+
+[1] datasheet: http://www.ti.com/lit/ds/symlink/cc1101.pdf
+[2] smartRFstudio: http://www.ti.com/tool/SMARTRFTM-STUDIO
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c55b63750757d..fe2c9b8b72a63 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -466,6 +466,17 @@ config PVPANIC
 	  a paravirtualized device provided by QEMU; it lets a virtual machine
 	  (guest) communicate panic events to the host.
 
+config CC1101
+	tristate "cc1101 device support"
+	depends on SPI
+	help
+	  This driver provides support for the Low-Power Sub-1 GHz RF
+	  Transceiver chip from Texas Instruments. It provides a
+	  simple message based protocol to set chip registers, send
+	  and receive packets to/from the chip. For more details
+	  see Documentation/misc-devices/cc1101.txt and
+	  Documentation/devicetree/bindings/misc/cc1101.txt
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c1860d35dc7e2..ec51e73359c48 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,3 +57,4 @@ obj-y				+= cardreader/
 obj-$(CONFIG_PVPANIC)   	+= pvpanic.o
 obj-$(CONFIG_HABANA_AI)		+= habanalabs/
 obj-$(CONFIG_XILINX_SDFEC)	+= xilinx_sdfec.o
+obj-$(CONFIG_CC1101)		+= cc1101.o
diff --git a/drivers/misc/cc1101.c b/drivers/misc/cc1101.c
new file mode 100644
index 0000000000000..432a33039ac02
--- /dev/null
+++ b/drivers/misc/cc1101.c
@@ -0,0 +1,2004 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Heiko Schocher <hs@xxxxxxx>
+ * DENX Software Engineering GmbH
+ *
+ */
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
+#include <linux/of_gpio.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/list.h>
+#include <linux/atomic.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi.h>
+#include <linux/cc1101_user.h>
+
+#include <cc1101.h>
+
+static int preamble_timeout = 360;
+module_param(preamble_timeout, int, 0644);
+MODULE_PARM_DESC(preamble_timeout,
+		 "timeout for long preamble in msec");
+
+#define CC1101_TIMEOUT_WAIT_IRQ		(5 * HZ)
+
+#define	CLASS_NAME	"rf868"
+#define DRV_NAME	"cc1101"
+#define DRV_VERSION	"1.00"
+#define CC1101_MAJOR	247
+
+static int opened;
+static int cc1101_major = CC1101_MAJOR;
+static struct class *cc1101_class;
+static struct device *cc1101_device;
+struct cc1101_data *g_cc1101;
+
+#define BITS_X_WORD		8
+
+#define CC1101_WRITE_SINGLE(A)  ((A)&(~0xC0))
+#define CC1101_WRITE_BURST(A)   (((A)&(~0xC0)) | 0x40)
+#define CC1101_READ_SINGLE(A)   (((A)&(~0xC0)) | 0x80)
+#define CC1101_READ_BURST(A)    (((A)&(~0xC0)) | 0xC0)
+
+#define FIFO_BYTES                      0x7F
+#define FIFO_UNDER_OVERFLOW             0x80
+
+/* max time in usec, for waiting after chip reset */
+#define CC1101_WAIT_CHIP_RESET	1000000
+
+struct msg_config_data {
+	struct	list_head	list;
+	char	config_name[CFG_NAME_MAX_LEN];
+	char	flags; /* CC1101_FLAG_CFG_* */
+	struct	config_param *cfg;
+} __packed;
+
+struct msg_delay_data {
+	struct	list_head	list;
+	struct	msg_delay_data_user mddu;
+	struct	delay_param *delay;
+} __packed;
+
+/* list of jobs to do */
+struct job_data {
+	struct	list_head	list;
+	struct job_data_user	jobu;
+	struct	list_head	msg_list;
+} __packed;
+
+/* one message in a job */
+struct msg_data {
+	struct	list_head	list;
+	struct msg_user		user;
+} __packed;
+
+/* list of data to user space */
+struct read_data {
+	struct	list_head	list;
+	struct	read_data_user	user;
+} __packed;
+
+struct cc1101_stat {
+	unsigned long flags;
+	int rx_pcts;
+	int rx_bytes;
+	int tx_pcts;
+	int tx_bytes;
+	int tx_miss;
+	int tx_signal;
+	int err_csma;
+	int err_dt_count;
+	int err_rx;
+	int err_rx_len;
+	int err_tx_busy;
+	int err_tx_count;
+	int err_tx_fifo_not_empty;
+	int err_tx_underflow;
+	int err_crc_drop;
+	int poll_reset;
+};
+
+#define CC1101_INIT_MEM		0x01
+#define CC1101_INIT_CHAR_DEV	0x02
+#define CC1101_INIT_CDEV	0x04
+#define CC1101_INIT_CLASS	0x08
+#define CC1101_INIT_DEVICE	0x10
+#define CC1101_INIT_WQ_IRQ	0x20
+#define CC1101_INIT_SYSFS	0x80
+
+struct cc1101_data {
+	/* structs */
+	struct cdev cdev;
+	struct spi_device	*spi;
+	wait_queue_head_t	poll_wait;
+	wait_queue_head_t	wait_tx_irq;
+	struct workqueue_struct *wq_irq;
+	struct work_struct	work;
+	struct msg_config_data	cfg_list;
+	struct mutex		lock_cfg_list;
+	struct msg_delay_data	delay_list;
+	struct mutex		lock_delay_list;
+	struct msg_delay_data	*cur_delay_table;
+	struct job_data		job_list;
+	struct mutex		lock_write;
+	struct read_data	read_list;
+	struct mutex		lock_read;
+	/* vars */
+	struct cc1101_stat	stat;
+	atomic_t		tx_active;
+	struct msg_config_data	*config_set;
+	unsigned long		init;
+	int			irq_ena;
+	int			csma_busy;
+	int			spi_transfer_error;
+	int			newjob;
+	int			append_status;
+	int			set_rx;
+	/* carrier sense event */
+	int			average;
+	u8			cur_val;
+	u8			old_val;
+	int			trigger;
+	atomic_t		dotimer;
+	atomic_t		doirq;
+	int			cancel_timer;
+	struct hrtimer		event_timer;
+	ktime_t			event_delay;
+	/* parameters */
+	u32			freq;
+	u32			gdo0; /* IRQ pin */
+	u32			gdo0_flags;
+	u32			gdo2; /* busy */
+	u32			gdo2_flags;
+	/* buffers */
+	char			buf[CC1101_MAX_USER_BUF];
+	u8 __aligned(4)		spi_rx_buf[CC1101_MAX_DATA_LEN];
+	u8 __aligned(4)		spi_tx_buf[CC1101_MAX_DATA_LEN];
+};
+
+static inline int
+cc1101_get_count_msg(size_t l)
+{
+	return (l - sizeof(struct job_data_user)) / sizeof(struct msg_user);
+}
+
+static inline int
+cc1101_check_count_msg(size_t l)
+{
+	return (l - sizeof(struct job_data_user)) % sizeof(struct msg_user);
+}
+
+static inline struct job_data_user
+*cc1101_get_job_data_user(char *buf)
+{
+	return (struct job_data_user *)&buf[sizeof(struct msg_queue_user)];
+}
+
+static inline struct msg_user
+*cc1101_get_msg_data_user(char *buf)
+{
+	int off;
+
+	off = sizeof(struct job_data_user) + sizeof(struct msg_queue_user);
+	return (struct msg_user *)&buf[off];
+}
+
+/* list functions */
+static int
+cc1101_cfg_add_list(struct cc1101_data *cc1101, struct msg_config_data *new)
+{
+	int ret = 0;
+
+	mutex_lock(&cc1101->lock_cfg_list);
+	list_add_tail(&(new->list), &(cc1101->cfg_list.list));
+	mutex_unlock(&cc1101->lock_cfg_list);
+
+	return ret;
+}
+
+static int cc1101_cfg_rm_list(struct cc1101_data *cc1101, char *name)
+{
+	struct msg_config_data *found, *tmp;
+	int	len = strlen(name);
+
+	mutex_lock(&cc1101->lock_cfg_list);
+	list_for_each_entry_safe(found, tmp,
+			&cc1101->cfg_list.list, list) {
+		if (strncmp(found->config_name, name, len) == 0) {
+			kfree(found->cfg);
+			list_del(&found->list);
+			kfree(found);
+			mutex_unlock(&cc1101->lock_cfg_list);
+			return 0;
+		}
+	}
+	mutex_unlock(&cc1101->lock_cfg_list);
+
+	return -EFAULT;
+}
+
+static int
+cc1101_cfg_create_new_list(struct cc1101_data *cc1101,
+			   struct msg_config_data_user *user,
+			   int len)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct msg_config_data *cfg_data;
+	struct config_param *cfg;
+	int	count;
+	int	ret;
+
+	cc1101_cfg_rm_list(cc1101, user->config_name);
+	cfg_data = kzalloc(sizeof(struct msg_config_data), GFP_ATOMIC);
+	if (!cfg_data) {
+		dev_err(&spi->dev, "could not alloc msg_config_data\n");
+		return -ENOMEM;
+	}
+
+	count = (len - CFG_NAME_MAX_LEN - 1) / sizeof(struct config_param);
+	/* plus one for end */
+	cfg = kzalloc(sizeof(struct config_param) * (count + 1), GFP_ATOMIC);
+	if (!cfg) {
+		dev_err(&spi->dev, "could not alloc config_param\n");
+		kfree(cfg_data);
+		return -ENOMEM;
+	}
+	strncpy(cfg_data->config_name, user->config_name, CFG_NAME_MAX_LEN);
+	cfg_data->flags = user->flags;
+	memcpy(cfg, &user->cfg, count * sizeof(struct config_param));
+	cfg[count].addr = CFG_END;
+	cfg[count].val = CFG_END;
+
+	cfg_data->cfg = cfg;
+
+	ret = cc1101_cfg_add_list(cc1101, cfg_data);
+
+	return ret;
+}
+
+static struct msg_config_data
+*cc1101_cfg_get_data_list(struct cc1101_data *cc1101, char *name)
+{
+	struct msg_config_data *found, *tmp;
+	int	len = strlen(name);
+
+	mutex_lock(&cc1101->lock_cfg_list);
+	list_for_each_entry_safe(found, tmp,
+			&cc1101->cfg_list.list, list) {
+		if (strncmp(found->config_name, name, len) == 0) {
+			mutex_unlock(&cc1101->lock_cfg_list);
+			return found;
+		}
+	}
+
+	mutex_unlock(&cc1101->lock_cfg_list);
+
+	return NULL;
+}
+
+static struct msg_delay_data
+*cc1101_delay_get_data_list(struct cc1101_data *cc1101, char *name)
+{
+	struct msg_delay_data *found, *tmp;
+	int	len = strlen(name);
+
+	mutex_lock(&cc1101->lock_delay_list);
+	list_for_each_entry_safe(found, tmp,
+			&cc1101->delay_list.list, list) {
+		if (strncmp(found->mddu.delay_name, name, len) == 0) {
+			mutex_unlock(&cc1101->lock_delay_list);
+			return found;
+		}
+	}
+
+	mutex_unlock(&cc1101->lock_delay_list);
+
+	return NULL;
+}
+
+static int cc1101_delay_rm_list(struct cc1101_data *cc1101, char *name)
+{
+	struct msg_delay_data *found;
+
+	found = cc1101_delay_get_data_list(cc1101, name);
+	if (!found)
+		return 0;
+
+	if (cc1101->cur_delay_table == found)
+		cc1101->cur_delay_table = NULL;
+
+	kfree(found->delay);
+	list_del(&found->list);
+	kfree(found);
+	return 0;
+}
+
+static int
+cc1101_delay_create_new_list(struct cc1101_data *cc1101,
+			     struct msg_queue_user_delay_data *user)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct msg_delay_data_user *delay_data_user = &user->delay_data_user;
+	struct msg_delay_data *delay_data;
+	int	count;
+	int	ret;
+
+	cc1101_delay_rm_list(cc1101, delay_data_user->delay_name);
+	count = delay_data_user->count;
+	delay_data = kzalloc(sizeof(struct msg_delay_data), GFP_ATOMIC);
+	if (!delay_data) {
+		dev_err(&spi->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	delay_data->delay = kzalloc(sizeof(struct delay_param) * count,
+				    GFP_ATOMIC);
+	if (!delay_data->delay) {
+		dev_err(&spi->dev, "could not alloc delay_param\n");
+		kfree(delay_data);
+		return -ENOMEM;
+	}
+
+	memcpy(&delay_data->mddu, delay_data_user,
+	       sizeof(struct msg_delay_data_user));
+	memcpy(delay_data->delay, &user->delays,
+	       count * sizeof(struct delay_param));
+
+	mutex_lock(&cc1101->lock_delay_list);
+	list_add_tail(&(delay_data->list), &(cc1101->delay_list.list));
+	mutex_unlock(&cc1101->lock_delay_list);
+
+	return ret;
+}
+
+static int cc1101_job_add(struct cc1101_data *cc1101, struct job_data *new)
+{
+	int ret = 0;
+
+	mutex_lock(&cc1101->lock_write);
+	list_add_tail(&(new->list), &(cc1101->job_list.list));
+	cc1101->newjob = 1;
+	mutex_unlock(&cc1101->lock_write);
+	queue_work(cc1101->wq_irq, &cc1101->work);
+
+	return ret;
+}
+
+static int cc1101_job_empty(struct cc1101_data *cc1101)
+{
+	int ret = 0;
+
+	mutex_lock(&cc1101->lock_write);
+	ret = list_empty(&cc1101->job_list.list);
+	cc1101->newjob = !ret;
+	mutex_unlock(&cc1101->lock_write);
+
+	return ret;
+}
+
+static struct job_data *cc1101_job_get(struct cc1101_data *cc1101)
+{
+	struct job_data *job;
+	int ret = 0;
+
+	mutex_lock(&cc1101->lock_write);
+	ret = list_empty(&cc1101->job_list.list);
+	cc1101->newjob = !ret;
+	if (ret) {
+		mutex_unlock(&cc1101->lock_write);
+		return NULL;
+	}
+
+	job = list_first_entry(&(cc1101->job_list.list), struct job_data,
+			       list);
+	list_del(&(job->list));
+	mutex_unlock(&cc1101->lock_write);
+	kfree(job);
+
+	return job;
+}
+
+static int cc1101_read_add(struct cc1101_data *cc1101, struct read_data *new)
+{
+	int ret = 0;
+
+	mutex_lock(&cc1101->lock_read);
+	list_add_tail(&(new->list), &(cc1101->read_list.list));
+	mutex_unlock(&cc1101->lock_read);
+
+	/* wakeup poll users */
+	wake_up_interruptible(&cc1101->poll_wait);
+
+	return ret;
+}
+
+static int cc1101_read_empty(struct cc1101_data *cc1101)
+{
+	int ret;
+
+	mutex_lock(&cc1101->lock_read);
+	ret = list_empty(&cc1101->read_list.list);
+	mutex_unlock(&cc1101->lock_read);
+
+	return ret;
+}
+
+static struct read_data *cc1101_read_get(struct cc1101_data *cc1101)
+{
+	struct read_data *read;
+
+	mutex_lock(&cc1101->lock_read);
+	if (list_empty(&cc1101->read_list.list)) {
+		mutex_unlock(&cc1101->lock_read);
+		return NULL;
+	}
+
+	read = list_first_entry(&(cc1101->read_list.list), struct read_data,
+				list);
+	list_del(&(read->list));
+	mutex_unlock(&cc1101->lock_read);
+
+	return read;
+}
+
+static int
+cc1101_msg_add(struct cc1101_data *cc1101, struct job_data *job,
+	       struct msg_data *new)
+{
+	int ret = 0;
+
+	list_add_tail(&(new->list), &(job->msg_list));
+	return ret;
+}
+
+static struct msg_data
+*cc1101_msg_get(struct cc1101_data *cc1101, struct job_data *job)
+{
+	struct msg_data *tmp;
+
+	if (list_empty(&job->msg_list))
+		return NULL;
+
+	tmp = list_first_entry(&(job->msg_list), struct msg_data, list);
+	return tmp;
+}
+
+static int cc1101_msg_free(struct cc1101_data *cc1101, struct msg_data *msg)
+{
+	list_del(&(msg->list));
+	kfree(msg);
+	return 0;
+}
+
+/* spi functions */
+
+/* The parameters for SPI are fixed as defined from chip */
+static void spi_cc1101_initialize(struct spi_device *spi)
+{
+	/* Configure the SPI bus */
+	spi->mode = SPI_MODE_3;
+	spi->bits_per_word = BITS_X_WORD;
+	spi_setup(spi);
+}
+
+static int cc1101_spi_transfer(struct cc1101_data *cc1101, u32 len)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct spi_message m;
+	struct spi_transfer t;
+	int ret = 0;
+
+	memset(&t, 0, sizeof(t));
+	t.tx_buf = cc1101->spi_tx_buf;
+	t.rx_buf = cc1101->spi_rx_buf;
+	t.len = len;
+	t.cs_change = 0;
+	if (cc1101->freq)
+		t.speed_hz = cc1101->freq;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(spi, &m);
+	if (ret)
+		dev_err(&spi->dev, "spi transfer failed\n");
+	cc1101->spi_transfer_error = ret;
+	return ret;
+}
+
+static int
+cc1101_write_reg(struct cc1101_data *cc1101, int reg, u8 data, u8 *status)
+{
+	struct spi_device *spi = cc1101->spi;
+	int len = 2;
+	int ret = 0;
+
+	cc1101->spi_tx_buf[0] = CC1101_WRITE_SINGLE(reg);
+	cc1101->spi_tx_buf[1] = data;
+	ret = cc1101_spi_transfer(cc1101, len);
+	if (ret < 0) {
+		dev_err(&spi->dev, "write reg failed: %x ret: %d\n", reg,
+			ret);
+		return ret;
+	}
+	*status = cc1101->spi_rx_buf[1];
+	return ret;
+}
+
+static int
+cc1101_write_reg_burst(struct cc1101_data *cc1101, int reg, u8 *data,
+		       int count, u8 *status)
+{
+	struct spi_device *spi = cc1101->spi;
+	int len = count + 1;
+	int ret = 0;
+
+	cc1101->spi_tx_buf[0] = CC1101_WRITE_BURST(reg);
+	memcpy(&cc1101->spi_tx_buf[1], data, count);
+	ret = cc1101_spi_transfer(cc1101, len);
+	if (ret < 0) {
+		dev_err(&spi->dev, "write reg burst failed: %x ret: %d\n",
+			reg, ret);
+		return ret;
+	}
+	*status = cc1101->spi_rx_buf[1];
+	return ret;
+}
+
+static int
+cc1101_read_reg(struct cc1101_data *cc1101, int reg, u8 *status, u8 *val)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret = 0;
+
+	if ((reg >= CCxxx0_IOCFG2) && (reg <= CCxxx0_TEST0)) {
+		cc1101->spi_tx_buf[0] = CC1101_READ_SINGLE(reg);
+	} else if ((reg >= CCxxx0_PARTNUM) && (reg <= CCxxx0_RXFIFO)) {
+		cc1101->spi_tx_buf[0] = CC1101_READ_BURST(reg);
+	} else {
+		dev_err(&spi->dev, "no valid register address: %x\n", reg);
+		return -EFAULT;
+	}
+	cc1101->spi_tx_buf[1] = 0;
+	ret = cc1101_spi_transfer(cc1101, 2);
+	if (ret < 0) {
+		dev_err(&spi->dev, "read reg failed: %x ret: %d\n", reg,
+			ret);
+		return ret;
+	}
+	*status = cc1101->spi_rx_buf[0];
+	*val = cc1101->spi_rx_buf[1];
+	return ret;
+}
+
+static int
+cc1101_read_reg_burst(struct cc1101_data *cc1101, int reg, u8 *data,
+		      int count, u8 *status)
+{
+	struct spi_device *spi = cc1101->spi;
+	int len = count + 1;
+	int ret = 0;
+
+	memset(cc1101->spi_tx_buf, 0, len);
+	if ((reg >= CCxxx0_PARTNUM) && (reg <= CCxxx0_RXFIFO)) {
+		cc1101->spi_tx_buf[0] = CC1101_READ_BURST(reg);
+	} else {
+		dev_err(&spi->dev, "no valid register address for burst: %x\n",
+			reg);
+		return -EFAULT;
+	}
+	ret = cc1101_spi_transfer(cc1101, len);
+	if (ret < 0) {
+		dev_err(&spi->dev, "read reg burst failed: %x ret: %d\n",
+			reg, ret);
+		return ret;
+	}
+	*status = cc1101->spi_rx_buf[0];
+	memcpy(data, &cc1101->spi_rx_buf[1], count);
+
+	return ret;
+}
+
+static int cc1101_cmd(struct cc1101_data *cc1101, int reg)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret = 0;
+	u8 status;
+
+	cc1101->spi_tx_buf[0] = CC1101_WRITE_SINGLE(reg);
+	cc1101->spi_rx_buf[0] = 0;
+	cc1101->spi_rx_buf[1] = 0;
+	ret = cc1101_spi_transfer(cc1101, 1);
+	if (ret < 0)
+		dev_err(&spi->dev, "command %x failed. ret: %d\n", reg, ret);
+
+	/* status table page 31 */
+	status = cc1101->spi_rx_buf[0];
+
+	return ret;
+}
+
+static void cc1101_sleep(int intervall)
+{
+	if (intervall > 10000) {
+		msleep(intervall / 1000);
+		return;
+	}
+	usleep_range(intervall, intervall);
+}
+
+/* chip functions */
+static int cc1101_start_rx_mode(struct cc1101_data *cc1101)
+{
+	struct spi_device *spi = cc1101->spi;
+
+	/* check, if we are not in Tx Mode */
+	if (atomic_read(&cc1101->tx_active) == 1) {
+		dev_err(&spi->dev, "we are in tx mode\n");
+		return -EBUSY;
+	}
+
+	/* switch to receive mode */
+	cc1101_cmd(cc1101, CCxxx0_SRX);
+
+	return 0;
+}
+
+static int
+cc1101_tx_packet(struct cc1101_data *cc1101, u8 *buf, u8 count, int preamble)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret;
+	u8 state;
+	u8 val;
+
+	if (preamble) {
+		atomic_set(&cc1101->tx_active, 1);
+		/* set into IDLE */
+		cc1101_cmd(cc1101, CCxxx0_SIDLE);
+		/* flush TX FIFO */
+		cc1101_cmd(cc1101, CCxxx0_SFTX);
+		/* enable freq synthesizer */
+		cc1101_cmd(cc1101, CCxxx0_SFSTXON);
+		/* enable TX */
+		cc1101_cmd(cc1101, CCxxx0_STX);
+
+		/*
+		 * now cc1101 starts with generating the preamble
+		 * wait preamble timeout
+		 */
+		cc1101_sleep(preamble_timeout * 1000);
+	}
+
+	ret = cc1101_write_reg_burst(cc1101, CCxxx0_TXFIFO, buf, count,
+				     &state);
+	if (ret < 0)
+		return ret;
+
+	if (!preamble) {
+		/* now start the Tx */
+		atomic_set(&cc1101->tx_active, 1);
+		cc1101_cmd(cc1101, CCxxx0_STX);
+	}
+
+	/* Now wait for Tx end IRQ */
+	ret = wait_event_interruptible_timeout(cc1101->wait_tx_irq,
+					       !atomic_read(&cc1101->tx_active),
+					       CC1101_TIMEOUT_WAIT_IRQ);
+	if (ret > 0) {
+		/* all fine, IRQ arrived */
+		ret = cc1101_read_reg(cc1101, CCxxx0_TXBYTES, &state, &val);
+		if ((ret != 0) || ((val & FIFO_BYTES) != 0)) {
+			dev_err(&spi->dev,
+				"%s: TX fifo underflow val: %x state: %x\n",
+				__func__, val, state);
+			cc1101->stat.err_tx_underflow++;
+			return -EIO;
+		}
+
+		cc1101->stat.tx_pcts++;
+		cc1101->stat.tx_bytes += count;
+		return 0;
+	} else if (ret == 0) {
+		/* timeout ... missing IRQ */
+		dev_err(&spi->dev, "%s IRQ missing!\n", __func__);
+		atomic_set(&cc1101->tx_active, 0);
+		cc1101->stat.tx_miss++;
+		return -EIO;
+	}
+
+	/* Interrupted through signal */
+	atomic_set(&cc1101->tx_active, 0);
+	cc1101->stat.tx_signal++;
+
+	return ret;
+}
+
+/*
+ * function should be called, after GDO0 is cleared
+ * -> end of packet received
+ */
+static int
+cc1101_rx_packet(struct cc1101_data *cc1101, u8 *buf, u8 *sz, u8 *rssi)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret;
+	u8 state;
+	u8 val;
+	u8 p_len;
+
+	ret = cc1101_read_reg(cc1101, CCxxx0_RXBYTES, &state, &val);
+	if (ret < 0)
+		return ret;
+
+	if (val & FIFO_UNDER_OVERFLOW) {
+		cc1101->stat.err_rx++;
+		dev_err(&spi->dev, "%s fifo underflow: state: %x val: %x\n",
+			__func__, state, val);
+		cc1101_read_reg(cc1101, CCxxx0_PKTSTATUS, &state, &val);
+		dev_err(&spi->dev, "%s pktstate: %x\n", __func__, val);
+		cc1101_cmd(cc1101, CCxxx0_SIDLE);
+		cc1101_cmd(cc1101, CCxxx0_SFRX);
+		cc1101_start_rx_mode(cc1101);
+		return -EPROTO;
+	} else if (!(val & FIFO_BYTES)) {
+		/* no packet due to filtering */
+		cc1101_read_reg(cc1101, CCxxx0_PKTSTATUS, &state, &val);
+		cc1101_cmd(cc1101, CCxxx0_SIDLE);
+		cc1101_cmd(cc1101, CCxxx0_SFRX);
+		cc1101_start_rx_mode(cc1101);
+		return -ENOENT;
+	}
+
+	ret = cc1101_read_reg(cc1101, CCxxx0_RXFIFO, &state, &p_len);
+	if (ret < 0)
+		return ret;
+
+	if (p_len > *sz) {
+		dev_err(&spi->dev, "%s p_len: %x > buffersize: %x\n",
+			__func__, p_len, *sz);
+		cc1101->stat.err_rx_len++;
+		cc1101_cmd(cc1101, CCxxx0_SIDLE);
+		cc1101_cmd(cc1101, CCxxx0_SFRX);
+		cc1101_start_rx_mode(cc1101);
+		return -ENOSPC;
+	}
+
+	/*
+	 * read 2 additional state bytes if
+	 * PKTCTRL1 (0x07) APPEND_STATUS is set.
+	 * state[0] = RSSI
+	 * state[1] = LQI
+	 */
+	ret = cc1101_read_reg_burst(cc1101, CCxxx0_RXFIFO, buf,
+				    p_len + cc1101->append_status, &state);
+	if (ret < 0)
+		return ret;
+
+	*sz = p_len;
+	cc1101->stat.rx_pcts++;
+	cc1101->stat.rx_bytes += p_len;
+
+	*rssi = 0;
+	if (cc1101->append_status) {
+		*rssi = buf[p_len];
+		if (!(buf[p_len + 1] & CRC_OK)) {
+			cc1101->stat.err_crc_drop++;
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+static int
+cc1101_check_register(struct cc1101_data *cc1101, int reg, u8 *val)
+{
+	struct spi_device *spi = cc1101->spi;
+
+	if (reg == CCxxx0_IOCFG2) {
+		if (*val != 0xe)
+			dev_err(&spi->dev,
+				"reg: %x val: %x not supported. Only mode 0xe allowed.\n",
+				reg, *val);
+		*val = 0xe;
+	}
+	if (reg == CCxxx0_IOCFG1) {
+		if (*val != 0x2e)
+			dev_err(&spi->dev,
+				"reg: %x val: %x not supported. Only mode 0x2e allowed.\n",
+				reg, *val);
+		*val = 0x2e;
+	}
+
+	if (reg == CCxxx0_IOCFG0) {
+		if (*val != 0x6)
+			dev_err(&spi->dev,
+				"reg: %x val: %x not supported. Only mode 0x6 allowed.\n",
+				reg, *val);
+		*val = 0x6;
+	}
+	if (reg == CCxxx0_PKTCTRL1) {
+		cc1101->append_status = 0;
+		/* check if append status bit is set */
+		if ((*val & CCxxx0_PKTCTRL1_APPEND_STATUS) ==
+		    CCxxx0_PKTCTRL1_APPEND_STATUS)
+			cc1101->append_status = 2;
+	}
+	if (reg == CCxxx0_PKTCTRL0) {
+		/* check if append status bit is set */
+		if ((*val & 0x30) != 0) {
+			dev_err(&spi->dev,
+				"reg: %x val: %x pkt_format must be 0\n",
+				reg, *val);
+		}
+		if ((*val & 0x03) != 1) {
+			dev_err(&spi->dev,
+				"reg: %x val: %x length config must be 1\n",
+				reg, *val);
+		}
+	}
+
+	if (reg == CCxxx0_MCSM1) {
+		cc1101->set_rx = 0;
+		if ((*val & 0xf) != 0xf) {
+			cc1101->set_rx = 1;
+			dev_err(&spi->dev,
+				"reg: %x val: %x not supported. Try to set Rx mode.\n",
+				reg, *val);
+		}
+	}
+	return 0;
+}
+
+static int cc1101_chip_reset(struct cc1101_data *cc1101)
+{
+	struct spi_device *spi = cc1101->spi;
+	ktime_t ktstart;
+	ktime_t delta_ktime;
+	u32 delta_usecs = 0;
+	u8 status, part;
+
+	/*
+	 * remove irq, as after reset default of gdo0 pin is 24MHz
+	 * which would stall our cpu.
+	 */
+	if (cc1101->irq_ena == 1) {
+		devm_free_irq(&spi->dev, gpio_to_irq(cc1101->gdo0), cc1101);
+		cc1101->irq_ena = 0;
+	}
+
+	/* reset */
+	cc1101_cmd(cc1101, 0);
+	cc1101_cmd(cc1101, CCxxx0_SRES);
+
+	/*
+	 * after a reset, we must poll the SO line, until it goes
+	 * low, see chapter 10.4 in the manual for the cc1101.
+	 *
+	 * Unfortunately, we cannot do this, so we read the
+	 * part number and check if it is 0.
+	 *
+	 * This works only for part number == 0, but we do check
+	 * the partnumber in this driver, so other supported
+	 * partnumbers must be added here too!
+	 */
+	cc1101->stat.poll_reset = 0;
+	ktstart = ktime_get();
+	cc1101_read_reg(cc1101, CCxxx0_PARTNUM, &status, &part);
+	while (part != 0) {
+		cc1101->stat.poll_reset++;
+		cc1101_read_reg(cc1101, CCxxx0_PARTNUM, &status, &part);
+		delta_ktime = ktime_sub(ktime_get(), ktstart);
+		delta_usecs = ktime_to_us(delta_ktime);
+		if (delta_usecs > CC1101_WAIT_CHIP_RESET) {
+			dev_dbg(&spi->dev,
+				"%s Timeout waiting for chip reset\n",
+				__func__);
+			return -EIO;
+		}
+	}
+
+	/* after reset we need a new config */
+	cc1101->config_set = NULL;
+	return 0;
+}
+
+static irqreturn_t cc1101_irq(int irq, void *pdata);
+
+static int
+cc1101_apply_config(struct cc1101_data *cc1101, struct msg_config_data *cfg)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct config_param *config = cfg->cfg;
+	int ret = 0;
+	int i;
+	int reg;
+	u8 val;
+
+	/* check if config is already set, so do not set it again. */
+	if (cc1101->config_set) {
+		if (cfg == cc1101->config_set) {
+			dev_dbg(&spi->dev, "config %s already set.\n",
+				cfg->config_name);
+			return -EEXIST;
+		}
+	}
+
+	/*
+	 * we reset the chip, as we know the default configuration
+	 * of the chip registers. So we need only to set register-
+	 * differences when we want a new configuration of the
+	 * chip.
+	 */
+	ret = cc1101_chip_reset(cc1101);
+	if (ret)
+		return ret;
+
+	i = 0;
+	while (!((config[i].addr == CFG_END) &&
+	       (config[i].val == CFG_END))) {
+		u8 status = 0;
+		u8 tmp;
+
+		reg = config[i].addr;
+		val = config[i].val;
+
+		cc1101_check_register(cc1101, reg, &val);
+		ret = cc1101_write_reg(cc1101, reg, val, &status);
+		if (ret != 0)
+			return ret;
+		ret = cc1101_read_reg(cc1101, reg, &status, &tmp);
+		if (ret != 0)
+			return ret;
+
+		if (tmp != val) {
+			dev_err(&spi->dev,
+				"config reg: %x failed: %x != %x\n", reg,
+				tmp, val);
+			return -EIO;
+		}
+		i++;
+	}
+
+	if (!cc1101->irq_ena) {
+		u32 active = (cc1101->gdo0_flags & GPIO_ACTIVE_LOW) ? 0 : 1;
+
+		/*
+		 * enable irq only after config is written
+		 * else we may have a wrong config for GDO0
+		 * default: there is a 24MHz clock output on it
+		 */
+		ret = devm_request_irq(&spi->dev, gpio_to_irq(cc1101->gdo0),
+				       cc1101_irq,
+				       (active ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING),
+				       "cc1101", cc1101);
+		if (ret)
+			return -ENODEV;
+
+		cc1101->irq_ena = 1;
+	}
+
+	cc1101->config_set = cfg;
+	return ret;
+}
+
+static int cc1101_detect_chip(struct cc1101_data *cc1101)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret = 0;
+	u8 status, part, vers;
+
+	ret = cc1101_chip_reset(cc1101);
+	if (ret != 0)
+		return ret;
+
+	ret = cc1101_read_reg(cc1101, CCxxx0_PARTNUM, &status, &part);
+	if (ret != 0)
+		return ret;
+
+	ret = cc1101_read_reg(cc1101, CCxxx0_VERSION, &status, &vers);
+	if (ret != 0)
+		return ret;
+
+	if ((part != 0) || (vers != 0x14)) {
+		dev_err(&spi->dev, "chip not found: part: %d version: %d\n",
+			part, vers);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int
+cc1101_startup(struct cc1101_data *cc1101, struct msg_config_data *cfg)
+{
+	int rx_notset;
+
+	rx_notset = cc1101_apply_config(cc1101, cfg);
+	if (rx_notset == -EEXIST)
+		rx_notset = 0;
+
+	atomic_set(&cc1101->tx_active, 0);
+	/* start in rx mode */
+	if (rx_notset == 0)
+		cc1101_start_rx_mode(cc1101);
+
+	return rx_notset;
+}
+
+/* GPIO Interrupt Handler. */
+static irqreturn_t cc1101_irq_gdo2(int irq, void *pdata)
+{
+	struct cc1101_data *cc1101 = (struct cc1101_data *)pdata;
+
+	cc1101->csma_busy = 1;
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t cc1101_irq(int irq, void *pdata)
+{
+	struct cc1101_data *cc1101 = (struct cc1101_data *)pdata;
+
+	if (atomic_read(&cc1101->tx_active) == 1) {
+		atomic_set(&cc1101->tx_active, 0);
+		wake_up(&cc1101->wait_tx_irq);
+	} else {
+		queue_work(cc1101->wq_irq, &cc1101->work);
+		atomic_inc(&cc1101->doirq);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* event specific functions */
+static int cc1101_send_event(struct cc1101_data *cc1101, u8 reason)
+{
+	struct read_data *new;
+	struct read_data_user_event *event;
+
+	new = kzalloc(sizeof(struct read_data), GFP_ATOMIC);
+	event = (struct read_data_user_event *)&new->user;
+	event->type = CC1101_READ_TYPE_EVENT;
+	event->eventid = CC1101_READ_EVENT_ID_CARRIER;
+	event->value = cc1101->cur_val;
+	event->reason = reason;
+
+	cc1101->old_val = cc1101->cur_val;
+	return cc1101_read_add(cc1101, new);
+}
+
+static int
+cc1101_check_carrier_sense_event(struct cc1101_data *cc1101, u8 reason)
+{
+	int ret = 0;
+
+	if (reason == CC1101_READ_EVENT_REASON_CALCULATED) {
+		ret = cc1101_send_event(cc1101, reason);
+		return ret;
+	}
+
+	/* if we have no running timer -> do not send event */
+	if (!hrtimer_active(&cc1101->event_timer))
+		return 0;
+
+	if (cc1101->cur_val > cc1101->old_val + 20)
+		ret = cc1101_send_event(cc1101,
+					CC1101_READ_EVENT_REASON_RISING);
+	if (cc1101->cur_val < cc1101->old_val - 20)
+		ret = cc1101_send_event(cc1101,
+					CC1101_READ_EVENT_REASON_FALLING);
+
+	return ret;
+}
+
+static int cc1101_calc_carrier_sense(struct cc1101_data *cc1101, u8 reason)
+{
+	struct spi_device *spi = cc1101->spi;
+	int val;
+	u16 carrier = 0;
+
+	if (atomic_read(&cc1101->tx_active) == 1) {
+		dev_dbg(&spi->dev, "do not calc carrier, as in tx mode\n");
+		return -EBUSY;
+	}
+
+	val = gpio_get_value(cc1101->gdo2);
+	if (val)
+		carrier = 200;
+
+	cc1101->average = cc1101->average - cc1101->cur_val + carrier;
+	cc1101->cur_val = cc1101->average >> 6;
+	cc1101_check_carrier_sense_event(cc1101, reason);
+
+	return 0;
+}
+
+static enum hrtimer_restart
+cc1101_event_timer_callback_nolock(struct hrtimer *timer)
+{
+	struct cc1101_data *cc1101 = container_of(timer, struct cc1101_data,
+						  event_timer);
+
+	atomic_inc(&cc1101->dotimer);
+	queue_work(cc1101->wq_irq, &cc1101->work);
+	if (cc1101->cancel_timer)
+		return HRTIMER_NORESTART;
+
+	hrtimer_forward_now(timer, ns_to_ktime(cc1101->event_delay));
+	return HRTIMER_RESTART;
+}
+
+static void cc1101_stop_event_timer(struct cc1101_data *cc1101)
+{
+	cc1101->cancel_timer = 1;
+	hrtimer_cancel(&cc1101->event_timer);
+}
+
+static void cc1101_start_event_timer(struct cc1101_data *cc1101)
+{
+	unsigned int delay = 100;
+	ktime_t softlimit = ms_to_ktime(delay);
+	int ret;
+
+	cc1101->cancel_timer = 0;
+	ret = hrtimer_active(&cc1101->event_timer);
+	if (ret)
+		return;
+
+	cc1101->event_delay = softlimit;
+	hrtimer_start(&cc1101->event_timer, softlimit, HRTIMER_MODE_REL);
+}
+
+/*
+ * check if channel is free (CSMA)
+ *
+ * csma    : -1 no csma,
+ *          0 send only
+ *         >0 try n times to get free channel
+ */
+static int cc1101_check_csma(struct cc1101_data *cc1101,
+			      struct msg_user *msg)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct  delay_param *dp;
+	int csma = msg->csma;
+	int val;
+	int wait;
+	int i = 0;
+	int ret;
+	char random;
+
+	if (csma <= 0)
+		return 0;
+
+	/* timeouts in cur_delay_table */
+	mutex_lock(&cc1101->lock_delay_list);
+	if (csma > cc1101->cur_delay_table->mddu.count) {
+		cc1101->stat.err_dt_count++;
+		mutex_unlock(&cc1101->lock_delay_list);
+		return -EFAULT;
+	}
+	dp = cc1101->cur_delay_table->delay;
+	mutex_unlock(&cc1101->lock_delay_list);
+
+	while (i < csma) {
+		/* wait for cc1101->cur_delay_table */
+		get_random_bytes(&random, 1);
+		wait = dp->min + (dp->max - dp->min) * random / 255;
+
+		val = gpio_get_value(cc1101->gdo2);
+		if (val == 0) {
+			cc1101->csma_busy = 0;
+			/*
+			 * use ggdo2 as interrupt
+			 * if gdo2 goes to 1 we know, that the slot is busy!
+			 */
+			ret = devm_request_irq(&spi->dev,
+					       gpio_to_irq(cc1101->gdo2),
+					       cc1101_irq_gdo2,
+					       IRQF_TRIGGER_RISING,
+					       "cc1101-gdo2", cc1101);
+			if (ret)
+				return -ENODEV;
+
+			cc1101_sleep(wait * 1000);
+			/* disable gdo2 irq */
+			devm_free_irq(&spi->dev, gpio_to_irq(cc1101->gdo2),
+				      cc1101);
+			if (cc1101->csma_busy == 0)
+				return 0;
+		} else {
+			/* slot is busy -> wait */
+			cc1101_sleep(dp->max * 1000);
+		}
+		i++;
+		dp++;
+	}
+
+	cc1101->stat.err_csma++;
+	return -EBUSY;
+}
+
+static int cc1101_handle_message_send(struct cc1101_data *cc1101,
+				      struct msg_user *msg)
+{
+	struct spi_device *spi = cc1101->spi;
+	int ret = 0;
+	u8 state;
+	u8 val;
+	int preamble = msg->preamble;
+
+	if ((atomic_read(&cc1101->tx_active) == 1) ||
+	    (cc1101->config_set == NULL)) {
+		cc1101->stat.err_tx_busy++;
+		dev_err(&spi->dev, "%s tx not possible %d %p\n", __func__,
+			atomic_read(&cc1101->tx_active), cc1101->config_set);
+		return -EBUSY;
+	}
+
+	/* check if Tx FIFO is empty */
+	ret = cc1101_read_reg(cc1101, CCxxx0_TXBYTES, &state, &val);
+	if (ret < 0)
+		return ret;
+
+	if (val != 0) {
+		cc1101->stat.err_tx_fifo_not_empty++;
+		dev_err(&spi->dev, "%s Tx FIFO not empty val: %x\n",
+			__func__, val);
+		cc1101_cmd(cc1101, CCxxx0_SIDLE);
+		cc1101_cmd(cc1101, CCxxx0_SFRX);
+		cc1101_cmd(cc1101, CCxxx0_SFTX);
+		cc1101_start_rx_mode(cc1101);
+	}
+
+	/* check CSMA */
+	ret = cc1101_check_csma(cc1101, msg);
+	if (ret) {
+		cc1101_start_rx_mode(cc1101);
+		return ret;
+	}
+
+	/* now we can send our message */
+	ret = cc1101_tx_packet(cc1101, msg->buf, msg->buf[0] + 1, preamble);
+
+	if (cc1101->set_rx)
+		/* always go back into rx mode */
+		cc1101_start_rx_mode(cc1101);
+
+	if (ret) {
+		dev_err(&spi->dev, "error sending: %d\n", ret);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int
+cc1101_handle_set_cfg(struct cc1101_data *cc1101, char *config_name)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct msg_config_data *config_data;
+	int ret = 0;
+
+	/* find config from name */
+	config_data = cc1101_cfg_get_data_list(cc1101, config_name);
+	if (!config_data) {
+		dev_err(&spi->dev, "unknown cfg: %s\n", config_name);
+		return -EINVAL;
+	}
+
+	cc1101_stop_event_timer(cc1101);
+
+	/* switch to config */
+	ret = cc1101_startup(cc1101, config_data);
+	if (ret) {
+		dev_err(&spi->dev, "error startup %d cfg: %s\n", ret,
+			config_name);
+		return -EFAULT;
+	}
+
+	/* start event timer */
+	if ((config_data->flags & CC1101_FLAG_CFG_TRIGGER) ==
+	    CC1101_FLAG_CFG_TRIGGER)
+		cc1101_start_event_timer(cc1101);
+
+	return 0;
+}
+
+static int
+cc1101_handle_set_delay(struct cc1101_data *cc1101, struct msg_user *msg)
+{
+	struct spi_device *spi = cc1101->spi;
+	unsigned char *name = msg->buf;
+	struct msg_delay_data *delay_data;
+
+	/* find config from name */
+	delay_data = cc1101_delay_get_data_list(cc1101, name);
+	if (!delay_data) {
+		dev_err(&spi->dev, "unknown delay table: %s\n", name);
+		return -EINVAL;
+	}
+
+	/* set table */
+	mutex_lock(&cc1101->lock_delay_list);
+	cc1101->cur_delay_table = delay_data;
+	mutex_unlock(&cc1101->lock_delay_list);
+
+	return 0;
+}
+
+static int
+cc1101_handle_set_patable(struct cc1101_data *cc1101, struct msg_user *msg)
+{
+	struct spi_device *spi = cc1101->spi;
+	u8 state;
+	int ret;
+
+	ret = cc1101_write_reg(cc1101, CCxxx0_PATABLE, msg->buf[0], &state);
+	if (ret) {
+		dev_err(&spi->dev, "error %d set patable: %x %x\n", ret,
+			msg->buf[0], state);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+cc1101_handle_message(struct cc1101_data *cc1101, struct msg_data *msg)
+{
+	struct spi_device *spi = cc1101->spi;
+	struct msg_user *msgu = &msg->user;
+	int type = msgu->type;
+	int ret = 0;
+
+	switch (type) {
+	case CC1101_MSG_USER_TYPE_SEND:
+		ret = cc1101_handle_message_send(cc1101, msgu);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_CFG:
+		ret = cc1101_handle_set_cfg(cc1101,
+					     (unsigned char *)msgu->buf);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_DELAY:
+		ret = cc1101_handle_set_delay(cc1101, msgu);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_PATABLE:
+		ret = cc1101_handle_set_patable(cc1101, msgu);
+		break;
+	default:
+		dev_err(&spi->dev, "unknown type: %d\n", type);
+		ret =  -EFAULT;
+		break;
+	}
+	return ret;
+}
+
+/*
+ * end job, as an error occurred.
+ * generate the message to userspace
+ */
+static struct read_data
+*cc1101_break_job(struct cc1101_data *cc1101, struct msg_data *msg,
+		  struct job_data *job, int retval)
+{
+	struct read_data *new;
+	struct read_data_user_ack *dtu;
+	struct msg_user *msgu = &msg->user;
+	int type = msgu->type;
+
+	/* init the message for the userspace */
+	new = kzalloc(sizeof(struct read_data), GFP_ATOMIC);
+	dtu = (struct read_data_user_ack *)&new->user;
+	dtu->type = CC1101_READ_TYPE_ID;
+	dtu->id = job->jobu.id;
+
+	dtu->errorcode = retval;
+	/* detect errorcode */
+	switch (type) {
+	case CC1101_MSG_USER_TYPE_SEND:
+		dtu->errorcode = CC1101_ECA_SEND;
+		memcpy(dtu->buf, cc1101->config_set->config_name,
+		       CFG_NAME_MAX_LEN);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_CFG:
+		if (retval == -EINVAL)
+			dtu->errorcode = CC1101_ECA_CFG_INVAL;
+		else
+			dtu->errorcode = CC1101_ECA_CFG;
+		memcpy(dtu->buf, msgu->buf,
+		       CC1101_MAX_DATA_LEN - 2);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_DELAY:
+		dtu->errorcode = CC1101_ECA_DELAY;
+		memcpy(dtu->buf, msgu->buf,
+		       CC1101_MAX_DATA_LEN - 2);
+		break;
+	case CC1101_MSG_USER_TYPE_SET_PATABLE:
+		dtu->errorcode = CC1101_ECA_PATABLE;
+		break;
+	}
+
+	/* in errorcase, delete all following messages */
+	while (msg != NULL) {
+		cc1101_msg_free(cc1101, msg);
+		msg = cc1101_msg_get(cc1101, job);
+	}
+
+	return new;
+}
+
+static int
+cc1101_handle_job(struct cc1101_data *cc1101, struct job_data *job)
+{
+	struct read_data *new = NULL;
+	struct read_data_user *dtu;
+	struct msg_data *msg;
+	int id = job->jobu.id;
+	int ret = 0;
+
+	msg = cc1101_msg_get(cc1101, job);
+	while (msg) {
+		/* handle message */
+		ret = cc1101_handle_message(cc1101, msg);
+		if (ret != 0) {
+			new = cc1101_break_job(cc1101, msg, job, ret);
+			msg = NULL;
+		} else {
+			cc1101_msg_free(cc1101, msg);
+			msg = cc1101_msg_get(cc1101, job);
+		}
+	}
+
+	if (!new) {
+		/* No error, Ack to userspace */
+		new = kzalloc(sizeof(struct read_data), GFP_ATOMIC);
+		dtu = &new->user;
+		dtu->type = CC1101_READ_TYPE_ID;
+		dtu->id = id;
+	}
+	if (cc1101->spi_transfer_error) {
+		struct read_data_user_ack *dua = (struct read_data_user_ack *)&new->user;
+
+		/* SPI communication problem */
+		dua->errorcode = CC1101_ECA_SPI_COM;
+	}
+	cc1101_read_add(cc1101, new);
+
+	return ret;
+}
+
+/* sysFS functions */
+static ssize_t
+cc1101_stat_show(struct device *dev, struct device_attribute *attr,
+		 char *buf)
+{
+	struct cc1101_data *cc1101 = g_cc1101;
+	struct cc1101_stat *st = &cc1101->stat;
+
+	return sprintf(buf, "%d %d %d %d %d %d %d %d\n",
+		       st->rx_pcts,
+		       st->rx_bytes,
+		       st->tx_pcts,
+		       st->tx_bytes,
+		       st->tx_miss,
+		       st->tx_signal,
+		       cc1101->append_status,
+		       cc1101->set_rx);
+}
+static DEVICE_ATTR_RO(cc1101_stat);
+
+static ssize_t
+cc1101_stat_error_show(struct device *dev, struct device_attribute *attr,
+		       char *buf)
+{
+	struct cc1101_data *cc1101 = g_cc1101;
+	struct cc1101_stat *st = &cc1101->stat;
+
+	return sprintf(buf, "%d %d %d %d %d %d %d %d %d %d\n",
+		       st->err_csma,
+		       st->err_dt_count,
+		       st->err_rx,
+		       st->err_rx_len,
+		       st->err_tx_fifo_not_empty,
+		       st->err_tx_count,
+		       st->err_tx_busy,
+		       st->err_tx_underflow,
+		       st->err_crc_drop,
+		       st->poll_reset);
+}
+static DEVICE_ATTR_RO(cc1101_stat_error);
+
+static int cc1101_create_sysfs(struct cc1101_data *cc1101)
+{
+	int ret = 0;
+
+	ret = device_create_file(cc1101_device, &dev_attr_cc1101_stat);
+	if (!ret)
+		cc1101->stat.flags |= 0x01;
+
+	ret = device_create_file(cc1101_device, &dev_attr_cc1101_stat_error);
+	if (!ret)
+		cc1101->stat.flags |= 0x02;
+
+	return ret;
+}
+
+static int cc1101_delete_sysfs(struct cc1101_data *cc1101)
+{
+	int ret = 0;
+
+	if ((cc1101->stat.flags & 0x01) == 0x01)
+		device_remove_file(cc1101_device, &dev_attr_cc1101_stat);
+
+	if ((cc1101->stat.flags & 0x02) == 0x02)
+		device_remove_file(cc1101_device,
+				   &dev_attr_cc1101_stat_error);
+	return ret;
+}
+
+/* device functions */
+static int cc1101_open(struct inode *inodep, struct file *filep)
+{
+	struct cc1101_data *cc1101 = g_cc1101;
+	struct spi_device *spi = cc1101->spi;
+	int ret;
+
+	if (opened)
+		return -EBUSY;
+
+	atomic_set(&cc1101->tx_active, 0);
+	filep->private_data = cc1101;
+	spi_cc1101_initialize(cc1101->spi);
+	ret = cc1101_detect_chip(cc1101);
+	if (ret != 0) {
+		dev_err(&spi->dev, "%s: could not find chip\n", DRV_NAME);
+		return ret;
+	}
+
+	cc1101_cmd(cc1101, CCxxx0_SIDLE);
+	cc1101_cmd(cc1101, CCxxx0_SFRX);
+	cc1101_cmd(cc1101, CCxxx0_SFTX);
+
+	hrtimer_init(&cc1101->event_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	cc1101->event_timer.function = cc1101_event_timer_callback_nolock;
+	cc1101_stop_event_timer(cc1101);
+	opened++;
+	return 0;
+}
+
+static ssize_t
+cc1101_read(struct file *filep, char *buffer, size_t len, loff_t *offset)
+{
+	struct cc1101_data *cc1101 = filep->private_data;
+	struct read_data *read;
+	struct  read_data_user	*user;
+	int ret;
+	int count = 0;
+	int pcklen = sizeof(struct read_data_user);
+
+	read = cc1101_read_get(cc1101);
+	while (read != NULL && (len >= count + pcklen)) {
+		user = &read->user;
+		ret = copy_to_user(&buffer[count], user, pcklen);
+		kfree(read);
+		if (ret != 0)
+			return -EFAULT;
+
+		count += pcklen;
+		read = cc1101_read_get(cc1101);
+	}
+
+	return count;
+}
+
+static ssize_t
+cc1101_write(struct file *filep, const char *buffer, size_t len,
+	     loff_t *offset)
+{
+	struct cc1101_data *cc1101 = filep->private_data;
+	struct spi_device *spi = cc1101->spi;
+	int ret;
+	struct msg_queue_user *msg;
+	struct msg_queue_user_config_data *config_data;
+	struct msg_queue_user_delay_data *dd;
+	struct msg_queue_user_rcv *rcv;
+	struct msg_queue_user_patable_data *pa;
+	struct job_data_user *user;
+	struct job_data *job;
+	struct msg_user	*msg_user;
+	struct msg_data	*msg_data;
+	int count;
+	int i;
+	u8 state;
+	u8 reason;
+	size_t l;
+
+	if (len > CC1101_MAX_USER_BUF)
+		return -ENOMEM;
+
+	ret = copy_from_user(&cc1101->buf[0], buffer, len);
+	if (ret)
+		return -EFAULT;
+
+	msg = (struct msg_queue_user *)cc1101->buf;
+	switch (msg->type) {
+	case CC1101_MSG_RCV:
+		/* set config */
+		rcv = (struct msg_queue_user_rcv *)cc1101->buf;
+		ret = cc1101_handle_set_cfg(cc1101,
+					    rcv->config_name);
+		break;
+	case CC1101_MSG_DEFINE_CONFIG:
+		config_data = (struct msg_queue_user_config_data *)cc1101->buf;
+		ret = cc1101_cfg_create_new_list(cc1101,
+						 &config_data->config_data_user,
+						 len - sizeof(int));
+		break;
+	case CC1101_MSG_DEFINE_DELAY:
+		dd = (struct msg_queue_user_delay_data *)cc1101->buf;
+		ret = cc1101_delay_create_new_list(cc1101, dd);
+		break;
+	case CC1101_MSG_GET_CARRIER_SENSE:
+		reason = CC1101_READ_EVENT_REASON_CALCULATED;
+		ret = cc1101_check_carrier_sense_event(cc1101, reason);
+		break;
+	case CC1101_MSG_SET_PATABLE:
+		/* set patable */
+		pa = (struct msg_queue_user_patable_data *)cc1101->buf;
+		ret = cc1101_write_reg(cc1101, CCxxx0_PATABLE,
+				       pa->patable_data_user.val, &state);
+		if (ret)
+			dev_err(&spi->dev, "error %d set patable: %x %x\n",
+				ret, pa->patable_data_user.val, state);
+		break;
+	case CC1101_MSG_JOB:
+		l = len - sizeof(struct msg_queue_user);
+		if (cc1101_check_count_msg(l)) {
+			dev_err(&spi->dev, "len: %d l: %d %d %d %d\n",
+				len, l, sizeof(struct msg_queue_user),
+				sizeof(struct job_data_user),
+				sizeof(struct msg_user));
+			return -EINVAL;
+		}
+		count = cc1101_get_count_msg(l);
+		user = cc1101_get_job_data_user(cc1101->buf);
+		job = kzalloc(sizeof(struct job_data), GFP_ATOMIC);
+		if (!job) {
+			dev_err(&spi->dev, "error job create, no mem\n");
+			return -ENOMEM;
+		}
+		job->jobu.id = user->id;
+		INIT_LIST_HEAD(&job->msg_list);
+		/* get now messages */
+		msg_user = cc1101_get_msg_data_user(cc1101->buf);
+		for (i = 0; i < count; i++) {
+			msg_data = kzalloc(sizeof(struct msg_data),
+					   GFP_ATOMIC);
+			memcpy(&msg_data->user, msg_user,
+			       sizeof(struct msg_user));
+			/* add to job list */
+			cc1101_msg_add(cc1101, job, msg_data);
+			msg_user++;
+		}
+		/* add job to job list */
+		cc1101_job_add(cc1101, job);
+		break;
+	default:
+		dev_err(&spi->dev, "unknown type: %d\n", msg->type);
+		ret = -EINVAL;
+	}
+
+	if (ret == 0)
+		return len;
+
+	return ret;
+}
+
+static int cc1101_release(struct inode *inodep, struct file *filep)
+{
+	struct cc1101_data *cc1101 = filep->private_data;
+	struct spi_device *spi = cc1101->spi;
+	struct read_data *rd;
+
+	if (!opened)
+		return 0;
+
+	/* wait for finishing all jobs */
+	while (!cc1101_job_empty(cc1101))
+		msleep(10);
+
+	cc1101_stop_event_timer(cc1101);
+
+	/* delete all messages to userspace */
+	rd = cc1101_read_get(cc1101);
+	while (rd != NULL)
+		rd = cc1101_read_get(cc1101);
+
+	atomic_set(&cc1101->dotimer, 0);
+	atomic_set(&cc1101->doirq, 0);
+	devm_free_irq(&spi->dev, gpio_to_irq(cc1101->gdo0), cc1101);
+	cc1101->irq_ena = 0;
+	opened--;
+
+	return 0;
+}
+
+static unsigned int
+cc1101_poll(struct file *filep, poll_table *wait)
+{
+	struct cc1101_data *cc1101 = filep->private_data;
+	unsigned int mask = 0;
+
+	poll_wait(filep, &cc1101->poll_wait, wait);
+	if (!cc1101_read_empty(cc1101))
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+
+static void cc1101_irq_work(struct cc1101_data *cc1101)
+{
+	struct read_data *new;
+	struct read_data_user *dt;
+	int ret;
+
+	/* called if rx data is there */
+	/* get data and save it in buffer */
+	new = kzalloc(sizeof(struct read_data), GFP_ATOMIC);
+	dt = &new->user;
+	dt->type = CC1101_READ_TYPE_RX;
+	dt->len = CC1101_MAX_DATA_LEN;
+	ret = cc1101_rx_packet(cc1101, dt->buf, &dt->len, &dt->rssi);
+	if (ret) {
+		kfree(new);
+		new = NULL;
+	}
+	if (new)
+		cc1101_read_add(cc1101, new);
+
+	/* we must enable rx mode again */
+	if (cc1101->set_rx)
+		cc1101_start_rx_mode(cc1101);
+}
+
+static void cc1101_work(struct work_struct *ws)
+{
+	struct cc1101_data *cc1101 = container_of(ws, struct cc1101_data,
+						  work);
+	struct job_data *job;
+
+	while (atomic_read(&cc1101->dotimer) || atomic_read(&cc1101->doirq)) {
+		if (atomic_read(&cc1101->doirq)) {
+			cc1101_irq_work(cc1101);
+			atomic_dec(&cc1101->doirq);
+		}
+		if (atomic_read(&cc1101->dotimer)) {
+			cc1101_calc_carrier_sense(cc1101,
+						  CC1101_READ_EVENT_REASON_CYCLE);
+			atomic_dec(&cc1101->dotimer);
+		}
+	}
+	job = cc1101_job_get(cc1101);
+	if (job)
+		cc1101_handle_job(cc1101, job);
+}
+
+static const struct file_operations cc1101_fops = {
+	.open = cc1101_open,
+	.read = cc1101_read,
+	.write = cc1101_write,
+	.release = cc1101_release,
+	.poll  = cc1101_poll,
+};
+
+static void cc1101_free(struct cc1101_data *cc1101)
+{
+	if ((cc1101->init & CC1101_INIT_SYSFS) == CC1101_INIT_SYSFS)
+		cc1101_delete_sysfs(cc1101);
+	if ((cc1101->init & CC1101_INIT_WQ_IRQ) == CC1101_INIT_WQ_IRQ) {
+		flush_workqueue(cc1101->wq_irq);
+		destroy_workqueue(cc1101->wq_irq);
+	}
+	if ((cc1101->init & CC1101_INIT_DEVICE) == CC1101_INIT_DEVICE)
+		device_destroy(cc1101_class, MKDEV(cc1101_major, 0));
+	if ((cc1101->init & CC1101_INIT_CLASS) == CC1101_INIT_CLASS)
+		class_destroy(cc1101_class);
+	if ((cc1101->init & CC1101_INIT_CDEV) == CC1101_INIT_CDEV)
+		cdev_del(&cc1101->cdev);
+	if ((cc1101->init & CC1101_INIT_CHAR_DEV) == CC1101_INIT_CHAR_DEV)
+		unregister_chrdev(cc1101_major, DRV_NAME);
+
+	g_cc1101 = NULL;
+}
+
+static int cc1101_probe(struct spi_device *spi)
+{
+	dev_t dev = MKDEV(CC1101_MAJOR, 0);
+	struct cc1101_data *cc1101;
+	u32 flags;
+	int ret = 0;
+	int freq = 0;
+
+	cc1101 = devm_kzalloc(&spi->dev, sizeof(*cc1101), GFP_ATOMIC);
+	if (!cc1101)
+		return -ENOMEM;
+
+	cc1101->init |= CC1101_INIT_MEM;
+	g_cc1101 = cc1101;
+
+	cc1101->spi = spi;
+	spi_set_drvdata(spi, cc1101);
+
+	if (!spi->dev.of_node) {
+		dev_err(&spi->dev, "no DTS info\n");
+		cc1101_free(cc1101);
+		return -ENODEV;
+	}
+
+	if (!freq)
+		of_property_read_u32(spi->dev.of_node,
+				     "freq", &cc1101->freq);
+
+	cc1101->gdo0 = of_get_gpio_flags(spi->dev.of_node, 0, &flags);
+	cc1101->gdo0_flags = flags;
+	if (!gpio_is_valid(cc1101->gdo0)) {
+		dev_err(&spi->dev, "irq GPIO: %d\n",
+			cc1101->gdo0);
+		cc1101_free(cc1101);
+		return -EINVAL;
+	}
+
+	cc1101->gdo2 = of_get_gpio_flags(spi->dev.of_node, 1, &flags);
+	cc1101->gdo2_flags = flags;
+	if (!gpio_is_valid(cc1101->gdo2)) {
+		dev_err(&spi->dev, "GDO2 GPIO: %d\n",
+			cc1101->gdo0);
+		cc1101_free(cc1101);
+		return -EINVAL;
+	}
+
+
+	ret = devm_gpio_request(&spi->dev, cc1101->gdo0, "cc1101-irq");
+	if (ret) {
+		dev_err(&spi->dev, "gpio %d cannot be acquired\n",
+			cc1101->gdo0);
+		cc1101_free(cc1101);
+		return -ENODEV;
+	}
+
+	ret = devm_gpio_request(&spi->dev, cc1101->gdo2, "cc1101-gdo2");
+	if (ret) {
+		dev_err(&spi->dev, "gpio %d cannot be acquired\n",
+			cc1101->gdo2);
+		cc1101_free(cc1101);
+		return -ENODEV;
+	}
+
+	/*
+	 * Get the GPIO used as interrupt. The Slave raises
+	 * an interrupt when receive data is there.
+	 */
+	gpio_direction_input(cc1101->gdo0);
+	gpio_direction_input(cc1101->gdo2);
+
+	cc1101_major = register_chrdev(0, DRV_NAME, &cc1101_fops);
+	if (cc1101_major < 0) {
+		dev_err(&spi->dev, "Could not get major %d\n", cc1101_major);
+		cc1101_free(cc1101);
+		return cc1101_major;
+	}
+	cc1101->init |= CC1101_INIT_CHAR_DEV;
+
+	cdev_init(&cc1101->cdev, &cc1101_fops);
+	ret = cdev_add(&cc1101->cdev, dev, 1);
+	if (ret) {
+		dev_err(&spi->dev, "ccdev init %d\n", ret);
+		cc1101_free(cc1101);
+		return ret;
+	}
+	cc1101->init |= CC1101_INIT_CDEV;
+
+	/* Register the device class */
+	cc1101_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(cc1101_class)) {
+		cc1101_free(cc1101);
+		return PTR_ERR(cc1101_class);
+	}
+	cc1101->init |= CC1101_INIT_CLASS;
+
+	/* Register the device driver */
+	cc1101_device = device_create(cc1101_class, NULL,
+				      MKDEV(cc1101_major, 0),
+				      cc1101, DRV_NAME);
+	if (IS_ERR(cc1101_device)) {
+		cc1101_free(cc1101);
+		return PTR_ERR(cc1101_device);
+	}
+	cc1101->init |= CC1101_INIT_DEVICE;
+
+	atomic_set(&cc1101->dotimer, 0);
+	atomic_set(&cc1101->doirq, 0);
+	INIT_WORK(&cc1101->work, cc1101_work);
+	init_waitqueue_head(&cc1101->wait_tx_irq);
+	/* stuff from userspace */
+	init_waitqueue_head(&cc1101->poll_wait);
+	INIT_LIST_HEAD(&cc1101->cfg_list.list);
+	mutex_init(&cc1101->lock_cfg_list);
+	INIT_LIST_HEAD(&cc1101->delay_list.list);
+	mutex_init(&cc1101->lock_delay_list);
+	INIT_LIST_HEAD(&cc1101->job_list.list);
+	mutex_init(&cc1101->lock_write);
+	/* stuff to userspace */
+	INIT_LIST_HEAD(&cc1101->read_list.list);
+	mutex_init(&cc1101->lock_read);
+	cc1101->wq_irq = alloc_workqueue("cc1101_wq_irq",
+					 WQ_HIGHPRI | WQ_FREEZABLE |
+					 WQ_MEM_RECLAIM, 0);
+	if (!cc1101->wq_irq) {
+		cc1101_free(cc1101);
+		return -ENOMEM;
+	}
+	cc1101->init |= CC1101_INIT_WQ_IRQ;
+
+	ret = cc1101_create_sysfs(cc1101);
+	if (ret) {
+		cc1101_free(cc1101);
+		return ret;
+	}
+	cc1101->init |= CC1101_INIT_SYSFS;
+
+	dev_info(&spi->dev, "%s version %s initialized, major: %d freq: %d preamble_timeout: %d %p\n",
+		 DRV_NAME, DRV_VERSION, cc1101_major, cc1101->freq,
+		 preamble_timeout, g_cc1101);
+
+	return ret;
+};
+
+static int cc1101_remove(struct spi_device *spi)
+{
+	struct cc1101_data *cc1101 = spi_get_drvdata(spi);
+	int ret = 0;
+
+	cc1101_free(cc1101);
+	return ret;
+};
+
+static const struct spi_device_id cc1101_ids[] = {
+	{DRV_NAME, 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, cc1101_ids);
+
+static const struct of_device_id cc1101_of_table[] = {
+	{ .compatible = "ti,cc1101" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cc1101_of_table);
+
+static struct spi_driver cc1101_driver = {
+	.driver = {
+		.name	= DRV_NAME,
+		.of_match_table = cc1101_of_table,
+		.bus = &spi_bus_type,
+	},
+	.id_table	= cc1101_ids,
+	.probe	= cc1101_probe,
+	.remove	= cc1101_remove,
+};
+module_spi_driver(cc1101_driver);
+
+MODULE_DESCRIPTION("TI cc1101 driver");
+MODULE_AUTHOR("Heiko Schocher hs@xxxxxxx");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/cc1101.h b/drivers/misc/cc1101.h
new file mode 100644
index 0000000000000..4877a5cf836f7
--- /dev/null
+++ b/drivers/misc/cc1101.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Heiko Schocher <hs@xxxxxxx>
+ * DENX Software Engineering GmbH
+ *
+ */
+#ifndef _CC1101_H
+#define _CC1101_H
+
+/* Strobe commands */
+
+// Reset chip.
+#define CCxxx0_SRES		0x30
+/*
+ * Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
+ * If in RX/TX: Go to a wait state where only the synthesizer is
+ * running (for quick RX / TX turnaround).
+ */
+#define CCxxx0_SFSTXON		0x31
+// Turn off crystal oscillator.
+#define CCxxx0_SXOFF		0x32
+/*
+ * Calibrate frequency synthesizer and turn it off
+ *(enables quick start).
+ */
+#define CCxxx0_SCAL		0x33
+/*
+ * Enable RX. Perform calibration first if coming from IDLE and
+ * MCSM0.FS_AUTOCAL=1.
+ */
+#define CCxxx0_SRX		0x34
+/*
+ * In IDLE state: Enable TX. Perform calibration first if
+ * MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled:
+ * Only go to TX if channel is clear.
+ */
+#define CCxxx0_STX		0x35
+/*
+ * Exit RX / TX, turn off frequency synthesizer and exit
+ * Wake-On-Radio mode if applicable.
+ */
+#define CCxxx0_SIDLE		0x36
+// Perform AFC adjustment of the frequency synthesizer
+#define CCxxx0_SAFC		0x37
+// Start automatic RX polling sequence (Wake-on-Radio)
+#define CCxxx0_SWOR		0x38
+// Enter power down mode when CSn goes high.
+#define CCxxx0_SPWD		0x39
+// Flush the RX FIFO buffer.
+#define CCxxx0_SFRX		0x3A
+// Flush the TX FIFO buffer.
+#define CCxxx0_SFTX		0x3B
+// Reset real time clock.
+#define CCxxx0_SWORRST		0x3C
+/*
+ * No operation. May be used to pad strobe commands to two
+ * bytes for simpler software.
+ */
+#define CCxxx0_SNOP		0x3D
+
+#define CCxxx0_PARTNUM		0x30
+#define CCxxx0_VERSION		0x31
+#define CCxxx0_FREQEST		0x32
+#define CCxxx0_LQI		0x33
+#define CCxxx0_RSSI		0x34
+#define CCxxx0_MARCSTATE	0x35
+#define CCxxx0_WORTIME1		0x36
+#define CCxxx0_WORTIME0		0x37
+#define CCxxx0_PKTSTATUS	0x38
+#define CCxxx0_VCO_VC_DAC	0x39
+#define CCxxx0_TXBYTES		0x3A
+#define CCxxx0_RXBYTES		0x3B
+#define CCxxx0_RCCTRL1_STATUS	0x3C
+#define CCxxx0_RCCTRL0_STATUS	0x3D
+
+#define CCxxx0_PATABLE		0x3E
+#define CCxxx0_TXFIFO		0x3F
+#define CCxxx0_RXFIFO		0x3F
+
+/* main state machine mode in status byte */
+#define MSMM			0x70
+#define IS_RXFIFO_OVERFLOW(status)      (((status) & (0x80)) == 0x80)
+#define IS_TXFIFO_UNDERFLOW(status)     (((status) & (0x80)) == 0x80)
+
+#define CRC_OK                  0x80
+
+#define CCxxx0_PKTCTRL1_APPEND_STATUS	0x04
+
+#endif /* _CC1101_H */
diff --git a/include/uapi/linux/cc1101_user.h b/include/uapi/linux/cc1101_user.h
new file mode 100644
index 0000000000000..20dca5271a9ad
--- /dev/null
+++ b/include/uapi/linux/cc1101_user.h
@@ -0,0 +1,255 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Heiko Schocher <hs@xxxxxxx>
+ * DENX Software Engineering GmbH
+ *
+ */
+#ifndef _CC1101_UAPI_H
+#define _CC1101_UAPI_H
+
+// GDO2 output pin configuration
+#define CCxxx0_IOCFG2       0x00
+// GDO1 output pin configuration
+#define CCxxx0_IOCFG1       0x01
+// GDO0 output pin configuration
+#define CCxxx0_IOCFG0       0x02
+// RX FIFO and TX FIFO thresholds
+#define CCxxx0_FIFOTHR      0x03
+// Sync word, high byte
+#define CCxxx0_SYNC1        0x04
+// Sync word, low byte
+#define CCxxx0_SYNC0        0x05
+// Packet length
+#define CCxxx0_PKTLEN       0x06
+// Packet automation control
+#define CCxxx0_PKTCTRL1     0x07
+// Packet automation control
+#define CCxxx0_PKTCTRL0     0x08
+// Device address
+#define CCxxx0_ADDR         0x09
+// Channel number
+#define CCxxx0_CHANNR       0x0A
+// Frequency synthesizer control
+#define CCxxx0_FSCTRL1      0x0B
+// Frequency synthesizer control
+#define CCxxx0_FSCTRL0      0x0C
+// Frequency control word, high byte
+#define CCxxx0_FREQ2        0x0D
+// Frequency control word, middle byte
+#define CCxxx0_FREQ1        0x0E
+// Frequency control word, low byte
+#define CCxxx0_FREQ0        0x0F
+// Modem configuration
+#define CCxxx0_MDMCFG4      0x10
+// Modem configuration
+#define CCxxx0_MDMCFG3      0x11
+// Modem configuration
+#define CCxxx0_MDMCFG2      0x12
+// Modem configuration
+#define CCxxx0_MDMCFG1      0x13
+// Modem configuration
+#define CCxxx0_MDMCFG0      0x14
+// Modem deviation setting
+#define CCxxx0_DEVIATN      0x15
+// Main Radio Control State Machine configuration
+#define CCxxx0_MCSM2        0x16
+// Main Radio Control State Machine configuration
+#define CCxxx0_MCSM1        0x17
+// Main Radio Control State Machine configuration
+#define CCxxx0_MCSM0        0x18
+// Frequency Offset Compensation configuration
+#define CCxxx0_FOCCFG       0x19
+// Bit Synchronization configuration
+#define CCxxx0_BSCFG        0x1A
+// AGC control
+#define CCxxx0_AGCCTRL2     0x1B
+// AGC control
+#define CCxxx0_AGCCTRL1     0x1C
+// AGC control
+#define CCxxx0_AGCCTRL0     0x1D
+// High byte Event 0 timeout
+#define CCxxx0_WOREVT1      0x1E
+// Low byte Event 0 timeout
+#define CCxxx0_WOREVT0      0x1F
+// Wake On Radio control
+#define CCxxx0_WORCTRL      0x20
+// Front end RX configuration
+#define CCxxx0_FREND1       0x21
+// Front end TX configuration
+#define CCxxx0_FREND0       0x22
+// Frequency synthesizer calibration
+#define CCxxx0_FSCAL3       0x23
+// Frequency synthesizer calibration
+#define CCxxx0_FSCAL2       0x24
+// Frequency synthesizer calibration
+#define CCxxx0_FSCAL1       0x25
+// Frequency synthesizer calibration
+#define CCxxx0_FSCAL0       0x26
+// RC oscillator configuration
+#define CCxxx0_RCCTRL1      0x27
+// RC oscillator configuration
+#define CCxxx0_RCCTRL0      0x28
+// Frequency synthesizer calibration control
+#define CCxxx0_FSTEST       0x29
+// Production test
+#define CCxxx0_PTEST        0x2A
+// AGC test
+#define CCxxx0_AGCTEST      0x2B
+// Various test settings
+#define CCxxx0_TEST2        0x2C
+// Various test settings
+#define CCxxx0_TEST1        0x2D
+// Various test settings
+#define CCxxx0_TEST0        0x2E
+// RSSI
+#define CCxxx0_RSSI         0x34
+// PA Table
+#define CCxxx0_PATABLE      0x3E
+
+#define CFG_NAME_MAX_LEN	20
+/* 1 byte length + max length 64 + 2 bytes status */
+#define CC1101_MAX_DATA_LEN	(1 + 64 + 2)
+
+#define CC1101_MAX_USER_BUF	1024
+
+/* Message IDs */
+#define	CC1101_MSG_RCV			2
+#define	CC1101_MSG_DEFINE_CONFIG	3
+#define	CC1101_MSG_DEL_CONFIG		4
+#define	CC1101_MSG_JOB			5
+#define	CC1101_MSG_DEFINE_DELAY		6
+#define	CC1101_MSG_SET_PATABLE		7
+#define	CC1101_MSG_GET_CARRIER_SENSE	8
+
+struct __attribute__ ((packed)) msg_queue_user {
+	int	type; /* CC1101_MSG_SET_ */
+};
+
+/* CC1101_MSG_DEFINE_CONFIG */
+struct __attribute__ ((packed)) config_param {
+	char addr;
+	char val;
+};
+#define CFG_END	0x0
+
+#define CC1101_FLAG_CFG_TRIGGER		0x01
+
+struct __attribute__ ((packed)) msg_config_data_user {
+	char	config_name[CFG_NAME_MAX_LEN];
+	char	flags; /* CC1101_FLAG_CFG_* */
+	struct	config_param cfg;
+};
+
+struct __attribute__ ((packed)) msg_queue_user_config_data {
+	struct msg_queue_user msg_user;
+	struct msg_config_data_user config_data_user;
+};
+
+/* CC1101_MSG_DEFINE_DELAY */
+struct __attribute__ ((packed)) delay_param {
+	int	min;
+	int	max;
+};
+
+struct __attribute__ ((packed)) msg_delay_data_user {
+	char	delay_name[CFG_NAME_MAX_LEN];
+	int	count;
+};
+
+struct __attribute__ ((packed)) msg_queue_user_delay_data {
+	struct	msg_queue_user msg_user;
+	struct	msg_delay_data_user delay_data_user;
+	struct	delay_param delays;
+};
+
+/* CC1101_MSG_SET_PATABLE */
+struct __attribute__ ((packed)) msg_patable_data_user {
+	char	val;
+};
+
+struct __attribute__ ((packed)) msg_queue_user_patable_data {
+	struct	msg_queue_user msg_user;
+	struct	msg_patable_data_user patable_data_user;
+};
+
+/* CC1101_MSG_RCV */
+struct __attribute__ ((packed)) msg_queue_user_rcv {
+	struct msg_queue_user msg_user;
+	char	config_name[CFG_NAME_MAX_LEN];
+};
+
+/* CC1101_MSG_SEND */
+struct __attribute__ ((packed)) msg_queue_user_send {
+	struct msg_queue_user msg_user;
+	int	preamble; /* 0 short, other long */
+	int	csma;		/* -1 = no csma,
+				 * 0 = send,
+				 * try csma times with delays defines from
+				 * currentdelay table.
+				 */
+	char	buf[CC1101_MAX_DATA_LEN]; /* first byte is len */
+};
+
+#define CC1101_READ_TYPE_RX	0 /* Rx packet */
+#define CC1101_READ_TYPE_ID	1 /* ID ack tx */
+#define CC1101_READ_TYPE_EVENT	2 /* Events */
+
+struct __attribute__ ((packed)) read_data_user {
+	int	type;	/* CC1101_READ_TYPE_RX */
+	int	id;
+	char	rssi;
+	char	len;
+	char	buf[CC1101_MAX_DATA_LEN];
+};
+
+/* Errorcodes from driver to Userspace */
+#define CC1101_ECA_SEND		-1
+#define CC1101_ECA_SPI_COM	-2
+#define CC1101_ECA_CFG_INVAL	-3
+#define CC1101_ECA_CFG		-4
+#define CC1101_ECA_DELAY	-5
+#define CC1101_ECA_PATABLE	-6
+
+struct __attribute__ ((packed)) read_data_user_ack {
+	int	type;	/* CC1101_READ_TYPE_ID */
+	int	id;
+	int	errorcode;
+	char	buf[CC1101_MAX_DATA_LEN - 2];
+};
+
+#define CC1101_READ_EVENT_ID_CARRIER	1
+
+#define CC1101_READ_EVENT_REASON_CYCLE		0
+#define CC1101_READ_EVENT_REASON_CALCULATED	1
+#define CC1101_READ_EVENT_REASON_FALLING	2
+#define CC1101_READ_EVENT_REASON_RISING		3
+
+struct __attribute__ ((packed)) read_data_user_event {
+	int	type;	/* CC1101_READ_TYPE_EVENT */
+	int	eventid;/* CC1101_READ_EVENT_ID_* */
+	char	value;	/* current event value */
+	char	reason;	/* CC1101_READ_EVENT_REASON_* */
+	char	buf[CC1101_MAX_DATA_LEN];
+};
+
+/* JOB struct */
+struct __attribute__ ((packed)) job_data_user {
+	int	id; /* unique ID got from userspace */
+};
+
+#define CC1101_MSG_USER_TYPE_SEND	1
+#define CC1101_MSG_USER_TYPE_SET_CFG	2
+#define CC1101_MSG_USER_TYPE_SET_DELAY	3
+#define CC1101_MSG_USER_TYPE_SET_PATABLE	4
+struct __attribute__ ((packed)) msg_user {
+	int	type;	/* CC1101_MSG_USER_TYPE_ */
+	int	preamble; /* 0 short, other long */
+	int	csma;		/* -1 = no csma,
+				 * 0 = send,
+				 * try csma times with delays defines from
+				 * currentdelay table.
+				 */
+	char	buf[CC1101_MAX_DATA_LEN]; /* first byte is len */
+};
+
+#endif
-- 
2.21.0





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux