[OpenWrt-Devel] [PATCH] RFC: OSMOCOM DECT stack on OpenWrt

Daniel Golle daniel at makrotopia.org
Thu Jun 26 12:27:28 EDT 2014


Many of todays routers got Sitel SC1442x or Dialog's SC14452 DECT
chips which makes them not entirely unlike hardware which is already
supported by Osmocom's DECT stack. I started exploring the idea to
make use of these devices in OpenWrt and thus ported the kernel-part
of the DECT stack to OpenWrt.
First of all, I'd be thankful for anyone to see if including this
patch breaks any existing OpenWrt builds on any kernel version.
(it really shouldn't)

Unfortunately, the
git://dect.osmocom.org/linux-2.6.git
tree is currently empty :(
However Patrick, indicated that he might push more recent code-base
soon, see
http://lists.osmocom.org/pipermail/linux-dect/2014-March/000241.html

I must admit that I did terrible things to struct dect_cctrl in order
to get it to be portable enough to compile, but as the alignment was
previously implicit I most likely didn't make things better not knowing
what I'm doing (but got everything to build).
Also, I'm pretty sure that there is still stuff missing, also because
I had to use a quite outdated refernce and starting point for the
kernel-patch:
http://dect.osmocom.org/attachment/wiki/Patches/linux-3.0.diff

As the TIPC kernel-internal interfaces were widely pruned away
(probably due to the lack of in-kernel users?), I didn't decide on
how to revive the DECT Cell Control Protocol support which previously
used these interfaces which are no longer present in recent kernels.

With the patch attached allows to build and package the existing
drivers from linux-3.0.diff to OpenWrt targets based on kernel v3.10
and above.
I'd be glad to know if the driver loads and probes correctly on
com-on-air-{pci,cs} hardware (there is some com-on-air-cs hardware
flying around here but I didn't yet get hold of a PCMCIA host to use
it with)


Have fun!

Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
 package/kernel/linux/modules/netdevices.mk         |  104 +
 target/linux/generic/files/drivers/dect/Kconfig    |   11 +
 target/linux/generic/files/drivers/dect/Makefile   |    2 +
 .../generic/files/drivers/dect/coa/.gitignore      |    4 +
 .../linux/generic/files/drivers/dect/coa/Kconfig   |   38 +
 .../linux/generic/files/drivers/dect/coa/Makefile  |   41 +
 .../linux/generic/files/drivers/dect/coa/bin2c.c   |   57 +
 .../generic/files/drivers/dect/coa/com_on_air.h    |   99 +
 .../generic/files/drivers/dect/coa/com_on_air_cs.c |  271 +
 .../files/drivers/dect/coa/com_on_air_pci.c        |  165 +
 .../generic/files/drivers/dect/coa/dip_opcodes.h   |  157 +
 .../generic/files/drivers/dect/coa/radio_lmx3161.c |   84 +
 .../generic/files/drivers/dect/coa/radio_u2785.c   |  261 +
 .../linux/generic/files/drivers/dect/coa/sc1442x.c | 1020 ++++
 .../files/drivers/dect/coa/sc1442x_firmware.asm    |  389 ++
 .../files/drivers/dect/coa/sc1442x_firmware.c      |   73 +
 .../files/drivers/dect/coa/sc1442x_firmware.h      |   66 +
 .../linux/generic/files/drivers/dect/vtrx/Kconfig  |    5 +
 .../linux/generic/files/drivers/dect/vtrx/Makefile |    2 +
 .../generic/files/drivers/dect/vtrx/mw_to_dbm.c    |  164 +
 .../generic/files/drivers/dect/vtrx/vtrx-sysfs.c   |  229 +
 .../linux/generic/files/drivers/dect/vtrx/vtrx.c   |  397 ++
 .../linux/generic/files/drivers/dect/vtrx/vtrx.h   |   42 +
 target/linux/generic/files/include/linux/dect.h    |  167 +
 .../generic/files/include/linux/dect_netlink.h     |  395 ++
 target/linux/generic/files/include/net/dect/ccp.h  |  110 +
 target/linux/generic/files/include/net/dect/dect.h |  319 ++
 target/linux/generic/files/include/net/dect/dlc.h  |  462 ++
 target/linux/generic/files/include/net/dect/dsc.h  |   12 +
 .../generic/files/include/net/dect/identities.h    |  194 +
 target/linux/generic/files/include/net/dect/mac.h  |  861 ++++
 .../linux/generic/files/include/net/dect/mac_ccf.h |  250 +
 .../linux/generic/files/include/net/dect/mac_csf.h |  596 +++
 .../generic/files/include/net/dect/transceiver.h   |  726 +++
 target/linux/generic/files/net/dect/Kconfig        |   66 +
 target/linux/generic/files/net/dect/Makefile       |   17 +
 target/linux/generic/files/net/dect/af_dect.c      |  456 ++
 target/linux/generic/files/net/dect/ccp.c          |  906 ++++
 target/linux/generic/files/net/dect/core.c         |  183 +
 target/linux/generic/files/net/dect/dect_netlink.c |  150 +
 target/linux/generic/files/net/dect/dlc.c          |  282 ++
 target/linux/generic/files/net/dect/dlc_b_sap.c    |  277 ++
 target/linux/generic/files/net/dect/dlc_cplane.c   |  981 ++++
 target/linux/generic/files/net/dect/dlc_lu1_sap.c  |  474 ++
 target/linux/generic/files/net/dect/dlc_s_sap.c    |  659 +++
 target/linux/generic/files/net/dect/dlc_uplane.c   |   86 +
 target/linux/generic/files/net/dect/dsc.c          |  141 +
 target/linux/generic/files/net/dect/identities.c   |  221 +
 target/linux/generic/files/net/dect/mac_ccf.c      | 2070 ++++++++
 target/linux/generic/files/net/dect/mac_csf.c      | 5151 ++++++++++++++++++++
 target/linux/generic/files/net/dect/raw.c          |  267 +
 target/linux/generic/files/net/dect/transceiver.c  | 1031 ++++
 .../generic/patches-3.10/780-dect-support.patch    |  155 +
 .../generic/patches-3.12/780-dect-support.patch    |  155 +
 .../generic/patches-3.13/780-dect-support.patch    |  155 +
 .../generic/patches-3.14/780-dect-support.patch    |  155 +
 56 files changed, 21811 insertions(+)
 create mode 100644 target/linux/generic/files/drivers/dect/Kconfig
 create mode 100644 target/linux/generic/files/drivers/dect/Makefile
 create mode 100644 target/linux/generic/files/drivers/dect/coa/.gitignore
 create mode 100644 target/linux/generic/files/drivers/dect/coa/Kconfig
 create mode 100644 target/linux/generic/files/drivers/dect/coa/Makefile
 create mode 100644 target/linux/generic/files/drivers/dect/coa/bin2c.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/com_on_air.h
 create mode 100644 target/linux/generic/files/drivers/dect/coa/com_on_air_cs.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/com_on_air_pci.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/dip_opcodes.h
 create mode 100644 target/linux/generic/files/drivers/dect/coa/radio_lmx3161.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/radio_u2785.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/sc1442x.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.asm
 create mode 100644 target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.c
 create mode 100644 target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.h
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/Kconfig
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/Makefile
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/mw_to_dbm.c
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/vtrx-sysfs.c
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/vtrx.c
 create mode 100644 target/linux/generic/files/drivers/dect/vtrx/vtrx.h
 create mode 100644 target/linux/generic/files/include/linux/dect.h
 create mode 100644 target/linux/generic/files/include/linux/dect_netlink.h
 create mode 100644 target/linux/generic/files/include/net/dect/ccp.h
 create mode 100644 target/linux/generic/files/include/net/dect/dect.h
 create mode 100644 target/linux/generic/files/include/net/dect/dlc.h
 create mode 100644 target/linux/generic/files/include/net/dect/dsc.h
 create mode 100644 target/linux/generic/files/include/net/dect/identities.h
 create mode 100644 target/linux/generic/files/include/net/dect/mac.h
 create mode 100644 target/linux/generic/files/include/net/dect/mac_ccf.h
 create mode 100644 target/linux/generic/files/include/net/dect/mac_csf.h
 create mode 100644 target/linux/generic/files/include/net/dect/transceiver.h
 create mode 100644 target/linux/generic/files/net/dect/Kconfig
 create mode 100644 target/linux/generic/files/net/dect/Makefile
 create mode 100644 target/linux/generic/files/net/dect/af_dect.c
 create mode 100644 target/linux/generic/files/net/dect/ccp.c
 create mode 100644 target/linux/generic/files/net/dect/core.c
 create mode 100644 target/linux/generic/files/net/dect/dect_netlink.c
 create mode 100644 target/linux/generic/files/net/dect/dlc.c
 create mode 100644 target/linux/generic/files/net/dect/dlc_b_sap.c
 create mode 100644 target/linux/generic/files/net/dect/dlc_cplane.c
 create mode 100644 target/linux/generic/files/net/dect/dlc_lu1_sap.c
 create mode 100644 target/linux/generic/files/net/dect/dlc_s_sap.c
 create mode 100644 target/linux/generic/files/net/dect/dlc_uplane.c
 create mode 100644 target/linux/generic/files/net/dect/dsc.c
 create mode 100644 target/linux/generic/files/net/dect/identities.c
 create mode 100644 target/linux/generic/files/net/dect/mac_ccf.c
 create mode 100644 target/linux/generic/files/net/dect/mac_csf.c
 create mode 100644 target/linux/generic/files/net/dect/raw.c
 create mode 100644 target/linux/generic/files/net/dect/transceiver.c
 create mode 100644 target/linux/generic/patches-3.10/780-dect-support.patch
 create mode 100644 target/linux/generic/patches-3.12/780-dect-support.patch
 create mode 100644 target/linux/generic/patches-3.13/780-dect-support.patch
 create mode 100644 target/linux/generic/patches-3.14/780-dect-support.patch

diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk
index 221eb2f..ec85b7e 100644
--- a/package/kernel/linux/modules/netdevices.mk
+++ b/package/kernel/linux/modules/netdevices.mk
@@ -772,3 +772,107 @@ define KernelPackage/vmxnet3/description
 endef
 
 $(eval $(call KernelPackage,vmxnet3))
+
+define KernelPackage/dect
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT subsystem
+  DEPENDS:=@!LINUX_3_3 @!LINUX_3_6 @!LINUX_3_8 @!LINUX_3_9
+  KCONFIG:= \
+	CONFIG_DECT \
+	CONFIG_DECTDEVICES=y \
+	CONFIG_DECT_DEBUG=n
+  FILES:=$(LINUX_DIR)/net/dect/dect.ko
+  AUTOLOAD:=$(call AutoProbe,dect)
+endef
+
+$(eval $(call KernelPackage,dect))
+
+
+define KernelPackage/dect-ccf
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT Cluster Control Functions (CCF) support
+  KCONFIG:=CONFIG_DECT_CCF
+  FILES:=$(LINUX_DIR)/net/dect/dect_ccf.ko
+  DEPENDS:=kmod-dect
+  AUTOLOAD:=$(call AutoProbe,dect-ccf)
+endef
+
+$(eval $(call KernelPackage,dect-ccf))
+
+define KernelPackage/dect-csf
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT Cell Site Functions
+  KCONFIG:=CONFIG_DECT_CSF
+  FILES:=$(LINUX_DIR)/net/dect/dect_csf.ko
+  DEPENDS:=kmod-dect
+  AUTOLOAD:=$(call AutoProbe,dect-csf)
+endef
+
+$(eval $(call KernelPackage,dect-csf))
+
+define KernelPackage/dect-dlc-lu1-sap
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT DLC LU1 SAP sockets
+  KCONFIG:=CONFIG_DECT_LU1_SAP
+  FILES:=$(LINUX_DIR)/net/dect/dlc_lu1_sap.ko
+  DEPENDS:=kmod-dect +kmod-dect-ccf
+  AUTOLOAD:=$(call AutoProbe,dlc-lu1-sap)
+endef
+
+$(eval $(call KernelPackage,dect-dlc-lu1-sap))
+
+define KernelPackage/dect-raw
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT RAW
+  KCONFIG:=CONFIG_DECT_RAW
+  FILES:=$(LINUX_DIR)/net/dect/dect_raw.ko
+  DEPENDS:=kmod-dect +kmod-dect-csf
+  AUTOLOAD:=$(call AutoProbe,dect-raw)
+endef
+
+$(eval $(call KernelPackage,dect-raw))
+
+define KernelPackage/dect-comonair
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT com-on-air common
+  KCONFIG:=CONFIG_DECT_COA \
+	CONFIG_DECT_COA_FIRMWARE=n
+  FILES:=$(LINUX_DIR)/drivers/dect/coa/com_on_air.ko
+  DEPENDS:=kmod-dect +kmod-dect-csf
+  AUTOLOAD:=$(call AutoProbe,com-on-air)
+endef
+
+$(eval $(call KernelPackage,dect-comonair))
+
+define KernelPackage/dect-comonair-pci
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Com-on-Air PCI DECT support
+  KCONFIG:=CONFIG_DECT_COA_PCI
+  FILES:=$(LINUX_DIR)/drivers/dect/coa/com_on_air_pci.ko
+  DEPENDS:=+kmod-dect-comonair @PCI_SUPPORT
+  AUTOLOAD:=$(call AutoProbe,com-on-air-pci)
+endef
+
+$(eval $(call KernelPackage,dect-comonair-pci))
+
+define KernelPackage/dect-comonair-cs
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Com-on-Air PCMCIA DECT support
+  KCONFIG:=CONFIG_DECT_COA_CS
+  FILES:=$(LINUX_DIR)/drivers/dect/coa/com_on_air_cs.ko
+  DEPENDS:=+kmod-dect-comonair @PCMCIA_SUPPORT
+  AUTOLOAD:=$(call AutoProbe,com-on-air-cs)
+endef
+
+$(eval $(call KernelPackage,dect-comonair-cs))
+
+define KernelPackage/dect-vtrx
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=DECT virtual transceiver (for testing)
+  KCONFIG:=CONFIG_DECT_VTRX
+  FILES:=$(LINUX_DIR)/drivers/dect/vtrx/dect-vtrx.ko
+  DEPENDS:=+kmod-dect-csf
+  AUTOLOAD:=$(call AutoProbe,dect-vtrx)
+endef
+
+$(eval $(call KernelPackage,dect-vtrx))
diff --git a/target/linux/generic/files/drivers/dect/Kconfig b/target/linux/generic/files/drivers/dect/Kconfig
new file mode 100644
index 0000000..baba4c1
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/Kconfig
@@ -0,0 +1,11 @@
+menuconfig DECTDEVICES
+	bool "DECT device support"
+	help
+	  Say Y here to show DECT device driver options.
+
+if DECTDEVICES
+
+source "drivers/dect/vtrx/Kconfig"
+source "drivers/dect/coa/Kconfig"
+
+endif
diff --git a/target/linux/generic/files/drivers/dect/Makefile b/target/linux/generic/files/drivers/dect/Makefile
new file mode 100644
index 0000000..c58af58
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DECT_VTRX)	+= vtrx/
+obj-$(CONFIG_DECT_COA)	+= coa/
diff --git a/target/linux/generic/files/drivers/dect/coa/.gitignore b/target/linux/generic/files/drivers/dect/coa/.gitignore
new file mode 100644
index 0000000..7890f99
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/.gitignore
@@ -0,0 +1,4 @@
+bin2c
+*.p
+*.h.tmp
+*.bin
diff --git a/target/linux/generic/files/drivers/dect/coa/Kconfig b/target/linux/generic/files/drivers/dect/coa/Kconfig
new file mode 100644
index 0000000..8773ad7
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/Kconfig
@@ -0,0 +1,38 @@
+config DECT_COA_PCI
+	tristate "Com-on-Air PCI DECT support"
+	depends on DECT
+	depends on PCI
+	select DECT_COA
+	select DECT_COA_U2785
+	help
+	  This option enables support for the Com-on-Air DECT PCI devices.
+
+config DECT_COA_CS
+	tristate "Com-on-Air PCMCIA DECT support"
+	depends on DECT
+	depends on PCMCIA
+	select DECT_COA
+	select DECT_COA_U2785
+	select DECT_COA_LMX3161
+	select CRC32
+	help
+	  This option enables support for the Com-on-Air DECT PCMCIA devices.
+
+config DECT_COA
+	tristate
+
+config DECT_COA_U2785
+	bool
+
+config DECT_COA_LMX3161
+	bool
+
+config DECT_COA_FIRMWARE
+	bool "Build Com-on-Air firmware (requires ASL macro assembler)"
+	depends on DECT_COA
+	help
+	  This option enables rebuild of the firmware for the Com-on-Air
+	  devices during the kernel build process. The ASL macro compiler
+	  is required for this.
+
+	  If unsure, say N.
diff --git a/target/linux/generic/files/drivers/dect/coa/Makefile b/target/linux/generic/files/drivers/dect/coa/Makefile
new file mode 100644
index 0000000..8ac23c3
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/Makefile
@@ -0,0 +1,41 @@
+com_on_air-objs				:= sc1442x_firmware.o sc1442x.o
+com_on_air-$(CONFIG_DECT_COA_U2785)	+= radio_u2785.o
+com_on_air-$(CONFIG_DECT_COA_LMX3161)	+= radio_lmx3161.o
+
+obj-$(CONFIG_DECT_COA)			+= com_on_air.o
+obj-$(CONFIG_DECT_COA_PCI)		+= com_on_air_pci.o
+obj-$(CONFIG_DECT_COA_CS)		+= com_on_air_cs.o
+
+$(obj)/sc1442x.o:			$(obj)/sc1442x_firmware.c
+$(obj)/sc1442x_firmware.c:		NAME=sc1442x
+clean-files				+= sc1442x_firmware.p
+clean-files				+= sc1442x_firmware.bin
+clean-files				+= sc1442x_firmware.h.tmp
+
+hostprogs-$(CONFIG_DECT_COA_FIRMWARE)	+= bin2c
+
+ifeq ($(CONFIG_DECT_COA_FIRMWARE),y)
+
+ASL		= asl
+P2BIN		= p2bin
+BIN2C		= $(obj)/bin2c
+
+quiet_cmd_asl	= ASL     $<
+      cmd_asl	= $(ASL) -q -c $< -o $(<:.asm=.p) -shareout $(<:.asm=.h.tmp); \
+		  $(P2BIN) $(<:.asm=.p) $(<:.asm=.bin) -r 0-509; \
+		  $(BIN2C) $(<:.asm=.bin) $(NAME)_firmware > $@; \
+		  ( \
+			echo "\#ifndef $$(echo $(NAME) | tr a-z A-Z)_FIRMWARE"; \
+			echo "\#define $$(echo $(NAME) | tr a-z A-Z)_FIRMWARE"; \
+			echo;\
+			echo "extern const unsigned char $(NAME)_firmware[510];"; \
+			echo;\
+			grep define $(<:.asm=.h.tmp); \
+			echo;\
+			echo "\#endif /* $$(echo $(NAME) | tr a-z A-Z)_FIRMWARE */"; \
+		  ) > $(@:.c=.h)
+
+$(obj)/%_firmware.c: $(src)/%_firmware.asm $(BIN2C)
+	$(call if_changed,asl)
+
+endif
diff --git a/target/linux/generic/files/drivers/dect/coa/bin2c.c b/target/linux/generic/files/drivers/dect/coa/bin2c.c
new file mode 100644
index 0000000..bab08fa
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/bin2c.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define HEADER_FMT \
+	"/*\n"						\
+	" * automatically generated file\n"		\
+	" * DO NOT EDIT\n"				\
+	" * edit firmware/filename.asm instead\n"	\
+	" */\n"						\
+	"\n"						\
+	"#include \"%s.h\"\n"				\
+	"\n"						\
+	"const unsigned char %s[] = {\n"
+
+#define FOOTER "};\n"
+
+int main(int argc, char *argv[])
+{
+	uint32_t wordcount = 0;
+	uint16_t w;
+	int f;
+
+	if (argc < 3) {
+		printf("usage: bin2c bin-file varname > c-file\n");
+		exit(1);
+	}
+
+	f = open(argv[1], O_RDONLY);
+	if (f < 0) {
+		printf("cant open(\"%s\"): %s\n", argv[1], strerror(errno));
+		exit(1);
+	}
+
+	printf(HEADER_FMT, argv[2], argv[2]);
+
+	while (2 == read(f, &w, 2)) {
+		if (!wordcount)
+			printf("\t");
+		else
+			if (!(wordcount % 4))
+				printf(",\n\t");
+			else
+				printf(", ");
+		printf("0x%.2x, 0x%.2x", (w & 0xff00) >> 8, w & 0xff);
+		wordcount++;
+	}
+	printf(FOOTER);
+	close(f);
+	return 0;
+}
diff --git a/target/linux/generic/files/drivers/dect/coa/com_on_air.h b/target/linux/generic/files/drivers/dect/coa/com_on_air.h
new file mode 100644
index 0000000..37c2b0a
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/com_on_air.h
@@ -0,0 +1,99 @@
+/*
+ * com_on_air - basic driver for the Dosch and Amand "com on air" cards
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * authors:
+ * (C) 2008  Andreas Schuler <krater at badterrorist dot com>
+ * (C) 2008  Matthias Wenzel <dect at mazzoo dot de>
+ * (C) 2009  Patrick McHardy <kaber at trash.net>
+ *
+ */
+
+#ifndef COM_ON_AIR_H
+#define COM_ON_AIR_H
+
+#include <linux/types.h>
+
+struct coa_freq_map_entry {
+	struct {
+		u8	divisor;
+		u8	swcnt;
+	} rx, tx;
+};
+
+struct coa_freq_map {
+	struct coa_freq_map_entry	carrier[DECT_CARRIER_NUM];
+};
+
+struct coa_device;
+struct coa_radio_ops {
+	void		(*rx_init)(const struct coa_device *dev, u16 offset);
+	void		(*tx_init)(const struct coa_device *dev, u16 offset);
+	void		(*set_carrier)(const struct coa_device *dev, u16 offset,
+				       enum dect_slot_states mode, u8 carrier);
+	u64		(*map_band)(struct coa_device *dev,
+				    const struct dect_band *band);
+	const char	*type;
+};
+
+extern const struct coa_radio_ops coa_u2785_radio_ops;
+extern const struct coa_radio_ops coa_lmx3161_radio_ops;
+
+/**
+ * struct sc1442x_phase_state - per-slot phase offset state
+ *
+ * @framenum:	frame number the information was last updated
+ * @tap:	sc1442x internal clock cycle which sampled the data
+ * @phase:	offset of number of symbol periods to nominal 11520 symbols per frame
+ *
+ * This structure is used to store the measured values for one particular
+ * frame. The actual phase offset is calculated from the differences of two
+ * consequitive frames.
+ */
+struct sc1442x_phase_state {
+	u8	framenum;
+	u8	tap;
+	s8	phase;
+};
+
+enum coa_device_types {
+	COA_TYPE_PCI,
+	COA_TYPE_PCMCIA,
+};
+
+struct coa_device {
+	const struct device		*dev;
+	unsigned int			irq;
+
+	enum coa_device_types		type;
+
+	const struct coa_radio_ops	*radio_ops;
+	struct coa_freq_map		freq_map;
+	struct sc1442x_phase_state	phase_state[DECT_FRAME_SIZE / 2];
+
+	spinlock_t			lock;
+	uint				config_base;
+	u8 __iomem			*sc1442x_base;
+	u16				cfg_reg;
+	u16				irq_reg;
+	u16				code_base;
+	u16				data_base;
+	u16				data_mask;
+
+	u8				ctrl;
+	u8				led;
+};
+
+extern irqreturn_t sc1442x_interrupt(int irq, void *dev_id);
+extern const struct dect_transceiver_ops sc1442x_transceiver_ops;
+
+extern int sc1442x_init_device(struct coa_device *dev);
+extern void sc1442x_shutdown_device(struct coa_device *dev);
+
+extern void sc1442x_rfdesc_write(const struct coa_device *dev, u16 offset,
+				 const u8 *src, u16 length);
+
+#endif
diff --git a/target/linux/generic/files/drivers/dect/coa/com_on_air_cs.c b/target/linux/generic/files/drivers/dect/coa/com_on_air_cs.c
new file mode 100644
index 0000000..e1f2231
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/com_on_air_cs.c
@@ -0,0 +1,271 @@
+/*
+ * com_on_air_cs - basic driver for the Dosch and Amand "com on air" cards
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * authors:
+ * (C) 2008  Andreas Schuler <krater at badterrorist dot com>
+ * (C) 2008  Matthias Wenzel <dect at mazzoo dot de>
+ * (C) 2009  Patrick McHardy <kaber at trash.net>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <net/dect/transceiver.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "com_on_air.h"
+
+MODULE_AUTHOR("Matthias Wenzel comonair<a>mazzoo.de;"
+              "Andreas Schuler dect<a>badterrorist.com");
+MODULE_DESCRIPTION("Dosch&Amand COM-ON-AIR PCMCIA driver");
+MODULE_LICENSE("GPL");
+
+static int get_card_id(const struct pcmcia_device *link);
+
+static int com_on_air_probe(struct pcmcia_device *link)
+{
+	struct dect_transceiver *trx;
+	struct coa_device *dev;
+	int err;
+
+	trx = dect_transceiver_alloc(&sc1442x_transceiver_ops, sizeof(*dev));
+	if (!trx) {
+		err = -ENOMEM;
+		goto err1;
+	}
+
+	link->priv = trx;
+	dev = dect_transceiver_priv(trx);
+	dev->type      = COA_TYPE_PCMCIA;
+	dev->code_base = 0x0;
+	dev->data_base = 0x0;
+	dev->data_mask = 0x0ff;
+	dev->cfg_reg   = 0x1ff;
+	dev->irq_reg   = 0x0;
+	dev->dev       = &link->dev;
+
+	dev_info(dev->dev, "%s %s %s %s\n", link->prod_id[0], link->prod_id[1],
+		 link->prod_id[2] ? : "", link->prod_id[3] ? : "");
+
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+	link->resource[0]->end	  = 16;
+	link->resource[1]->flags |= 0;
+
+	link->config_flags	  = CONF_ENABLE_IRQ;
+	link->config_index	  = 1;
+	link->config_regs	  = PRESENT_OPTION;
+	link->config_base	  = 0x1020;
+
+	link->resource[2]->flags  = WIN_DATA_WIDTH_16 | WIN_ENABLE;
+	link->resource[2]->start  = 0;
+	link->resource[2]->end	  = 0x1000;
+
+	err = pcmcia_request_window(link, link->resource[2], 500);
+	if (err < 0) {
+		dev_err(dev->dev, "failed to obtain PCMCIA window\n");
+		goto err2;
+	}
+
+	dev->sc1442x_base = ioremap_nocache(link->resource[2]->start,
+					    resource_size(link->resource[2]));
+	if (!dev->sc1442x_base) {
+		dev_err(dev->dev, "failed to remap PCMCIA resource\n");
+		err = -EIO;
+		goto err3;
+	}
+
+	link->socket->functions = 0;
+
+	err = pcmcia_request_irq(link, sc1442x_interrupt);
+	if (err < 0) {
+		dev_err(dev->dev, "failed to request IRQ%d\n", link->irq);
+		goto err4;
+	}
+
+	err = pcmcia_enable_device(link);
+	if (err < 0) {
+		dev_err(dev->dev, "failed to enable PCMCIA device\n");
+		goto err5;
+	}
+
+	dev_dbg(dev->dev, "%svalid client.\n", (link->config_flags) ? "":"in");
+	dev_dbg(dev->dev, "Type          0x%x\n", link->socket->state);
+	dev_dbg(dev->dev, "Function      0x%x\n", link->func);
+	dev_dbg(dev->dev, "config_flags  %d\n", link->config_flags);
+	dev_dbg(dev->dev, "config_base   0x%x\n", link->config_base);
+	dev_dbg(dev->dev, "config_regs   %d\n", link->config_regs);
+	dev_dbg(dev->dev, "IRQ           0x%x\n", link->irq);
+	dev_dbg(dev->dev, "BasePort1     0x%llx\n", link->resource[0]->start);
+	dev_dbg(dev->dev, "NumPorts1     0x%llx\n", link->resource[0]->end);
+	dev_dbg(dev->dev, "Attributes1   0x%lx\n", link->resource[0]->flags);
+	dev_dbg(dev->dev, "BasePort2     0x%llx\n", link->resource[1]->start);
+	dev_dbg(dev->dev, "NumPorts2     0x%llx\n", link->resource[1]->end);
+	dev_dbg(dev->dev, "Attributes2   0x%lx\n", link->resource[1]->flags);
+	dev_dbg(dev->dev, "IOAddrLines   0x%x\n", link->io_lines);
+	dev_dbg(dev->dev, "has%s function_config\n",
+		link->function_config ? "":" no");
+
+	switch (get_card_id(link)) {
+	case 0:
+	case 3:
+		dev->radio_ops = &coa_u2785_radio_ops;
+		break;
+	case 1:
+	case 2:
+		dev->radio_ops = &coa_lmx3161_radio_ops;
+		break;
+	default:
+		dev_err(dev->dev, "unknown radio type\n");
+		err = -EINVAL;
+		goto err5;
+	}
+
+	dev_info(dev->dev, "Radio type %s\n", dev->radio_ops->type);
+
+	dev->irq	 = link->irq;
+	dev->config_base = link->config_base;
+	err = sc1442x_init_device(dev);
+	if (err < 0)
+		goto err5;
+
+	err = dect_register_transceiver(trx);
+	if (err < 0)
+		goto err6;
+
+	return 0;
+
+err6:
+	sc1442x_shutdown_device(dev);
+err5:
+	pcmcia_disable_device(link);
+err4:
+	iounmap(dev->sc1442x_base);
+err3:
+	pcmcia_release_window(link, link->resource[2]);
+err2:
+	dect_transceiver_free(trx);
+err1:
+	return err;
+}
+
+static void com_on_air_remove(struct pcmcia_device *link)
+{
+	struct dect_transceiver *trx = link->priv;
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	u8 __iomem *sc1442x_base = dev->sc1442x_base;
+
+	sc1442x_shutdown_device(dev);
+	pcmcia_disable_device(link);
+	dect_unregister_transceiver(trx);
+	iounmap(sc1442x_base);
+}
+
+static int com_on_air_suspend(struct pcmcia_device *link)
+{
+	struct dect_transceiver *trx = link->priv;
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	sc1442x_shutdown_device(dev);
+	return 0;
+}
+
+static int com_on_air_resume(struct pcmcia_device *link)
+{
+	struct dect_transceiver *trx = link->priv;
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	return sc1442x_init_device(dev);
+}
+
+static struct pcmcia_device_id com_on_air_ids[] = {
+	/*
+	 * The crc32 hashes below are generated by the tool in
+	 * Documentation/pcmcia/devicetable.txt
+	 */
+	PCMCIA_DEVICE_PROD_ID12  ("DECTDataDevice", "PCMCIA F22",
+			           0x11fe69e9,       0x253670b2),
+	PCMCIA_DEVICE_PROD_ID12  ("DECTDataDevice", "PCMCIA",
+			           0x11fe69e9,       0x281f1c5d),
+	PCMCIA_DEVICE_PROD_ID1234("DOSCH-AMAND",    "MMAP PCMCIA",
+			          "MXM500",         "V1.00",
+				   0x4bc552e7,       0x0df519bb,
+				   0x09e43c7c,       0x3488c81a),
+	PCMCIA_DEVICE_PROD_ID12  ("DECTVoIPDevice", "PCMCIA DA099",
+				   0xeabb0be4,       0xd7b915fe),
+#if 0
+	There are more devices out there, I only own the above three.
+	an excerpt from win32 dna.inf:
+
+%String1%=pcmcia.install,PCMCIA\DOSCH-AMAND-MMAP_PCMCIA-C7D7
+%String1%=pcmcia.install,PCMCIA\Dosch-Amand-DECT_MultiMedia-BD0D
+%String1%=pcmcia.install,PCMCIA\DOSCH_&_AMAND-DECT_MULTIMEDIA-1A9F
+%String1%=pcmcia.install,PCMCIA\DECTDataDevice-F13-6433
+%String1%=pcmcia.install,PCMCIA\DECTDataDevice-PCMCIA-0EF8
+%String4%=pci.install,PCI\VEN_11E3&DEV_0001&SUBSYS_000111E3&REV_00
+%String4%=pci.install,PCI\VEN_11E3&DEV_0001&SUBSYS_00011786&REV_32
+%String4%=pci.install,PCI\VEN_1786&DEV_0001&SUBSYS_000111E3&REV_00
+%String5%=freekey2.install,PCMCIA\DECTDataDevice-PCMCIA-FEF2
+%String6%=freekey2.install,PCMCIA\DECTDataDevice-PCMCIA_F22-4BD3
+%String6%=freekey2.install,PCMCIA\DECTDataDevice-PCMCIA_F22-BBD9
+
+#endif
+	PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, com_on_air_ids);
+
+/* returns an index into com_on_air_ids[] */
+static int get_card_id(const struct pcmcia_device *link)
+{
+	u32 hash[4] = {};
+	unsigned int i;
+
+	for (i = 0; i < 4; i++) {
+		if (link->prod_id[i] == NULL)
+			continue;
+		hash[i] = crc32(0, link->prod_id[i], strlen(link->prod_id[i]));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(com_on_air_ids) - 1; i++) {
+		if ((hash[0] == com_on_air_ids[i].prod_id_hash[0]) &&
+		    (hash[1] == com_on_air_ids[i].prod_id_hash[1]) &&
+		    (hash[2] == com_on_air_ids[i].prod_id_hash[2]) &&
+		    (hash[3] == com_on_air_ids[i].prod_id_hash[3]))
+			return i;
+	}
+	return -1;
+}
+
+static struct pcmcia_driver coa_driver = {
+	.owner		= THIS_MODULE,
+	.name		= KBUILD_MODNAME,
+	.probe		= com_on_air_probe,
+	.remove		= com_on_air_remove,
+	.suspend	= com_on_air_suspend,
+	.resume		= com_on_air_resume,
+	.id_table	= com_on_air_ids,
+};
+
+static int __init init_com_on_air_cs(void)
+{
+	return pcmcia_register_driver(&coa_driver);
+}
+
+static void __exit exit_com_on_air_cs(void)
+{
+	pcmcia_unregister_driver(&coa_driver);
+}
+
+module_init(init_com_on_air_cs);
+module_exit(exit_com_on_air_cs);
diff --git a/target/linux/generic/files/drivers/dect/coa/com_on_air_pci.c b/target/linux/generic/files/drivers/dect/coa/com_on_air_pci.c
new file mode 100644
index 0000000..a266f75
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/com_on_air_pci.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <net/dect/transceiver.h>
+
+#include "com_on_air.h"
+
+#define PCI_VENDOR_ID_QUICKLOGIC	0x11e3
+#define PCI_DEVICE_ID_COA		0x0001
+
+static int coa_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *ent)
+{
+	struct dect_transceiver *trx;
+	struct coa_device *dev;
+	void __iomem *base;
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to enable PCI device\n");
+		goto err1;
+	}
+	pci_set_master(pdev);
+
+	err = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to obtain PCI resources\n");
+		goto err2;
+	}
+
+	base = ioremap_nocache(pci_resource_start(pdev, 0),
+			       pci_resource_len(pdev, 0));
+	if (base == NULL) {
+		dev_err(&pdev->dev, "failed to remap PCI resource\n");
+		err = -EIO;
+		goto err3;
+	}
+
+	trx = dect_transceiver_alloc(&sc1442x_transceiver_ops, sizeof(*dev));
+	if (trx == NULL) {
+		err = -ENOMEM;
+		goto err4;
+	}
+	pci_set_drvdata(pdev, trx);
+
+	dev = dect_transceiver_priv(trx);
+	dev->type	  = COA_TYPE_PCI;
+	dev->dev	  = &pdev->dev;
+	dev->sc1442x_base = base;
+	dev->radio_ops    = &coa_u2785_radio_ops;
+	dev->data_base	  = 0x0a00;
+	dev->data_mask	  = 0x7ff;
+	dev->cfg_reg	  = 0x1fe2;
+	dev->code_base	  = 0x1a00;
+
+	err = sc1442x_init_device(dev);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to initialize chip\n");
+		goto err5;
+	}
+
+	err = request_irq(pdev->irq, sc1442x_interrupt, IRQF_SHARED,
+			  KBUILD_MODNAME, trx);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to request IRQ%d\n", pdev->irq);
+		goto err6;
+	}
+
+	dev->irq = pdev->irq;
+	err = dect_register_transceiver(trx);
+	if (err < 0)
+		goto err7;
+
+	return 0;
+
+err7:
+	free_irq(pdev->irq, trx);
+err6:
+	sc1442x_shutdown_device(dev);
+err5:
+	dect_transceiver_free(trx);
+err4:
+	iounmap(base);
+err3:
+	pci_release_regions(pdev);
+err2:
+	pci_disable_device(pdev);
+err1:
+	return err;
+}
+
+static void coa_remove(struct pci_dev *pdev)
+{
+	struct dect_transceiver *trx = pci_get_drvdata(pdev);
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	u8 __iomem *sc1442x_base = dev->sc1442x_base;
+
+	sc1442x_shutdown_device(dev);
+	free_irq(pdev->irq, trx);
+	dect_unregister_transceiver(trx);
+	iounmap(sc1442x_base);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static int coa_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct dect_transceiver *trx = pci_get_drvdata(pdev);
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	sc1442x_shutdown_device(dev);
+	pci_save_state(pdev);
+	return 0;
+}
+
+static int coa_resume(struct pci_dev *pdev)
+{
+	struct dect_transceiver *trx = pci_get_drvdata(pdev);
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	pci_restore_state(pdev);
+	return sc1442x_init_device(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(coa_pci_tbl) = {
+	{PCI_DEVICE(PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_COA)},
+	{}
+};
+
+static struct pci_driver coa_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= coa_pci_tbl,
+	.probe		= coa_probe,
+	.remove		= coa_remove,
+	.suspend	= coa_suspend,
+	.resume		= coa_resume,
+};
+
+static int __init coa_pci_init(void)
+{
+	return pci_register_driver(&coa_driver);
+}
+
+static void __exit coa_pci_exit(void)
+{
+	pci_unregister_driver(&coa_driver);
+}
+
+module_init(coa_pci_init);
+module_exit(coa_pci_exit);
+
+MODULE_DESCRIPTION("Dosch&Amand COM-ON-AIR PCI driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, coa_pci_tbl);
diff --git a/target/linux/generic/files/drivers/dect/coa/dip_opcodes.h b/target/linux/generic/files/drivers/dect/coa/dip_opcodes.h
new file mode 100644
index 0000000..bd50056
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/dip_opcodes.h
@@ -0,0 +1,157 @@
+/*
+ * com_on_air - basic driver for the Dosch and Amand "com on air" cards
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * authors:
+ * (C) 2008  Andreas Schuler <krater at badterrorist dot com>
+ * (C) 2008  Matthias Wenzel <dect at mazzoo dot de>
+ *
+ */
+
+#ifndef DIP_OPCODE_H
+#define DIP_OPCODE_H
+
+#define	BR		0x01
+#define	JMP		0x02
+#define	JMP1		0x03
+#define	RTN		0x04
+#define	BK_A1		0x05
+#define	WNTM1		0x06
+#define	WNTP1		0x07
+#define	WNT		0x08
+#define	WT		0x09
+#define	RFDIS		0x0a
+#define	RFEN		0x0b
+#define	LD_PTR		0x0c
+#define	SLOTZERO	0x0d
+#define	BK_A		0x0e
+#define	BK_C		0x0f
+
+
+#define	B_RST		0x20
+#define	B_ST2		0x21
+#define	B_XT		0x24
+#define	B_BT2		0x25
+#define	B_BTFU		0x25
+#define	B_XOFF		0x26
+#define	B_ON		0x27
+#define	B_XON		0x27
+#define	UNLCK		0x28
+#define	B_SR		0x29
+#define	B_XR		0x2b
+#define	EN_SL_ADJ	0x2c
+#define	B_BR2		0x2d
+#define	B_BRFU		0x2d
+#define	B_RINV		0x2e
+#define	B_RON		0x2f
+
+
+#define	B_ST		0x31
+#define	B_TX		0x31
+#define	B_AT		0x32
+#define	B_RC		0x33
+#define	B_BT		0x34
+#define	B_BTFP		0x35
+#define	B_BTP		0x35
+#define	B_AT2		0x37
+#define	B_WRS		0x39
+#define	B_AR		0x3a
+#define	B_BR		0x3c
+#define	B_BRP		0x3d
+#define	B_BRFP		0x3d
+#define	B_AR2		0x3f
+
+
+#define	D_RST		0x40
+#define	D_ON		0x42
+#define	D_OFF		0x43
+#define	D_PREP		0x44
+#define	WSC		0x48
+
+
+#define	D_LDK		0x50
+#define	D_LDS		0x57
+#define	D_WRS		0x5f
+
+
+#define	U_PSC		0x60
+#define	U_INT0		0x61
+#define	RCK_INT		0x62
+#define	RCK_EXT		0x63
+#define	B_WB_OFF	0x64
+#define	B_WB_ON		0x65
+#define	CLK1		0x66
+#define	CLK3		0x67
+#define	U_CK8		0x68
+#define	U_CK4		0x69
+#define	U_CK2		0x6a
+#define	U_INT1		0x6b
+#define	U_CK1		0x6c
+#define	U_INT2		0x6d
+#define	U_INT3		0x6f
+
+
+#define	A_RCV0		0x80
+#define	A_RCV36		0x82
+#define	A_RCV30		0x83
+#define	A_RCV24		0x84
+#define	A_RCV18		0x85
+#define	A_RCV12		0x86
+#define	A_RCV6		0x87
+#define	A_RCV33		0x8a
+#define	A_RCV27		0x8b
+#define	A_RCV21		0x8c
+#define	A_RCV15		0x8d
+#define	A_RCV9		0x8e
+#define	A_RCV3		0x8f
+
+
+#define	MEN3N		0xa2
+#define	MEN3		0xa3
+#define	MEN1N		0xa4
+#define	MEN1		0xa5
+#define	MEN2N		0xa6
+#define	MEN2		0xa7
+#define	M_RD		0xa8
+#define	M_RST		0xa9
+
+
+#define	M_WRS		0xb8
+#define	M_WR		0xb9
+
+
+#define	A_RST		0xc0
+#define	A_MUTE		0xc1
+#define	A_STOFF		0xc2
+#define	A_ALAW		0xc3
+#define	A_DT		0xc4
+#define	A_NORM		0xc5
+#define	A_LDR		0xc6
+#define	A_LDW		0xc7
+#define	A_LIN		0xc8
+#define	A_MTOFF		0xc9
+#define	A_MUTE1		0xca
+#define	A_MTOFF1	0xcb
+#define	A_STON		0xcc
+#define	A_DT1		0xcd
+#define	A_LDR1		0xce
+#define	A_LDW1		0xcf
+
+
+#define	A_STRN		0xe0
+#define	P_LD		0xe8
+#define	P_EN		0xe9
+#define	P_SC		0xea
+#define	A_RST1		0xeb
+#define	P_LDL		0xec
+#define	P_LDH		0xed
+#define	C_ON		0xee
+#define	C_OFF		0xef
+
+
+#define	C_LD		0xfa
+
+#endif
diff --git a/target/linux/generic/files/drivers/dect/coa/radio_lmx3161.c b/target/linux/generic/files/drivers/dect/coa/radio_lmx3161.c
new file mode 100644
index 0000000..9f6d5ae
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/radio_lmx3161.c
@@ -0,0 +1,84 @@
+/*
+ * radio_lmx3161 - NSC LMX3161 Single Chip Radio Transceiver radio operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/transceiver.h>
+
+#include "com_on_air.h"
+
+/* Intermediate frequency */
+#define RADIO_LMX3161_FREQ_IF	110592	/* kHz */
+
+/*
+ * Control Bits
+ */
+
+/* N-counter */
+#define RADIO_LMX3161_CTRL_N	0x0
+/* R-counter */
+#define RADIO_LMX3161_CTRL_R	0x2
+/* F-latch */
+#define RADIO_LMX3161_CTRL_F	0x1
+
+/*
+ * Function Register (18 bit F-latch)
+ */
+
+/* Prescaler modules select */
+#define RADIO_LMX3161_PRESCALER_32_33
+#define RADIO_LMX3161_PRESCALER_64_65
+/* Phase detector polarity: 0 = negative, 1 = positive */
+#define RADIO_LMX3161_PD		(1 << 3)
+/* Charge pump current gain select: 0 = LOW (1*I_cpo), 1 = high (4*I_cpo) */
+#define RADIO_LMX3161_CP		(1 << 4)
+/* tri-state charge pump output: 0 = normal, 1 = tri-state */
+#define RADIO_LMX3161_CP_TRISTATE	(1 << 5)
+/* Receive chain power down control: 0 = power up, 1 = power down */
+#define RADIO_LMX3161_RX_POWER		(1 << 7)
+/* Transmit chain power down control: 0 = power up, 1 = power down */
+#define RADIO_LMX3161_TX_POWER		(1 << 8)
+/* Out 0 CMOS output: 0 = low, 1 = high */
+#define RADIO_LMX3161_CMOS0A		(1 << 9)
+/* Out 1 CMOS output: 0 = low, 1 = high */
+#define RADIO_LMX3161_CMOS1		(1 << 10)
+/* Out 2 CMOS output: 0 = low, 1 = high */
+#define RADIO_LMX3161_CMOS2		(1 << 11)
+/* Power down mode select: */
+#define RADIO_LMX3161_POWER_DOWN_MASK		(0x3 << 12)
+#define RADIO_LMX3161_POWER_DOWN_SW		0
+#define RADIO_LMX3161_POWER_DOWN_HARDWIRE	(0x3 << 12)
+/* Demodulator gain select */
+/* Demodulator DC level shifting polarity */
+/* Demodulator DC level shift */
+
+static u64 lmx3161_map_band(struct coa_device *dev, const struct dect_band *band)
+{
+	struct coa_freq_map_entry *fe;
+	u32 frequency;
+	u8 carrier;
+
+	for (carrier = 0; carrier < band->carriers; carrier++) {
+		frequency = band->frequency[carrier];
+		fe = &dev->freq_map.carrier[carrier];
+	}
+	return 0;
+}
+
+const struct coa_radio_ops coa_lmx3161_radio_ops = {
+	.type		= "LMX3161",
+	.rx_init	= NULL,
+	.tx_init	= NULL,
+	.set_carrier	= NULL,
+	.map_band	= lmx3161_map_band,
+};
+EXPORT_SYMBOL_GPL(coa_lmx3161_radio_ops);
diff --git a/target/linux/generic/files/drivers/dect/coa/radio_u2785.c b/target/linux/generic/files/drivers/dect/coa/radio_u2785.c
new file mode 100644
index 0000000..dab53a0
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/radio_u2785.c
@@ -0,0 +1,261 @@
+/*
+ * radio_u2785 - ATMEL U2785 RF IC radio operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+//#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/transceiver.h>
+
+#include "com_on_air.h"
+
+#define u2785_debug(dev, fmt, args...) \
+	dev_dbg(dev->dev, "u2785: " fmt, ## args)
+
+/* Intermediate frequencies */
+#define RADIO_U2785_FREQ_IF1	110592	/* kHz */
+#define RADIO_U2785_FREQ_IF2	112320	/* kHz */
+
+/*
+ *  RC (Reference Divider)
+ */
+#define RADIO_U2785_RC_SHIFT	22
+#define RADIO_U2785_RC_12	(0x1 << RADIO_U2785_RC_SHIFT)
+#define RADIO_U2785_RC_16	(0x2 << RADIO_U2785_RC_SHIFT)
+#define RADIO_U2785_RC_24	(0x3 << RADIO_U2785_RC_SHIFT)
+
+/*
+ * SC (Swallow Counter) 0-31
+ */
+#define RADIO_U2785_SC_SHIFT	17
+#define RADIO_U2785_SC_MAX	31
+#define RADIO_U2785_SC_MASK	(0x1F << RADIO_U2785_SC_SHIFT)
+
+/*
+ *  MC (Main Divider)
+ */
+#define RADIO_U2785_MC_SHIFT	15
+#define RADIO_U2785_MC_MIN	31
+#define RADIO_U2785_MC_MAX	34
+#define RADIO_U2785_MC_31	(0x0 << RADIO_U2785_MC_SHIFT)
+#define RADIO_U2785_MC_32	(0x1 << RADIO_U2785_MC_SHIFT)
+#define RADIO_U2785_MC_33	(0x2 << RADIO_U2785_MC_SHIFT)
+#define RADIO_U2785_MC_34	(0x3 << RADIO_U2785_MC_SHIFT)
+
+/*
+ *  PS (Phase Settings)
+ */
+
+/* Phase of GF_DATA */
+#define RADIO_U2785_PS_GF	(0x1 << 14)
+/* Phase of MCC Internal Connection */
+#define RADIO_U2785_PS_MCC	(0x1 << 13)
+/* Phase of Charge Pump */
+#define RADIO_U2785_PS_CP	(0x1 << 12)
+
+/*
+ * Current-Saving Power-up/down Settings
+ */
+
+/* Gaussian Filter */
+#define RADIO_U2785_GF		(0x1 << 11)
+/* Modulation Compensation Circuit */
+#define RADIO_U2785_MCC		(0x1 << 10)
+/* Frequency Doubler */
+#define RADIO_U2785_FD		(0x1 << 8)
+/* OP1 + OP2 (Op Amps) */
+#define RADIO_U2785_OP		(0x1 << 7)
+
+/*
+ *  Current Gain Settings (in percent)
+ */
+#define RADIO_U2785_CGS_60	0x0
+#define RADIO_U2785_CGS_70	0x1
+#define RADIO_U2785_CGS_80	0x2
+#define RADIO_U2785_CGS_90	0x3
+#define RADIO_U2785_CGS_100	0x4
+#define RADIO_U2785_CGS_110	0x5
+#define RADIO_U2785_CGS_120	0x6
+#define RADIO_U2785_CGS_130	0x7
+
+/* GFCS (Gaussian-Filter Current Settings) */
+#define RADIO_U2785_GFCS_SHIFT	7
+/* CPCS (Charge-Pump Current Settings) */
+#define RADIO_U2785_CPCS_SHIFT	1
+/* MCCS (Modulation-Compensation Current Settings) */
+#define RADIO_U2785_MCCS_SHIFT	4
+
+/*
+ * Pretune DAC Voltage
+ */
+#define RADIO_U2785_DAC_SHIFT	4
+#define RADIO_U2785_DAC_300mV	(0x0 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_600mV	(0x1 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_900mV	(0x2 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_1200mV	(0x3 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_1400mV	(0x4 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_1700mV	(0x5 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_2000mV	(0x6 << RADIO_U2785_DAC_SHIFT)
+#define RADIO_U2785_DAC_2300mV	(0x7 << RADIO_U2785_DAC_SHIFT)
+
+/*
+ * Address bit
+ */
+#define RADIO_U2785_ADDRESS_BIT	0x1
+
+static void u2785_write_config(const struct coa_device *dev, u16 offset,
+			       u32 init1, u32 init2)
+{
+	u8 init[5] = {
+		/* first word: 24 bits */
+		[0]	= init1 >> 16,
+		[1]	= init1 >> 8,
+		[2]	= init1 | RADIO_U2785_ADDRESS_BIT,
+		/* second word: 9 bits */
+		[3]	= init2 >> 1,
+		[4]	= 0,
+	};
+
+	sc1442x_rfdesc_write(dev, offset, init, sizeof(init));
+}
+
+static void u2785_rx_init(const struct coa_device *dev, u16 offset)
+{
+	u32 init1 = 0, init2 = 0;
+
+	init1 |= RADIO_U2785_RC_12;
+	init1 |= RADIO_U2785_MC_32;
+	init1 |= 10 << RADIO_U2785_SC_SHIFT;
+
+	init1 |= RADIO_U2785_CGS_100 << RADIO_U2785_CPCS_SHIFT;
+
+	init2 |= RADIO_U2785_FD;
+	init2 |= RADIO_U2785_CGS_100 << RADIO_U2785_MCCS_SHIFT;
+
+	u2785_write_config(dev, offset, init1, init2);
+}
+
+static void u2785_tx_init(const struct coa_device *dev, u16 offset)
+{
+	u32 init1 = 0, init2 = 0;
+
+	init1 |= RADIO_U2785_RC_12;
+	init1 |= RADIO_U2785_MC_34;
+	init1 |= 7 << RADIO_U2785_SC_SHIFT;
+
+	init1 |= RADIO_U2785_GF;
+	init1 |= RADIO_U2785_MCC;
+	init1 |= RADIO_U2785_CGS_120 << RADIO_U2785_GFCS_SHIFT;
+	init1 |= RADIO_U2785_DAC_1400mV;
+	init1 |= RADIO_U2785_CGS_60 << RADIO_U2785_CPCS_SHIFT;
+
+	init2 |= RADIO_U2785_FD;
+	init2 |= RADIO_U2785_CGS_130 << RADIO_U2785_MCCS_SHIFT;
+
+	u2785_write_config(dev, offset, init1, init2);
+}
+
+static void u2785_write_carrier(const struct coa_device *dev, u16 offset,
+			        u32 init1)
+{
+	u8 init[3] = {
+		/* first word: 24 bits */
+		[0]	= init1 >> 16,
+		[1]	= init1 >> 8,
+		[2]	= init1 | RADIO_U2785_ADDRESS_BIT,
+	};
+
+	sc1442x_rfdesc_write(dev, offset, init, sizeof(init));
+}
+
+static void u2785_set_carrier(const struct coa_device *dev, u16 offset,
+			      enum dect_slot_states mode, u8 carrier)
+{
+	const struct coa_freq_map_entry *fe = &dev->freq_map.carrier[carrier];
+	u32 init1 = 0;
+
+	init1 |= RADIO_U2785_RC_12;
+
+	switch (mode) {
+	case DECT_SLOT_SCANNING:
+	case DECT_SLOT_RX:
+		init1 |= (fe->rx.divisor - RADIO_U2785_MC_MIN) <<
+			 RADIO_U2785_MC_SHIFT;
+		init1 |= fe->rx.swcnt << RADIO_U2785_SC_SHIFT;
+
+		init1 |= RADIO_U2785_CGS_100 << RADIO_U2785_CPCS_SHIFT;
+		break;
+	case DECT_SLOT_TX:
+		init1 |= (fe->tx.divisor - RADIO_U2785_MC_MIN) <<
+			 RADIO_U2785_MC_SHIFT;
+		init1 |= fe->tx.swcnt << RADIO_U2785_SC_SHIFT;
+
+		init1 |= RADIO_U2785_GF;
+		init1 |= RADIO_U2785_MCC;
+		init1 |= RADIO_U2785_CGS_120 << RADIO_U2785_GFCS_SHIFT;
+		init1 |= RADIO_U2785_DAC_1400mV;
+		init1 |= RADIO_U2785_CGS_60 << RADIO_U2785_CPCS_SHIFT;
+		break;
+	default:
+		return;
+	}
+
+	u2785_write_carrier(dev, offset, init1);
+}
+
+static int u2785_map_freq(u32 frequency, u8 *s_mc, u8 *s_sc)
+{
+	frequency /= DECT_CARRIER_WIDTH;
+
+	*s_mc = frequency / 32;
+	if (*s_mc < RADIO_U2785_MC_MIN || *s_mc > RADIO_U2785_MC_MAX)
+		return false;
+	*s_sc = frequency % 32;
+	return true;
+}
+
+static u64 u2785_map_band(struct coa_device *dev, const struct dect_band *band)
+{
+	struct coa_freq_map_entry *fe;
+	u64 carriers = 0;
+	u32 frequency;
+	u8 carrier;
+
+	for (carrier = 0; carrier < band->carriers; carrier++) {
+		frequency = band->frequency[carrier];
+		fe = &dev->freq_map.carrier[carrier];
+
+		if (!u2785_map_freq(frequency - RADIO_U2785_FREQ_IF1,
+				    &fe->rx.divisor, &fe->rx.swcnt))
+			continue;
+		if (!u2785_map_freq(frequency,
+				    &fe->tx.divisor, &fe->tx.swcnt))
+			continue;
+
+		carriers |= 1 << carrier;
+		u2785_debug(dev, "carrier %u (%u.%03uMHz) => "
+			    "rx: div: %u sw: %u tx: div: %u sw: %u\n",
+			    carrier, frequency / 1000, frequency % 1000,
+			    fe->rx.divisor, fe->rx.swcnt,
+			    fe->tx.divisor, fe->tx.swcnt);
+	}
+
+	return carriers;
+}
+
+const struct coa_radio_ops coa_u2785_radio_ops = {
+	.type		= "U2785B",
+	.rx_init	= u2785_rx_init,
+	.tx_init	= u2785_tx_init,
+	.set_carrier	= u2785_set_carrier,
+	.map_band	= u2785_map_band,
+};
+EXPORT_SYMBOL_GPL(coa_u2785_radio_ops);
diff --git a/target/linux/generic/files/drivers/dect/coa/sc1442x.c b/target/linux/generic/files/drivers/dect/coa/sc1442x.c
new file mode 100644
index 0000000..b5458cd
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/sc1442x.c
@@ -0,0 +1,1020 @@
+/*
+ * com_on_air - basic driver for the Dosch and Amand "com on air" cards
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * authors:
+ * (C) 2008  Andreas Schuler <krater at badterrorist dot com>
+ * (C) 2008  Matthias Wenzel <dect at mazzoo dot de>
+ * (C) 2009  Patrick McHardy <kaber at trash.net>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+#include <net/dect/dsc.h>
+#include <net/dect/transceiver.h>
+#include <asm/io.h>
+
+#include "com_on_air.h"
+#include "sc1442x_firmware.h"
+#include "dip_opcodes.h"
+
+/*
+ * The sc1442x contain a 2k data RAM and 512b code RAM. The two primary
+ * methods for memory access are direct and indirect access. In indirect
+ * mode, the access goes through the DIP and the memory bank needs to be
+ * mapped by writting its number to the control register. In direct mode
+ * the memory can be accessed directly, the three modes differ only in
+ * the address space layout. The choice between direct and indirect mode
+ * is made by the device vendor.
+ *
+ * The address space is layed out as follows:
+ *
+ * PCI - size 8k:
+ *
+ * 0x0a00 - 0x11ff:	data memory
+ * 0x1a00 - 0x1bff:	code memory
+ * 0x1f00 - 0x1fff:	DIP control and status registers
+ *
+ * PCMCIA - size 1k:
+ *
+ * 0x0000 - 0x01ff:	256 bytes memory
+ * 0x0200 - 0x02ff:	DIP control and status registers
+ *
+ * Memory of the PCMCIA device is addressed in 16 bit little endian quantities.
+ *
+ * The first bank of the data memory contains DIP specific control data,
+ * the remaining banks are used to store packet and slot configuration data,
+ * with each slot having one half memory bank assigned.
+ *
+ * The per slot data of 128 bytes is layed out as follows:
+ *
+ * Offset		RX		TX
+ *
+ * 0x00 - 0x05:		Status		Preamble
+ * 0x06 - 0x0d:		A-Field		A-Field
+ * 0x0e - 0x35:		B-Field		B-Field
+ *
+ * 0x65 - 0x68:		Radio Cfg	Radio Cfg
+ * 0x69 - 0x6f:		BMC Ctrl	BMC Ctrl
+ * 0x70 - 0x7f:		DCS IV/Key	DCS IV/Key
+ * 0x70 - 0x7a:		DCS state	DCS state
+ */
+
+#define SC1442X_DIPSTOPPED		0x80
+#define SC1442X_PRESCALER_ENABLED	0x40
+#define SC1442X_TIMER_INTERRUPT_ENABLED	0x02
+
+/* Memory access modes */
+#define SC1442X_LINEAR_MODE		0x01
+#define SC1442X_LINEAR_MODE_0		(SC14421_LINEAR_MODE | 0x0)
+#define SC1442X_LINEAR_MODE_1		(SC14421_LINEAR_MODE | 0x2)
+#define SC1442X_LINEAR_MODE_2		(SC14421_LINEAR_MODE | 0x3)
+
+/* Indirect mode RAM bank select */
+#define SC1442X_RAMBANK0		0x00
+#define SC1442X_RAMBANK1		0x04
+#define SC1442X_RAMBANK2		0x08
+#define SC1442X_RAMBANK3		0x0c
+#define SC1442X_RAMBANK4		0x10
+#define SC1442X_RAMBANK5		0x14
+#define SC1442X_RAMBANK6		0x18
+#define SC1442X_RAMBANK7		0x1c
+#define SC1442X_CODEBANK		0x20
+#define SC1442X_BANKSIZE		0x100
+
+/* Interrupts 0-3 */
+#define SC1442X_IRQ_SLOT_0_5		0x01
+#define SC1442X_IRQ_SLOT_6_11		0x02
+#define SC1442X_IRQ_SLOT_12_17		0x04
+#define SC1442X_IRQ_SLOT_18_23		0x08
+#define SC1442X_IRQ_TIMER		0x10
+#define SC1442X_IRQ_MASK		0x1f
+
+/* Interrupt status 1: DIP/CLK100/TIM1/TIM0/SPI/UART/P10/KEYB */
+#define SC14424_RESET_INT_PENDING_1	0x1f02
+/* Interrupt status 2: CLK8K/TONE */
+#define SC14424_RESET_INT_PENDING_2	0x1f03
+
+/* DIP_INT and CLK100_INT priority level */
+#define SC14424_INT_PRIORITY_1		0x1f06
+
+/* P1 output control */
+#define SC14424_P1_SET_OUTPUT_DATA	0x1f21
+#define SC14424_P1_RESET_OUTPUT_DATA	0x1f22
+
+/* P1 input/output direction */
+#define SC14424_P1_DIR_REG		0x1f23
+
+/*
+ * Burst Mode Controller control information
+ */
+
+/* Maximum number of unmasked errors in S-field bits 8 to 31 */
+#define SC1442X_BC0_S_ERR_SHIFT		4
+/* Invert incoming data (RDI) */
+#define SC1442X_BC0_INV_RDI		0x08
+/* Invert outgoing data (TDO) */
+#define SC1442X_BC0_INV_TDO		0x04
+/* Disable writing B-field on A-field CRC error */
+#define SC1442X_BC0_SENS_A		0x02
+/* PP/FP mode */
+#define SC1442X_BC0_PP_MODE		0x01
+
+/* Error test mask for S-field bits 15-8 */
+#define SC1442X_BC1_MASK_MASK		0xff
+
+/* Sliding error test mask for S-field bits 15-8 */
+#define SC1442X_BC2_SLIDE_MASK		0xff
+
+/* DAC output value when BCM is active (for frequency control?) */
+#define SC1442X_BC3_DAC_MASK		0x1f
+
+/* Only perform phase jump for correct A-field CRC + SL_EN_ADJ command */
+#define SC1442X_BC4_ADP			0x10
+/* Window in which S-field is accepted */
+#define SC1442X_BC4_WIN_MASK		0x0f
+
+/* Amplitude-trimming of gaussian shape */
+#define SC1442X_BC5_VOL_SHIFT		4
+/* Disable scrambling */
+#define SC1442X_BC5_SC_OFF		0x08
+/* PD1 synchronization pattern:
+ * 0 = S-field received, 1 = preamble + first 2 bits of synchronization word */
+#define SC1442X_BC5_DO_FR		0x04
+/* TDO output shape */
+#define SC1442X_BC5_TDO_DIGITAL		0x00
+#define SC1442X_BC5_TDO_GAUSIAN		0x01
+#define SC1442X_BC5_TDO_POWER_DOWN	0x02
+#define SC1442X_BC5_TDO_MID_LEVEL	0x03
+
+/* Low 4 bits of multiframe number */
+#define SC1442X_BC6_MFR_SHIFT		4
+#define SC1442X_BC6_MFR_MASK		0xf0
+/* Frame number */
+#define SC1442X_BC6_FR_MASK		0x0f
+
+/*
+ * Burst Mode Controller status information
+ */
+
+/* Peak binary value of ADC (RSSI) */
+#define SC1442X_ST0_ADC_MASK		0x3f
+
+/* S-pattern recognized according to BMC configuration */
+#define SC1442X_ST1_IN_SYNC		0x80
+
+/* A-field R-CRC correct */
+#define SC1442X_ST1_A_CRC		0x40
+
+/* Protected Bn-subfield R-CRC correct */
+#define SC1442X_ST1_B_CRC_MASK		0x3c
+#define SC1442X_ST1_B1_CRC		0x20
+#define SC1442X_ST1_B2_CRC		0x10
+#define SC1442X_ST1_B3_CRC		0x08
+#define SC1442X_ST1_B4_CRC		0x04
+
+/* B-field X-CRC correct */
+#define SC1442X_ST1_X_CRC		0x02
+
+/* Z-field equals X-CRC */
+#define SC1442X_ST1_Z_CRC		0x01
+
+/* Phase offset of received S-field: which of the nine internal clock cycles
+ * per symbol sampled the incoming data. The frequency deviation can be
+ * calculated from the difference of the offsets of two consequitive frames as:
+ *
+ * K * (T / 9) / 10m = K * 96ns / 10m = K * 9.6ppm
+ */
+#define SC1442X_ST2_TAP_SHIFT		4
+#define SC1442X_ST2_TAP_MASK		0xf0
+#define SC1442X_ST2_TAP_SCALE		(DECT_PHASE_OFFSET_SCALE * 96 / 10)
+
+/* Number of unmasked S-field errors according to BMC configuration */
+#define SC1442X_ST2_S_ERR_SHIFT		0
+#define SC1442X_ST2_S_ERR_MASK		0x0f
+
+/* Phase offset of received S-field: difference of number of symbol periods
+ * between nominal 11520 symbols per frame and actual number of symbols. The
+ * frequency deviation can be calculated from the difference of two
+ * consequitive frames as:
+ *
+ * N * T / 10m = N * 870ns / 10m = N * 87ppm
+ */
+#define SC1442X_ST3_PHASE_MASK		0xff
+#define SC1442X_ST3_PHASE_SCALE		(DECT_PHASE_OFFSET_SCALE * 87)
+
+/* DC offset of received data to comparator reference input (DAC) */
+#define SC1442X_ST4_DC_MASK		0x3f
+
+/*
+ * Codec configuration
+ */
+
+#define SC1442X_CC_SIZE			6
+
+#define SC1442X_CC0_STANDBY		0xc2
+#define SC1442X_CC0_POWERDOWN		0x3d
+
+/* Logical memory banks */
+#define SC1442X_BANK_UNITS		8
+#define SC1442X_SLOT_BANK_SIZE		128
+
+static const u8 banktable[] = {
+	SC1442X_RAMBANK1,
+	SC1442X_RAMBANK2,
+	SC1442X_RAMBANK3,
+	SC1442X_RAMBANK4,
+	SC1442X_RAMBANK5,
+	SC1442X_RAMBANK6,
+};
+
+static const u8 slottable[] = {
+	Slot00, Slot01, Slot02, Slot03, Slot04, Slot05, Slot06, Slot07,
+	Slot08, Slot09, Slot10, Slot11, Slot12, Slot13, Slot14, Slot15,
+	Slot16, Slot17, Slot18, Slot19, Slot20, Slot21,	Slot22, Slot23,
+};
+
+static const u8 sc1442x_rx_funcs[DECT_PACKET_MAX + 1][DECT_B_MAX + 1][2][2] = {
+	[DECT_PACKET_P00][DECT_B_NONE][0][0]		= RX_P00,
+	[DECT_PACKET_P00][DECT_B_NONE][0][1]		= RX_P00_Sync,
+	[DECT_PACKET_P32][DECT_B_UNPROTECTED][0][0]	= RX_P32U,
+	[DECT_PACKET_P32][DECT_B_UNPROTECTED][1][0]	= RX_P32U_Enc,
+};
+
+static const u8 sc1442x_tx_funcs[DECT_PACKET_MAX + 1][DECT_B_MAX + 1][2] = {
+	[DECT_PACKET_P00][DECT_B_NONE][0]		= TX_P00,
+	[DECT_PACKET_P32][DECT_B_UNPROTECTED][0]	= TX_P32U,
+	[DECT_PACKET_P32][DECT_B_UNPROTECTED][1]	= TX_P32U_Enc,
+};
+
+/*
+ * Raw IO functions
+ */
+
+static void sc1442x_lock_mem(struct coa_device *dev) __acquires(dev->lock)
+{
+	spin_lock_irq(&dev->lock);
+}
+
+static void sc1442x_unlock_mem(struct coa_device *dev) __releases(dev->lock)
+{
+	mmiowb();
+	spin_unlock_irq(&dev->lock);
+}
+
+static u8 sc1442x_readb(const struct coa_device *dev, u16 offset)
+{
+	switch (dev->type) {
+	case COA_TYPE_PCI:
+		return readb(dev->sc1442x_base + offset);
+	case COA_TYPE_PCMCIA:
+		return le16_to_cpu(readw(dev->sc1442x_base + 2 * offset));
+	default:
+		BUG();
+	}
+}
+
+static u16 sc1442x_readw(const struct coa_device *dev, u16 offset)
+{
+	u32 tmp;
+
+	switch (dev->type) {
+	case COA_TYPE_PCI:
+		return le16_to_cpu(readw(dev->sc1442x_base + offset));
+	case COA_TYPE_PCMCIA:
+		tmp = le32_to_cpu(readl(dev->sc1442x_base + 2 * offset));
+		return (tmp >> 8) | (tmp & 0xff);
+	default:
+		BUG();
+	}
+}
+
+static void sc1442x_writeb(const struct coa_device *dev, u16 offset, u8 value)
+{
+	switch (dev->type) {
+	case COA_TYPE_PCI:
+		writeb(value, dev->sc1442x_base + offset);
+		break;
+	case COA_TYPE_PCMCIA:
+		writew(cpu_to_le16(value), dev->sc1442x_base + 2 * offset);
+		break;
+	}
+}
+
+static void sc1442x_writew(const struct coa_device *dev, u16 offset, u16 value)
+{
+	u32 tmp;
+
+	switch (dev->type) {
+	case COA_TYPE_PCI:
+		writew(cpu_to_le16(value), dev->sc1442x_base + offset);
+		break;
+	case COA_TYPE_PCMCIA:
+		tmp = ((value & 0xff00) << 8) | (value & 0xff);
+		writel(cpu_to_le32(tmp), dev->sc1442x_base + 2 * offset);
+		break;
+	}
+}
+
+static void sc1442x_stop_dip(struct coa_device *dev)
+{
+	/* Prevent the interrupt handler from restarting the DIP */
+	dev->ctrl = SC1442X_DIPSTOPPED;
+
+	/* Stop the DIP and wait for interrupt handler to complete */
+	sc1442x_writeb(dev, dev->cfg_reg, SC1442X_DIPSTOPPED);
+	synchronize_irq(dev->irq);
+}
+
+static void sc1442x_start_dip(struct coa_device *dev)
+{
+	dev->ctrl = 0;
+	sc1442x_writeb(dev, dev->cfg_reg, 0x00);
+}
+
+static void sc1442x_switch_to_bank(const struct coa_device *dev, u8 bank)
+{
+	if (dev->type != COA_TYPE_PCMCIA)
+		return;
+	sc1442x_writeb(dev, dev->cfg_reg, bank | dev->ctrl);
+	/* need to wait for 4 IO cycles */
+	inb_p(dev->config_base);
+	inb_p(dev->config_base);
+	inb_p(dev->config_base);
+	inb_p(dev->config_base);
+}
+
+static void sc1442x_toggle_led(struct coa_device *dev)
+{
+	if (dev->type != COA_TYPE_PCI)
+		return;
+
+	if ((dev->led & 0xf) > 0x7)
+		sc1442x_writeb(dev, SC14424_P1_SET_OUTPUT_DATA, 0x40);
+	else
+		sc1442x_writeb(dev, SC14424_P1_RESET_OUTPUT_DATA, 0x40);
+	dev->led++;
+}
+
+/*
+ * Code memory IO functions
+ */
+static void sc1442x_write_cmd(const struct coa_device *dev, u16 label,
+			      u8 opcode, u8 operand)
+{
+	sc1442x_writew(dev, dev->code_base + 2 * label, operand << 8 | opcode);
+}
+
+static void sc1442x_to_cmem(const struct coa_device *dev,
+			    const u8 *src, u16 length)
+{
+	u16 i;
+
+	for (i = 0; i < length; i++)
+		sc1442x_writeb(dev, dev->code_base + i, src[i]);
+}
+
+/*
+ * Data memory IO functions
+ */
+static inline u8 sc1442x_dreadb(const struct coa_device *dev, u16 offset)
+{
+	return sc1442x_readb(dev, dev->data_base + (offset & dev->data_mask));
+}
+
+static inline u16 sc1442x_dreadw(const struct coa_device *dev, u16 offset)
+{
+	return sc1442x_readw(dev, dev->data_base + (offset & dev->data_mask));
+}
+
+static inline void sc1442x_dwriteb(const struct coa_device *dev,
+				  u16 offset, u8 value)
+{
+	sc1442x_writeb(dev, dev->data_base + (offset & dev->data_mask), value);
+}
+
+static inline void sc1442x_dwritew(const struct coa_device *dev,
+				   u16 offset, u16 value)
+{
+	sc1442x_writew(dev, dev->data_base + (offset & dev->data_mask), value);
+}
+
+static void sc1442x_to_dmem(const struct coa_device *dev, u16 offset,
+			    const void *src, u16 length)
+{
+	u16 i = 0;
+
+	for (; length >= 2; length -= 2, i += 2)
+		sc1442x_dwritew(dev, offset + i, *(u16 *)(src + i));
+	for (; length >= 1; length -= 1, i += 1)
+		sc1442x_dwriteb(dev, offset + i, *(u8 *)(src + i));
+}
+
+static void sc1442x_from_dmem(const struct coa_device *dev, void *dst,
+			      u16 offset, u16 length)
+{
+	u16 i = 0;
+
+	for (; length >= 2; length -= 2, i += 2)
+		*(u16 *)(dst +  i) = sc1442x_dreadw(dev, offset + i);
+	for (; length >= 1; length -= 1, i += 1)
+		*(u8 *)(dst + i) = sc1442x_dreadb(dev, offset + i);
+}
+
+static u8 sc1442x_dip_bankaddress(u8 slot)
+{
+	return (slot / 2 + 2) * SC1442X_SLOT_BANK_SIZE / SC1442X_BANK_UNITS;
+}
+
+static u8 sc1442x_slot_bank(u8 slot)
+{
+	return banktable[slot / 4];
+}
+
+static u16 sc1442x_slot_offset(u8 slot)
+{
+	u16 offset;
+
+	offset = SC1442X_BANKSIZE + slot / 4 * SC1442X_BANKSIZE;
+	if (slot & 0x2)
+		offset += SC1442X_BANKSIZE / 2;
+	return offset;
+}
+
+void sc1442x_rfdesc_write(const struct coa_device *dev, u16 offset,
+			  const u8 *src, u16 length)
+{
+	sc1442x_to_dmem(dev, offset + RF_DESC, src, length);
+}
+
+/*
+ * Ciphering
+ */
+
+static void sc1442x_dcs_init(const struct coa_device *dev,
+			     const struct dect_transceiver *trx,
+			     u8 slot, u32 mfn, u8 framenum)
+{
+	const struct dect_transceiver_slot *ts = &trx->slots[slot];
+	u16 off = sc1442x_slot_offset(slot);
+	__le64 iv;
+
+	iv = dect_dsc_iv(mfn, framenum);
+	sc1442x_to_dmem(dev, off + DCS_IV, &iv, 8);
+	sc1442x_to_dmem(dev, off + DCS_CK, &ts->ck, 8);
+}
+
+/* Transfer DCS cipher state between two TDD slots of a MAC connection */
+static void sc1442x_transfer_dcs_state(struct coa_device *dev,
+				       struct dect_transceiver *trx,
+				       u8 slot)
+{
+	u8 slot2 = slot + DECT_HALF_FRAME_SIZE;
+	struct dect_transceiver_slot *ts1 = &trx->slots[slot];
+	struct dect_transceiver_slot *ts2 = &trx->slots[slot2];
+	u8 dcs_state[DCS_STATE_SIZE];
+	u16 off;
+
+	if (!(ts1->flags & DECT_SLOT_CIPHER) ||
+	    !(ts2->flags & DECT_SLOT_CIPHER))
+		return;
+
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+	off = sc1442x_slot_offset(slot);
+	sc1442x_from_dmem(dev, dcs_state, off + DCS_STATE, DCS_STATE_SIZE);
+
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot2));
+	off = sc1442x_slot_offset(slot2);
+	sc1442x_to_dmem(dev, off + DCS_STATE, dcs_state, DCS_STATE_SIZE);
+}
+
+/*
+ * Transceiver operations
+ */
+
+static void sc1442x_disable(const struct dect_transceiver *trx)
+{
+	sc1442x_stop_dip(dect_transceiver_priv(trx));
+}
+
+static void sc1442x_enable(const struct dect_transceiver *trx)
+{
+	const struct coa_device *dev = dect_transceiver_priv(trx);
+	u8 slot;
+
+	/* Restore slot table to a pristine state */
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+	for (slot = 0; slot < DECT_FRAME_SIZE; slot++) {
+		sc1442x_write_cmd(dev, slottable[slot] + 0, WT, 1);
+		sc1442x_write_cmd(dev, slottable[slot] + 1, WNT, 1);
+	}
+
+	if (trx->cell->mode == DECT_MODE_FP) {
+		sc1442x_write_cmd(dev, ClockSyncOn, WT, 1);
+		sc1442x_write_cmd(dev, ClockAdjust, WT, 1);
+		sc1442x_write_cmd(dev, ClockSyncOff, WT, 1);
+
+		sc1442x_write_cmd(dev, TX_P32U_Enc, JMP, LoadEncKey);
+		sc1442x_write_cmd(dev, RX_P32U_Enc, JMP, LoadEncState);
+	} else {
+		sc1442x_write_cmd(dev, ClockSyncOn, P_SC, PSC_S_SYNC_ON);
+		sc1442x_write_cmd(dev, ClockAdjust, EN_SL_ADJ, 1);
+		sc1442x_write_cmd(dev, ClockSyncOff, P_SC, 0x00);
+
+		sc1442x_write_cmd(dev, RX_P32U_Enc, JMP, LoadEncKey);
+		sc1442x_write_cmd(dev, TX_P32U_Enc, JMP, LoadEncState);
+	}
+
+	if (trx->mode == DECT_TRANSCEIVER_MASTER)
+		sc1442x_write_cmd(dev, RFStart, BR, SlotTable);
+	else {
+		sc1442x_write_cmd(dev, RFStart, BR, SyncInit);
+		sc1442x_write_cmd(dev, SyncLoop, BR, Sync);
+	}
+
+	sc1442x_start_dip(dect_transceiver_priv(trx));
+}
+
+static void sc1442x_confirm(const struct dect_transceiver *trx)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	/*
+	 * This locks the firmware into a cycle where it will receive every
+	 * 24th slot. This must happen within the time it takes to transmit
+	 * 22 slots after the interrupt to lock to the correct signal.
+	 */
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+	sc1442x_write_cmd(dev, SyncLoop, BR, SyncLock);
+	sc1442x_unlock_mem(dev);
+}
+
+static void sc1442x_unlock(const struct dect_transceiver *trx)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	/* Restore jump into Sync loop */
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+	sc1442x_write_cmd(dev, SyncLoop, BR, Sync);
+	sc1442x_write_cmd(dev, SlotTable, BR, SyncInit);
+	sc1442x_unlock_mem(dev);
+}
+
+static void sc1442x_lock(const struct dect_transceiver *trx, u8 slot)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	/*
+	 * We're receiving the single slot "slot". Adjust the firmware so it
+	 * will jump into the correct slottable position on the next receive
+	 * event. This will automagically establish the correct slot numbers
+	 * and thereby interrupt timing for all slots.
+	 */
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+	sc1442x_write_cmd(dev, SlotTable, SLOTZERO, 0);
+	sc1442x_write_cmd(dev, SyncLoop, BR, slottable[slot]);
+	sc1442x_unlock_mem(dev);
+}
+
+static void sc1442x_write_bmc_config(const struct coa_device *dev,
+				     u8 slot, bool tx)
+{
+	u16 off;
+	u8 cfg;
+
+	off = sc1442x_slot_offset(slot) + BMC_CTRL;
+
+	cfg  = 2 << SC1442X_BC0_S_ERR_SHIFT;
+	cfg |= SC1442X_BC0_INV_TDO;
+	cfg |= SC1442X_BC0_SENS_A;
+	if (slot < 12 && !tx)
+		cfg |= SC1442X_BC0_PP_MODE;
+	sc1442x_dwriteb(dev, off + 0, cfg);
+
+	/* S-field error mask */
+	sc1442x_dwriteb(dev, off + 1, 0);
+	/* S-field sliding window error mask */
+	sc1442x_dwriteb(dev, off + 2, 0x3f);
+
+	/* DAC output */
+	sc1442x_dwriteb(dev, off + 3, 0);
+
+	cfg  = SC1442X_BC4_ADP;
+	cfg |= 0xf & SC1442X_BC4_WIN_MASK;
+	cfg |= 0x80;
+	sc1442x_dwriteb(dev, off + 4, cfg);
+
+	cfg  = SC1442X_BC5_DO_FR;
+	cfg |= tx ? SC1442X_BC5_TDO_DIGITAL : SC1442X_BC5_TDO_POWER_DOWN;
+	sc1442x_dwriteb(dev, off + 5, cfg);
+
+	/* Frame number */
+	sc1442x_dwriteb(dev, off + 6, 0);
+}
+
+static void sc1442x_set_mode(const struct dect_transceiver *trx,
+			     const struct dect_channel_desc *chd,
+			     enum dect_slot_states mode)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	bool cipher = trx->slots[chd->slot].flags & DECT_SLOT_CIPHER;
+	bool sync = trx->slots[chd->slot].flags & DECT_SLOT_SYNC;
+	u8 slot = chd->slot, prev = dect_slot_sub(slot, 1);
+
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+
+	switch (mode) {
+	case DECT_SLOT_IDLE:
+		sc1442x_write_cmd(dev, slottable[prev] + 0, WT, 1);
+		sc1442x_write_cmd(dev, slottable[prev] + 1, WNT, 1);
+		sc1442x_write_cmd(dev, slottable[slot] + 0, WT, 1);
+		sc1442x_write_cmd(dev, slottable[slot] + 1, WNT, 1);
+		break;
+	case DECT_SLOT_SCANNING:
+	case DECT_SLOT_RX:
+		sc1442x_write_cmd(dev, slottable[prev] + 0, BK_C,
+				  sc1442x_dip_bankaddress(slot));
+		sc1442x_write_cmd(dev, slottable[prev] + 1, JMP, RFInit);
+		sc1442x_write_cmd(dev, slottable[slot] + 0, WT, 1);
+		sc1442x_write_cmd(dev, slottable[slot] + 1, JMP,
+				  sc1442x_rx_funcs[chd->pkt][chd->b_fmt][cipher][sync]);
+
+		sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+		sc1442x_write_bmc_config(dev, slot, false);
+		break;
+	case DECT_SLOT_TX:
+		sc1442x_write_cmd(dev, slottable[prev] + 0, BK_C,
+				  sc1442x_dip_bankaddress(slot));
+		sc1442x_write_cmd(dev, slottable[prev] + 1, JMP, RFInit);
+		sc1442x_write_cmd(dev, slottable[slot] + 0, WT, 1);
+		sc1442x_write_cmd(dev, slottable[slot] + 1, JMP,
+				  sc1442x_tx_funcs[chd->pkt][chd->b_fmt][cipher]);
+
+		sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+		sc1442x_write_bmc_config(dev, slot, true);
+		break;
+	}
+	sc1442x_unlock_mem(dev);
+}
+
+static void sc1442x_set_carrier(const struct dect_transceiver *trx,
+				u8 slot, u8 carrier)
+{
+	const struct dect_transceiver_slot *ts = &trx->slots[slot];
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	u16 off;
+
+	WARN_ON(ts->state == DECT_SLOT_IDLE);
+
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+	off = sc1442x_slot_offset(slot);
+	dev->radio_ops->set_carrier(dev, off, ts->state, carrier);
+	sc1442x_unlock_mem(dev);
+}
+
+static u64 sc1442x_set_band(const struct dect_transceiver *trx,
+			    const struct dect_band *band)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+
+	return dev->radio_ops->map_band(dev, band);
+}
+
+static void sc1442x_tx(const struct dect_transceiver *trx, struct sk_buff *skb)
+{
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	const struct dect_skb_trx_cb *cb = DECT_TRX_CB(skb);
+	const struct dect_transceiver_slot *ts = &trx->slots[cb->slot];
+	u8 slot = cb->slot;
+	u16 off;
+
+	sc1442x_lock_mem(dev);
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+	off = sc1442x_slot_offset(slot);
+
+	/* Duplicate first byte for transmission during ramp-up */
+	sc1442x_dwriteb(dev, off + SD_PREAMBLE_OFF - 1, *skb_mac_header(skb));
+	sc1442x_to_dmem(dev, off + SD_PREAMBLE_OFF,
+			skb_mac_header(skb), skb->mac_len);
+	sc1442x_to_dmem(dev, off + SD_DATA_OFF, skb->data, skb->len);
+	sc1442x_dwriteb(dev, off + BMC_CTRL + BMC_CTRL_MFR_OFF, cb->frame);
+
+	/* Init DCS for slots in the first half frame */
+	if (ts->flags & DECT_SLOT_CIPHER && slot < DECT_HALF_FRAME_SIZE)
+		sc1442x_dcs_init(dev, trx, slot, cb->mfn, cb->frame);
+
+	sc1442x_toggle_led(dev);
+	sc1442x_unlock_mem(dev);
+	kfree_skb(skb);
+}
+
+const struct dect_transceiver_ops sc1442x_transceiver_ops = {
+	.name			= "sc1442x",
+	.features		= DECT_TRANSCEIVER_SLOW_HOPPING,
+	.eventrate		= 6,
+	.latency		= 6,
+	.disable		= sc1442x_disable,
+	.enable			= sc1442x_enable,
+	.confirm		= sc1442x_confirm,
+	.unlock			= sc1442x_unlock,
+	.lock			= sc1442x_lock,
+	.set_mode		= sc1442x_set_mode,
+	.set_carrier		= sc1442x_set_carrier,
+	.set_band		= sc1442x_set_band,
+	.tx			= sc1442x_tx,
+	.destructor		= dect_transceiver_free,
+};
+EXPORT_SYMBOL_GPL(sc1442x_transceiver_ops);
+
+static u8 sc1442x_clear_interrupt(const struct coa_device *dev)
+{
+	u8 int1, int2, cnt = 0;
+
+	int1 = sc1442x_readb(dev, dev->cfg_reg);
+	/* is the card still plugged? */
+	if (int1 == 0xff)
+		return 0;
+
+	int2 = int1 & SC1442X_IRQ_MASK;
+
+	/* Clear interrupt status before checking for any remaining events */
+	if (int2 && dev->type == COA_TYPE_PCI)
+		sc1442x_writeb(dev, SC14424_RESET_INT_PENDING_1, 0x80);
+
+	while (int1) {
+		cnt++;
+		if (cnt > 254) {
+			int2 = 0;
+			break;
+		}
+
+		int1 = sc1442x_readb(dev, dev->cfg_reg) & SC1442X_IRQ_MASK;
+		int2 |= int1;
+	}
+
+	return int2 & SC1442X_IRQ_MASK;
+}
+
+static void sc1442x_update_phase_offset(struct coa_device *dev,
+					struct dect_transceiver_slot *ts,
+					u8 framenum)
+{
+	struct sc1442x_phase_state *ps = &dev->phase_state[ts->chd.slot / 2];
+	u16 off = sc1442x_slot_offset(ts->chd.slot);
+	s32 phaseoff;
+	s8 phase;
+	u8 tap;
+
+	/* The phase offset is calculated from the differences of the tap and
+	 * phase status of two consequitive frames. The tap field contains
+	 * which of the nine internal clock cycles per symbol sampled the
+	 * incoming data and measures small scale frequency deviations up to
+	 * +-8 * 9.6ppm == +-86.4ppm. The phase field contains the absolute
+	 * phase offset in multiples of 87ppm.
+	 */
+	tap   = sc1442x_dreadb(dev, off + 2) >> SC1442X_ST2_TAP_SHIFT;
+	phase = sc1442x_dreadb(dev, off + 3);
+
+	if (dect_next_framenum(ps->framenum) == framenum) {
+		phaseoff = (tap - ps->tap) * SC1442X_ST2_TAP_SCALE;
+		phaseoff += (phase - ps->phase) * SC1442X_ST3_PHASE_SCALE;
+
+		ts->phaseoff = dect_average_phase_offset(ts->phaseoff, phaseoff);
+	}
+
+	ps->framenum = framenum;
+	ps->tap      = tap;
+	ps->phase    = phase;
+}
+
+static void sc1442x_process_slot(struct coa_device *dev,
+				 struct dect_transceiver *trx,
+				 struct dect_transceiver_event *event,
+				 u8 slot)
+{
+	struct dect_transceiver_slot *ts = &trx->slots[slot];
+	struct sk_buff *skb;
+	u8 status, framenum, csum, rssi;
+	u32 mfn;
+	u16 off;
+
+	if (ts->state == DECT_SLOT_IDLE || ts->state == DECT_SLOT_TX)
+		return;
+
+	mfn = trx->cell->timer_base[DECT_TIMER_RX].mfn;
+	framenum = trx->cell->timer_base[DECT_TIMER_RX].framenum;
+
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+	off = sc1442x_slot_offset(slot);
+
+	/*
+	 * The SC1442X contains a 6 bit ADC for RSSI measurement, convert to
+	 * units used by the stack.
+	 */
+	status = sc1442x_dreadb(dev, off + SD_RSSI_OFF);
+	rssi = (status & SC1442X_ST0_ADC_MASK) * DECT_RSSI_RANGE / 63;
+
+	/* validate and clear checksum */
+	status = sc1442x_dreadb(dev, off + SD_CSUM_OFF);
+	if (!(status & SC1442X_ST1_IN_SYNC))
+		goto out;
+	sc1442x_dwriteb(dev, off + SD_CSUM_OFF, 0);
+
+	if (!(status & SC1442X_ST1_A_CRC)) {
+		ts->rx_a_crc_errors++;
+		if (ts->chd.pkt == DECT_PACKET_P00)
+			goto out;
+		csum = 0;
+	} else
+		csum = DECT_CHECKSUM_A_CRC_OK;
+
+	if (ts->chd.pkt != DECT_PACKET_P00) {
+		if (!(status & SC1442X_ST1_X_CRC))
+			ts->rx_x_crc_errors++;
+		else
+			csum |= DECT_CHECKSUM_X_CRC_OK;
+
+		if (!(status & SC1442X_ST1_Z_CRC))
+			ts->rx_z_crc_errors++;
+		else
+			csum |= DECT_CHECKSUM_Z_CRC_OK;
+	}
+
+	/* calculate phase offset */
+	sc1442x_update_phase_offset(dev, ts, framenum);
+
+	skb = dect_transceiver_alloc_skb(trx, slot);
+	if (skb == NULL)
+		goto out;
+	sc1442x_from_dmem(dev, skb->data, off + SD_DATA_OFF, skb->len);
+	DECT_TRX_CB(skb)->csum = csum;
+	DECT_TRX_CB(skb)->rssi = rssi;
+	__skb_queue_tail(&event->rx_queue, skb);
+
+	ts->rx_bytes += skb->len;
+	ts->rx_packets++;
+
+	sc1442x_toggle_led(dev);
+out:
+	ts->rssi = dect_average_rssi(ts->rssi, rssi);
+	dect_transceiver_record_rssi(event, slot, rssi);
+
+	/* Update frame number for next reception */
+	sc1442x_dwriteb(dev, off + BMC_CTRL + BMC_CTRL_MFR_OFF, framenum + 1);
+
+	/* Init DCS for slots in the first half frame */
+	if (ts->flags & DECT_SLOT_CIPHER && slot < DECT_HALF_FRAME_SIZE)
+		sc1442x_dcs_init(dev, trx, slot, mfn, framenum + 1);
+}
+
+irqreturn_t sc1442x_interrupt(int irq, void *dev_id)
+{
+	struct dect_transceiver *trx = dev_id;
+	struct coa_device *dev = dect_transceiver_priv(trx);
+	struct dect_transceiver_event *event;
+	u8 slot, i;
+
+	irq = sc1442x_clear_interrupt(dev);
+	if (!irq)
+		return IRQ_NONE;
+
+	if (unlikely(hweight8(irq) != 1 && net_ratelimit()))
+		dev_info(dev->dev, "lost some interrupts\n");
+
+	for (i = 0; i < 4; i++) {
+		if (!(irq & (1 << i)))
+			continue;
+
+		event = dect_transceiver_event(trx, i % 2, i * 6);
+		if (event == NULL)
+			goto out;
+
+		spin_lock(&dev->lock);
+		for (slot = 6 * i; slot < 6 * (i + 1); slot++) {
+			sc1442x_process_slot(dev, trx, event, slot);
+			if (slot < DECT_HALF_FRAME_SIZE)
+				sc1442x_transfer_dcs_state(dev, trx, slot);
+		}
+		spin_unlock(&dev->lock);
+
+		dect_transceiver_queue_event(trx, event);
+	}
+out:
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(sc1442x_interrupt);
+
+static void sc1442x_init_slot(const struct coa_device *dev, u8 slot)
+{
+	u16 off;
+
+	sc1442x_switch_to_bank(dev, sc1442x_slot_bank(slot));
+	off = sc1442x_slot_offset(slot);
+	dev->radio_ops->rx_init(dev, off);
+	dev->radio_ops->tx_init(dev, off);
+}
+
+static int sc1442x_check_dram(const struct coa_device *dev)
+{
+	unsigned int bank, i;
+	unsigned int cnt;
+	u16 off;
+	u8 val;
+
+	for (bank = 0; bank < 8; bank++) {
+		sc1442x_switch_to_bank(dev, 4 * bank);
+
+		off = bank * SC1442X_BANKSIZE;
+		for (i = 0; i < SC1442X_BANKSIZE - 2; i++)
+			sc1442x_dwriteb(dev, off + i, bank + i);
+	}
+
+	cnt = 0;
+	for (bank = 0; bank < 8; bank++) {
+		sc1442x_switch_to_bank(dev, 4 * bank);
+
+		off = bank * SC1442X_BANKSIZE;
+		for (i = 0; i < SC1442X_BANKSIZE - 2; i++) {
+			val = sc1442x_dreadb(dev, off + i);
+			if (val != ((bank + i) & 0xff)) {
+				dev_err(dev->dev,
+					"memory error bank %.2x offset %.2x: "
+					"%.2x != %.2x\n", bank, i,
+					val, (bank + i) & 0xff);
+				cnt++;
+			}
+			sc1442x_dwriteb(dev, off + i, 0);
+		}
+	}
+
+	if (cnt > 0)
+		dev_err(dev->dev, "found %u memory r/w errors\n", cnt);
+	return cnt ? -1 : 0;
+}
+
+int sc1442x_init_device(struct coa_device *dev)
+{
+	unsigned int i;
+	u8 slot;
+
+	spin_lock_init(&dev->lock);
+	dev->ctrl = SC1442X_DIPSTOPPED;
+
+	if (sc1442x_check_dram(dev) < 0)
+		return -EIO;
+
+	dev_info(dev->dev, "Loading firmware ...\n");
+	sc1442x_switch_to_bank(dev, SC1442X_CODEBANK);
+	sc1442x_to_cmem(dev, sc1442x_firmware, sizeof(sc1442x_firmware));
+
+	sc1442x_clear_interrupt(dev);
+
+	/* Init DIP */
+	sc1442x_switch_to_bank(dev, SC1442X_RAMBANK0);
+
+	/* Disable Codec */
+	sc1442x_dwriteb(dev, DIP_CC_INIT, SC1442X_CC0_STANDBY);
+	for (i = 1; i < SC1442X_CC_SIZE; i++)
+		sc1442x_dwriteb(dev, DIP_CC_INIT + i, 0);
+
+	for (slot = 0; slot < DECT_FRAME_SIZE; slot += 2)
+		sc1442x_init_slot(dev, slot);
+
+	if (dev->type == COA_TYPE_PCI) {
+		/* Enable DIP interrupt */
+		sc1442x_writeb(dev, SC14424_INT_PRIORITY_1, 0x70);
+		/* Enable SPI for LED control */
+		sc1442x_writeb(dev, SC14424_P1_DIR_REG, 0xd6);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sc1442x_init_device);
+
+void sc1442x_shutdown_device(struct coa_device *dev)
+{
+	sc1442x_stop_dip(dev);
+
+	if (dev->type == COA_TYPE_PCI) {
+		/* Clear pening interrupts */
+		sc1442x_writeb(dev, SC14424_RESET_INT_PENDING_1, 0xff);
+		sc1442x_writeb(dev, SC14424_RESET_INT_PENDING_2, 0xff);
+		/* Reset LED */
+		sc1442x_writeb(dev, SC14424_P1_RESET_OUTPUT_DATA, 0x40);
+	}
+}
+EXPORT_SYMBOL_GPL(sc1442x_shutdown_device);
+
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.asm b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.asm
new file mode 100644
index 0000000..223a760
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.asm
@@ -0,0 +1,389 @@
+		CPU     SC14421
+		ORG	0
+
+		BR      Start
+
+PB_LED          EQU     0x80
+PB_RX_ON        EQU     0x40
+PB_TX_ON        EQU     0x10
+PB_RADIOPOWER   EQU     0x04
+PB_DCTHRESHOLD  EQU     0x02
+PB_RSSI		EQU	0x01
+
+; synchronisation control
+PSC_ARPD1	EQU	0x80
+PSC_S_SYNC	EQU	0x40
+PSC_S_SYNC_ON	EQU	0x20
+PSC_EOPSM	EQU	0x10
+
+; memory banks 0-7, lower and upper halfs (128 bytes each)
+BANK0_LOW	EQU	0x00
+BANK0_HIGH	EQU	0x10
+BANK1_LOW	EQU	0x20
+BANK1_HIGH	EQU	0x30
+BANK2_LOW	EQU	0x40
+BANK2_HIGH	EQU	0x50
+BANK3_LOW	EQU	0x60
+BANK3_HIGH	EQU	0x70
+BANK4_LOW	EQU	0x80
+BANK4_HIGH	EQU	0x90
+BANK5_LOW	EQU	0xa0
+BANK5_HIGH	EQU	0xb0
+BANK6_LOW	EQU	0xc0
+BANK6_HIGH	EQU	0xd0
+BANK7_LOW	EQU	0xe0
+BANK7_HIGH	EQU	0xf0
+
+; Codec Control
+DIP_CC_INIT	EQU	0x10
+
+; Radio configuration word
+RF_DESC		EQU	0x65
+
+; BMC control information
+BMC_CTRL_SIZE	EQU	7
+BMC_CTRL	EQU	0x69
+
+; (multi) frame number for scambler and DCS
+BMC_CTRL_MFR_OFF EQU	6
+
+; Cipher IV/Key
+DCS_DESC	EQU	0x70
+DCS_IV		EQU	DCS_DESC
+DCS_CK		EQU	DCS_DESC + 0x8
+
+; Cipher state
+DCS_STATE	EQU	0x70
+DCS_STATE_SIZE	EQU	11
+
+SD_PREAMBLE_OFF	EQU	0x01
+SD_A_FIELD_OFF	EQU	0x06
+SD_B_FIELD_OFF	EQU	0x0E
+
+; status descriptor
+SD_BASE_OFF	EQU	0x00
+SD_RSSI_OFF	EQU	0x00
+SD_CSUM_OFF	EQU	0x01
+SD_DATA_OFF	EQU	0x06
+
+; U2785 radio
+U2785_CFG1_LEN	EQU	24
+U2785_CFG2_LEN	EQU	9
+
+;-------------------------------------------------------------
+
+Start:		BR	InitDIP
+;-------------------------------------------------------------
+
+SlotTable:	SLOTZERO
+
+Slot00:		WT	1
+		WNT	1
+Slot01:		WT	1
+		WNT	1
+Slot02:		WT	1
+		WNT	1
+Slot03:		WT	1
+		WNT	1
+Slot04:		WT	1
+		WNT	1
+Slot05:		WT	1
+		WNT	1
+		U_INT0
+
+Slot06:		WT	1
+		WNT	1
+Slot07:		WT	1
+		WNT	1
+Slot08:		WT	1
+		WNT	1
+Slot09:		WT	1
+		WNT	1
+Slot10:		WT	1
+		WNT	1
+Slot11:		WT	1
+		WNT	1
+		U_INT1
+
+Slot12:		WT	1
+		WNT	1
+Slot13:		WT	1
+		WNT	1
+Slot14:		WT	1
+		WNT	1
+Slot15:		WT	1
+		WNT	1
+Slot16:		WT	1
+		WNT	1
+Slot17:		WT	1
+		WNT	1
+		U_INT2
+
+Slot18:		WT	1
+		WNT	1
+Slot19:		WT	1
+		WNT	1
+Slot20:		WT	1
+		WNT	1
+Slot21:		WT	1
+		WNT	1
+Slot22:		WT	1
+		WNT	1
+Slot23:		WT	1
+		WNT	1
+		U_INT3
+
+		BR	SlotTable
+
+;-------------------------------------------------------------------------------
+; Receive a P00 packet
+;
+RX_P00:		JMP	Receive		; Receive S- and beginning of A-field		|
+RX_P00_End:	B_BRFU	SD_B_FIELD_OFF	; Receive unprotected full-slot B-field		| p: 95		A: 63
+		JMP	ReceiveEnd	; End reception					| p: 96		B:  0
+		BR	WriteBMC1	;
+
+RX_P00_Sync:	JMP	ReceiveSync	; Receive S- and beginning of A-field		|
+		BR	RX_P00_End
+
+; Receive a P32 packet using the the unprotected full slot B-field format in
+; the D32-field
+;
+RX_P32U_Enc:	JMP	LoadEncKey
+RX_P32U:	JMP	Receive
+		B_BRFU	SD_B_FIELD_OFF	; Receive unprotected full-slot B-field		| p: 95		A: 63
+		JMP	RX_P32U_BZ	; Receive B-field				| p: 96		B:  0
+		BR	WriteBMC2
+
+;-------------------------------------------------------------------------------
+; Transmit a P00 packet
+;
+TX_P00:		JMP	Transmit	; Transmit S- and beginning of A-field		|
+		JMP	TransmitEnd	; End transmission				| p: 94		A: 62
+		BR	label_53	;
+
+; Transmit a P32 packet using the unprotected full slot B-field format in the
+; D32-field
+;
+TX_P32U_Enc:	JMP	LoadEncKey
+TX_P32U:	JMP	Transmit	; Transmit S- and beginning of A-field		|
+		B_BTFU	SD_B_FIELD_OFF	; Transmit unprotected full-slot B-field data	| p: 95		A: 63
+		JMP	TX_P32U_BZ	; Transmit the B- and Z-fields			| p: 96		B: 0
+		BR	label_54	;
+
+;-------------------------------------------------------------------------------
+WriteBMC1:	B_WRS	SD_BASE_OFF	; write status
+		WT	6
+
+label_53:	B_RST
+label_54:	P_LDL	PB_RX_ON | PB_TX_ON
+		WT	5
+		WNT	1
+		RTN
+
+;-------------------------------------------------------------------------------
+WriteBMC2:	B_WRS	SD_BASE_OFF	; write status
+		WT	6
+label_58:	B_RST
+		P_LDL	PB_RX_ON | PB_TX_ON
+		RTN
+;-------------------------------------------------------------------------------
+; Enable the receiver, receive the S-field and the first 61 bits of the D-field
+; (93 bits total)
+;
+Receive:	B_RST
+		B_RC	BMC_CTRL
+		WT	BMC_CTRL_SIZE + 1
+		P_LDH	PB_RX_ON
+		P_LDL	PB_RSSI		; enable RSSI measurement
+		WT	25
+		WNT	1		; Wait until beginning of slot			|
+		WT	8		;						| p: -33--26
+		B_XON			;						| p: -25
+ClockSyncOn:	P_SC	PSC_S_SYNC_ON	;						| p: -24
+		P_LDH	PB_DCTHRESHOLD	;						| p: -23
+		WT	5		;						| p: -22--16
+		B_SR			; Receive S-field				| p: -17
+ClockAdjust:	EN_SL_ADJ		;						| p: -16	S: 0
+		WT	12		;						| p: -15--4	S: 1-12
+		P_LDL	PB_DCTHRESHOLD	;						| p:  -3	S: 13
+		WT	32		;						| p:  -2-29	S: 14-45
+ClockSyncOff:	P_SC	0x00		;						| p:  30	S: 46
+		B_AR2	SD_A_FIELD_OFF	; Start reception of A-field/A-field CRC	| p:  31	S: 47
+		WT	62		; Receive first 61 bits of A-field		| p:  32-92	A:  0-60
+		RTN			; Return					| p:  93	A: 61
+
+ReceiveSync:	B_RST
+		B_RC	BMC_CTRL
+		WT	BMC_CTRL_SIZE + 1
+		P_LDH	PB_RX_ON
+		P_LDL	PB_RSSI		; enable RSSI measurement
+		WT	25
+		WNT	1		; Wait until beginning of slot			|
+		WT	8		;						| p: -33--26
+		B_XON			;						| p: -25
+		P_SC	PSC_S_SYNC_ON	;						| p: -24
+		P_LDH	PB_DCTHRESHOLD	;						| p: -23
+		WT	5		;						| p: -22--16
+		B_SR			; Receive S-field				| p: -17
+		EN_SL_ADJ		;						| p: -16	S: 0
+		WT	12		;						| p: -15--4	S: 1-12
+		P_LDL	PB_DCTHRESHOLD	;						| p:  -3	S: 13
+		WT	32		;						| p:  -2-29	S: 14-45
+		P_SC	0x00		;						| p:  30	S: 46
+		B_AR2	SD_A_FIELD_OFF	; Start reception of A-field/A-field CRC	| p:  31	S: 47
+		WT	61		; Receive first 61 bits of A-field		| p:  32-92	A:  0-60
+		RTN			; Return					| p:  93	A: 61
+
+; Receive the B- and Z-fields of a P32 packet using the protected full slot
+; B-field format in the D32-field
+RX_P32U_BZ:	WT	249		;						| p:  97-345	B:   1-249
+		WT	79		;						| p: 346-415	B: 250-319
+					;						| p: 416-419	B: 320-323	X: 0-3
+					;						| p: 420-423	Z:   0-  3
+					;						| p: 424	??
+ReceiveEnd:	P_LDH	PB_RSSI		;						|
+		P_LDL	PB_RX_ON
+		BR	SaveEncState
+;-------------------------------------------------------------------------------
+; Enable transmitter, transmit the S-field and the first 61 bits of the D-field
+; (93 bits total)
+;
+Transmit:	P_LDH	0x00		;
+		WT	40		;
+		B_RST			;
+		B_RC	BMC_CTRL	;
+		WNT	1		; Wait until beginning of slot
+		B_ST	0x00		; Start transmission of S-field data		|
+		WT	1		; Wait one bit					| p: -8		S:  0
+		P_LDH	PB_TX_ON	; Enable transmitter				| p: -7		S:  1
+		WT	37		; Transmit 29 bits S-field			| p: -6-30	S:  2-38
+		B_AT2	SD_A_FIELD_OFF	; Start transission of A-field data/A-field CRC	| p: 31		S: 39
+		WT	62		; Transmit first 61 bits of A-field		| p: 32-92	A:  0-60
+		RTN			; Return					| p: 93		A: 61
+
+;-------------------------------------------------------------------------------
+;
+;
+TX_P32U_BZ:	WT	249		; 						| p:  97-345	B:   1-249
+		WT	84		; Last bits of B-field data			| p: 346-415	B: 250-319
+					; X-field					| p: 416-419	B: 320-323	X: 0-3
+					; Z-field (?)					| p: 420-424	Z:   0-  3
+					; 5 bits of crap?				| p: 425-429
+		B_RST			; Reset BMC					| p: 430
+
+TransmitEnd:	P_LDL	PB_TX_ON	; Disable transmitter				|
+		WT	8		; Wait until transmitter is disabled		|
+		P_LDL	0x00		;
+		BR	SaveEncState
+
+;-------------------------------------------------------------------------------
+
+RFInit:		RFEN			; Enable RF-clock
+		WT	2
+
+		MEN1N			; Transfer first radio configuration word
+		M_WR	RF_DESC
+		WT	U2785_CFG1_LEN + 1
+		M_RST
+		MEN1
+
+		MEN1N			; Transfer second radio configuration word
+		M_WR	RF_DESC + U2785_CFG1_LEN / 8
+		WT	U2785_CFG2_LEN + 1
+		M_RST
+		MEN1
+		;WT	1
+
+		P_LDL	0x20
+		WT	10
+		MEN2
+		WT	182
+		MEN2N
+		WT	16
+		RTN
+;--------------------------------------------------------------
+;
+LoadEncKey:	D_RST
+		D_LDK	DCS_DESC	; load IV (64 bits) and cipher key (64 bits)
+		WT	16
+		D_LDK	0
+		D_PREP	0
+		WT	39
+		D_PREP	0
+		RTN
+
+SaveEncState:	D_WRS   DCS_STATE
+		WT	DCS_STATE_SIZE	; actually should be -1, but does not work
+		D_WRS	0
+		D_RST
+		RTN
+
+LoadEncState:	D_RST
+		D_LDS	DCS_STATE
+		WT	DCS_STATE_SIZE	; actually should be -1, but does not work
+		D_LDS	0
+		RTN
+;-------------------------------------------------------------
+
+SyncInit:	BK_C	BANK1_LOW
+Sync:		JMP	RFInit
+		WT	250
+		P_SC	PSC_S_SYNC_ON
+		P_LDH	PB_RX_ON | PB_DCTHRESHOLD
+		UNLCK
+		WT	64
+		B_XOFF
+		B_SR
+		WNT	20
+		JMP1	SFieldFound
+		B_RST
+		U_INT1
+		WNT	23
+		BR	Sync
+;-------------------------------------------------------------
+
+SFieldFound:	WNT	23
+		P_SC	0x00
+SyncLock:	JMP	RFInit
+		JMP	RX_P00
+		U_INT0
+		WNT	22
+SyncLoop:	BR	Sync
+;-------------------------------------------------------------
+
+InitDIP:	;B_RST
+		BK_C	BANK0_LOW
+		C_LD	DIP_CC_INIT
+		WT	10
+		;B_RC	BMC_CTRL
+		;WT	BMC_CTRL_SIZE + 1
+		B_RST
+		;C_ON
+		WT	10
+		P_EN
+		P_LD	0x04
+		RCK_INT
+		RFEN
+RFStart:	BR	SyncInit
+;-------------------------------------------------------------
+
+		SHARED	DIP_CC_INIT,RF_DESC
+		SHARED	BMC_CTRL,BMC_CTRL_MFR_OFF
+		SHARED	SD_RSSI_OFF,SD_CSUM_OFF,SD_PREAMBLE_OFF,SD_DATA_OFF
+
+		SHARED	SlotTable
+		SHARED	Slot00,Slot01,Slot02,Slot03,Slot04,Slot05,Slot06,Slot07
+		SHARED	Slot08,Slot09,Slot10,Slot11,Slot12,Slot13,Slot14,Slot15
+		SHARED	Slot16,Slot17,Slot18,Slot19,Slot20,Slot21,Slot22,Slot23
+
+		SHARED	RFStart,RFInit
+		SHARED	SyncInit,Sync,SyncLock,SyncLoop
+		SHARED	ClockSyncOn,ClockSyncOff,ClockAdjust
+		SHARED	PSC_ARPD1,PSC_S_SYNC,PSC_S_SYNC_ON,PSC_EOPSM
+
+		SHARED	RX_P00,RX_P00_Sync,RX_P32U,RX_P32U_Enc
+		SHARED	TX_P00,TX_P32U,TX_P32U_Enc
+
+		SHARED	DCS_IV,DCS_CK,DCS_STATE,DCS_STATE_SIZE
+		SHARED	LoadEncKey,LoadEncState
diff --git a/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.c b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.c
new file mode 100644
index 0000000..77880ce
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.c
@@ -0,0 +1,73 @@
+/*
+ * automatically generated file
+ * DO NOT EDIT
+ * edit firmware/filename.asm instead
+ */
+
+#include "sc1442x_firmware.h"
+
+const unsigned char sc1442x_firmware[] = {
+	0x01, 0x01, 0x01, 0xd4, 0x0d, 0x00, 0x09, 0x01,
+	0x08, 0x01, 0x09, 0x01, 0x08, 0x01, 0x09, 0x01,
+	0x08, 0x01, 0x09, 0x01, 0x08, 0x01, 0x09, 0x01,
+	0x08, 0x01, 0x09, 0x01, 0x08, 0x01, 0x61, 0x00,
+	0x09, 0x01, 0x08, 0x01, 0x09, 0x01, 0x08, 0x01,
+	0x09, 0x01, 0x08, 0x01, 0x09, 0x01, 0x08, 0x01,
+	0x09, 0x01, 0x08, 0x01, 0x09, 0x01, 0x08, 0x01,
+	0x6b, 0x00, 0x09, 0x01, 0x08, 0x01, 0x09, 0x01,
+	0x08, 0x01, 0x09, 0x01, 0x08, 0x01, 0x09, 0x01,
+	0x08, 0x01, 0x09, 0x01, 0x08, 0x01, 0x09, 0x01,
+	0x08, 0x01, 0x6d, 0x00, 0x09, 0x01, 0x08, 0x01,
+	0x09, 0x01, 0x08, 0x01, 0x09, 0x01, 0x08, 0x01,
+	0x09, 0x01, 0x08, 0x01, 0x09, 0x01, 0x08, 0x01,
+	0x09, 0x01, 0x08, 0x01, 0x6f, 0x00, 0x01, 0x02,
+	0x02, 0x57, 0x2d, 0x0e, 0x02, 0x83, 0x01, 0x4b,
+	0x02, 0x6c, 0x01, 0x39, 0x02, 0xac, 0x02, 0x57,
+	0x2d, 0x0e, 0x02, 0x81, 0x01, 0x52, 0x02, 0x86,
+	0x02, 0x95, 0x01, 0x4d, 0x02, 0xac, 0x02, 0x86,
+	0x25, 0x0e, 0x02, 0x92, 0x01, 0x4e, 0x39, 0x00,
+	0x09, 0x06, 0x20, 0x00, 0xec, 0x50, 0x09, 0x05,
+	0x08, 0x01, 0x04, 0x00, 0x39, 0x00, 0x09, 0x06,
+	0x20, 0x00, 0xec, 0x50, 0x04, 0x00, 0x20, 0x00,
+	0x33, 0x69, 0x09, 0x08, 0xed, 0x40, 0xec, 0x01,
+	0x09, 0x19, 0x08, 0x01, 0x09, 0x08, 0x27, 0x00,
+	0xea, 0x20, 0xed, 0x02, 0x09, 0x05, 0x29, 0x00,
+	0x2c, 0x00, 0x09, 0x0c, 0xec, 0x02, 0x09, 0x20,
+	0xea, 0x00, 0x3f, 0x06, 0x09, 0x3e, 0x04, 0x00,
+	0x20, 0x00, 0x33, 0x69, 0x09, 0x08, 0xed, 0x40,
+	0xec, 0x01, 0x09, 0x19, 0x08, 0x01, 0x09, 0x08,
+	0x27, 0x00, 0xea, 0x20, 0xed, 0x02, 0x09, 0x05,
+	0x29, 0x00, 0x2c, 0x00, 0x09, 0x0c, 0xec, 0x02,
+	0x09, 0x20, 0xea, 0x00, 0x3f, 0x06, 0x09, 0x3d,
+	0x04, 0x00, 0x09, 0xf9, 0x09, 0x4f, 0xed, 0x01,
+	0xec, 0x40, 0x01, 0xb4, 0xed, 0x00, 0x09, 0x28,
+	0x20, 0x00, 0x33, 0x69, 0x08, 0x01, 0x31, 0x00,
+	0x09, 0x01, 0xed, 0x10, 0x09, 0x25, 0x37, 0x06,
+	0x09, 0x3e, 0x04, 0x00, 0x09, 0xf9, 0x09, 0x54,
+	0x20, 0x00, 0xec, 0x10, 0x09, 0x08, 0xec, 0x00,
+	0x01, 0xb4, 0x0b, 0x00, 0x09, 0x02, 0xa4, 0x00,
+	0xb9, 0x65, 0x09, 0x19, 0xa9, 0x00, 0xa5, 0x00,
+	0xa4, 0x00, 0xb9, 0x68, 0x09, 0x0a, 0xa9, 0x00,
+	0xa5, 0x00, 0xec, 0x20, 0x09, 0x0a, 0xa7, 0x00,
+	0x09, 0xb6, 0xa6, 0x00, 0x09, 0x10, 0x04, 0x00,
+	0x40, 0x00, 0x50, 0x70, 0x09, 0x10, 0x50, 0x00,
+	0x44, 0x00, 0x09, 0x27, 0x44, 0x00, 0x04, 0x00,
+	0x5f, 0x70, 0x09, 0x0b, 0x5f, 0x00, 0x40, 0x00,
+	0x04, 0x00, 0x40, 0x00, 0x57, 0x70, 0x09, 0x0b,
+	0x57, 0x00, 0x04, 0x00, 0x0f, 0x20, 0x02, 0x99,
+	0x09, 0xfa, 0xea, 0x20, 0xed, 0x42, 0x28, 0x00,
+	0x09, 0x40, 0x26, 0x00, 0x29, 0x00, 0x08, 0x14,
+	0x03, 0xcd, 0x20, 0x00, 0x6b, 0x00, 0x08, 0x17,
+	0x01, 0xbf, 0x08, 0x17, 0xea, 0x00, 0x02, 0x99,
+	0x02, 0x38, 0x61, 0x00, 0x08, 0x16, 0x01, 0xbf,
+	0x0f, 0x00, 0xfa, 0x10, 0x09, 0x0a, 0x20, 0x00,
+	0x09, 0x0a, 0xe9, 0x00, 0xe8, 0x04, 0x62, 0x00,
+	0x0b, 0x00, 0x01, 0xbe, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
diff --git a/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.h b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.h
new file mode 100644
index 0000000..bcf126a
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/coa/sc1442x_firmware.h
@@ -0,0 +1,66 @@
+#ifndef SC1442X_FIRMWARE
+#define SC1442X_FIRMWARE
+
+extern const unsigned char sc1442x_firmware[510];
+
+#define DIP_CC_INIT 0x10
+#define RF_DESC 0x65
+#define BMC_CTRL 0x69
+#define BMC_CTRL_MFR_OFF 0x6
+#define SD_RSSI_OFF 0x0
+#define SD_CSUM_OFF 0x1
+#define SD_PREAMBLE_OFF 0x1
+#define SD_DATA_OFF 0x6
+#define SlotTable 0x2
+#define Slot00 0x3
+#define Slot01 0x5
+#define Slot02 0x7
+#define Slot03 0x9
+#define Slot04 0xB
+#define Slot05 0xD
+#define Slot06 0x10
+#define Slot07 0x12
+#define Slot08 0x14
+#define Slot09 0x16
+#define Slot10 0x18
+#define Slot11 0x1A
+#define Slot12 0x1D
+#define Slot13 0x1F
+#define Slot14 0x21
+#define Slot15 0x23
+#define Slot16 0x25
+#define Slot17 0x27
+#define Slot18 0x2A
+#define Slot19 0x2C
+#define Slot20 0x2E
+#define Slot21 0x30
+#define Slot22 0x32
+#define Slot23 0x34
+#define RFStart 0xDD
+#define RFInit 0x99
+#define SyncInit 0xBE
+#define Sync 0xBF
+#define SyncLock 0xCF
+#define SyncLoop 0xD3
+#define ClockSyncOn 0x60
+#define ClockSyncOff 0x68
+#define ClockAdjust 0x64
+#define PSC_ARPD1 0x80
+#define PSC_S_SYNC 0x40
+#define PSC_S_SYNC_ON 0x20
+#define PSC_EOPSM 0x10
+#define RX_P00 0x38
+#define RX_P00_Sync 0x3C
+#define RX_P32U 0x3F
+#define RX_P32U_Enc 0x3E
+#define TX_P00 0x43
+#define TX_P32U 0x47
+#define TX_P32U_Enc 0x46
+#define DCS_IV 0x70
+#define DCS_CK 0x78
+#define DCS_STATE 0x70
+#define DCS_STATE_SIZE 0xB
+#define LoadEncKey 0xAC
+#define LoadEncState 0xB9
+
+#endif /* SC1442X_FIRMWARE */
diff --git a/target/linux/generic/files/drivers/dect/vtrx/Kconfig b/target/linux/generic/files/drivers/dect/vtrx/Kconfig
new file mode 100644
index 0000000..fb86eee
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/Kconfig
@@ -0,0 +1,5 @@
+config DECT_VTRX
+	tristate "DECT virtual transceiver support"
+	depends on DECT
+	help
+	  This option enables support for the virtual DECT transceiver.
diff --git a/target/linux/generic/files/drivers/dect/vtrx/Makefile b/target/linux/generic/files/drivers/dect/vtrx/Makefile
new file mode 100644
index 0000000..08abbf5
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DECT_VTRX)		+= dect-vtrx.o
+dect-vtrx-objs			:= vtrx.o vtrx-sysfs.o mw_to_dbm.o
diff --git a/target/linux/generic/files/drivers/dect/vtrx/mw_to_dbm.c b/target/linux/generic/files/drivers/dect/vtrx/mw_to_dbm.c
new file mode 100644
index 0000000..55122a8
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/mw_to_dbm.c
@@ -0,0 +1,164 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <net/dect/transceiver.h>
+#include "vtrx.h"
+
+static const struct {
+	u64		mw;
+	int		dbm;
+} mw_to_dbm_tbl[] = {
+	{ 5ULL,			-93 },
+	{ 6ULL,			-92 },
+	{ 7ULL,			-91 },
+	{ 10ULL,		-90 },
+	{ 12ULL,		-89 },
+	{ 15ULL,		-88 },
+	{ 19ULL,		-87 },
+	{ 25ULL,		-86 },
+	{ 31ULL,		-85 },
+	{ 39ULL,		-84 },
+	{ 50ULL,		-83 },
+	{ 63ULL,		-82 },
+	{ 79ULL,		-81 },
+	{ 100ULL,		-80 },
+	{ 125ULL,		-79 },
+	{ 158ULL,		-78 },
+	{ 199ULL,		-77 },
+	{ 251ULL,		-76 },
+	{ 316ULL,		-75 },
+	{ 398ULL,		-74 },
+	{ 501ULL,		-73 },
+	{ 630ULL,		-72 },
+	{ 794ULL,		-71 },
+	{ 1000ULL,		-70 },
+	{ 1258ULL,		-69 },
+	{ 1584ULL,		-68 },
+	{ 1995ULL,		-67 },
+	{ 2511ULL,		-66 },
+	{ 3162ULL,		-65 },
+	{ 3981ULL,		-64 },
+	{ 5011ULL,		-63 },
+	{ 6309ULL,		-62 },
+	{ 7943ULL,		-61 },
+	{ 10000ULL,		-60 },
+	{ 12589ULL,		-59 },
+	{ 15848ULL,		-58 },
+	{ 19952ULL,		-57 },
+	{ 25118ULL,		-56 },
+	{ 31622ULL,		-55 },
+	{ 39810ULL,		-54 },
+	{ 50118ULL,		-53 },
+	{ 63095ULL,		-52 },
+	{ 79432ULL,		-51 },
+	{ 100000ULL,		-50 },
+	{ 125892ULL,		-49 },
+	{ 158489ULL,		-48 },
+	{ 199526ULL,		-47 },
+	{ 251188ULL,		-46 },
+	{ 316227ULL,		-45 },
+	{ 398107ULL,		-44 },
+	{ 501187ULL,		-43 },
+	{ 630957ULL,		-42 },
+	{ 794328ULL,		-41 },
+	{ 1000000ULL,		-40 },
+	{ 1258925ULL,		-39 },
+	{ 1584893ULL,		-38 },
+	{ 1995262ULL,		-37 },
+	{ 2511886ULL,		-36 },
+	{ 3162277ULL,		-35 },
+	{ 3981071ULL,		-34 },
+	{ 5011872ULL,		-33 },
+	{ 6309573ULL,		-32 },
+	{ 7943282ULL,		-31 },
+	{ 10000000ULL,		-30 },
+	{ 12589254ULL,		-29 },
+	{ 15848931ULL,		-28 },
+	{ 19952623ULL,		-27 },
+	{ 25118864ULL,		-26 },
+	{ 31622776ULL,		-25 },
+	{ 39810717ULL,		-24 },
+	{ 50118723ULL,		-23 },
+	{ 63095734ULL,		-22 },
+	{ 79432823ULL,		-21 },
+	{ 100000000ULL,		-20 },
+	{ 125892541ULL,		-19 },
+	{ 158489319ULL,		-18 },
+	{ 199526231ULL,		-17 },
+	{ 251188643ULL,		-16 },
+	{ 316227766ULL,		-15 },
+	{ 398107170ULL,		-14 },
+	{ 501187233ULL,		-13 },
+	{ 630957344ULL,		-12 },
+	{ 794328234ULL,		-11 },
+	{ 1000000000ULL,	-10 },
+	{ 1258925411ULL,	-9 },
+	{ 1584893192ULL,	-8 },
+	{ 1995262314ULL,	-7 },
+	{ 2511886431ULL,	-6 },
+	{ 3162277660ULL,	-5 },
+	{ 3981071705ULL,	-4 },
+	{ 5011872336ULL,	-3 },
+	{ 6309573444ULL,	-2 },
+	{ 7943282347ULL,	-1 },
+	{ 10000000000ULL,	0 },
+	{ 12589254117ULL,	1 },
+	{ 15848931924ULL,	2 },
+	{ 19952623149ULL,	3 },
+	{ 25118864315ULL,	4 },
+	{ 31622776601ULL,	5 },
+	{ 39810717055ULL,	6 },
+	{ 50118723362ULL,	7 },
+	{ 63095734448ULL,	8 },
+	{ 79432823472ULL,	9 },
+	{ 100000000000ULL,	10 },
+	{ 125892541179ULL,	11 },
+	{ 158489319246ULL,	12 },
+	{ 199526231496ULL,	13 },
+	{ 251188643150ULL,	14 },
+	{ 316227766016ULL,	15 },
+	{ 398107170553ULL,	16 },
+	{ 501187233627ULL,	17 },
+	{ 630957344480ULL,	18 },
+	{ 794328234724ULL,	19 },
+	{ 1000000000000ULL,	20 },
+	{ 1258925411794ULL,	21 },
+	{ 1584893192461ULL,	22 },
+	{ 1995262314968ULL,	23 },
+	{ 2511886431509ULL,	24 },
+};
+
+int dect_mw_to_dbm(u64 mw)
+{
+	unsigned int min, max, mid;
+	u64 val;
+
+	min = 0;
+	max = ARRAY_SIZE(mw_to_dbm_tbl) - 1;
+
+	while (min < max) {
+		mid = min + (max - min) / 2;
+
+		val = mw_to_dbm_tbl[mid].mw;
+		if (val < mw)
+			min = mid + 1;
+		else
+			max = mid;
+	}
+
+	if (val > mw) {
+		if (mid == 0)
+			return 0;
+		mid--;
+	}
+
+	return mw_to_dbm_tbl[mid].dbm;
+}
diff --git a/target/linux/generic/files/drivers/dect/vtrx/vtrx-sysfs.c b/target/linux/generic/files/drivers/dect/vtrx/vtrx-sysfs.c
new file mode 100644
index 0000000..32fbea6
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/vtrx-sysfs.c
@@ -0,0 +1,229 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <net/dect/transceiver.h>
+#include "vtrx.h"
+
+static struct class *dect_class;
+
+/*
+ * Transceivers
+ */
+
+#define VTRX_ATTR(_name, _mode, _show, _store)						\
+	struct device_attribute vtrx_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+#define VTRX_NUMERIC_ATTR(name, field, scale)						\
+static ssize_t vtrx_show_##name(struct device *dev, struct device_attribute *attr,	\
+				char *buf)						\
+{											\
+	struct dect_vtrx *vtrx = dev_get_drvdata(dev);					\
+	return sprintf(buf, "%llu\n",							\
+		       (unsigned long long)div64_u64(vtrx->field, scale));		\
+}											\
+											\
+static ssize_t vtrx_store_##name(struct device *dev, struct device_attribute *attr,	\
+				 const char *buf, size_t count)				\
+{											\
+	struct dect_vtrx *vtrx = dev_get_drvdata(dev);					\
+	char *ptr;									\
+	u32 val;									\
+											\
+	val = simple_strtoul(buf, &ptr, 10);						\
+	if (ptr == buf)									\
+		return -EINVAL;								\
+	vtrx->field = val * scale;							\
+	return count;									\
+}											\
+static VTRX_ATTR(name, S_IRUGO | S_IWUSR, vtrx_show_##name, vtrx_store_##name)
+
+VTRX_NUMERIC_ATTR(tx_power, tx_power, DECT_VTRX_POWER_SCALE);
+VTRX_NUMERIC_ATTR(pos_x, pos_x, 1000);
+VTRX_NUMERIC_ATTR(pos_y, pos_y, 1000);
+
+static ssize_t vtrx_store_remove(struct device *dev, struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct dect_vtrx *vtrx = dev_get_drvdata(dev);
+
+	dect_vtrx_free(vtrx);
+	return count;
+}
+
+static VTRX_ATTR(remove, S_IWUSR, NULL, vtrx_store_remove);
+
+static struct attribute *vtrx_attrs[] = {
+	&vtrx_attr_tx_power.attr,
+	&vtrx_attr_pos_x.attr,
+	&vtrx_attr_pos_y.attr,
+	&vtrx_attr_remove.attr,
+	NULL
+};
+
+static struct attribute_group vtrx_attr_group = {
+	.attrs		= vtrx_attrs,
+};
+
+static const struct attribute_group *vtrx_attr_groups[] = {
+	&vtrx_attr_group,
+	NULL,
+};
+
+static void dect_vtrx_release(struct device *dev)
+{
+	printk("%s\n", __func__);
+}
+
+static struct device_type dect_vtrx_group = {
+	.name		= "vtrx",
+	.groups		= vtrx_attr_groups,
+	.release	= dect_vtrx_release,
+};
+
+int dect_vtrx_register_sysfs(struct dect_vtrx *vtrx)
+{
+	struct device *dev = &vtrx->dev;
+
+	dev->type   = &dect_vtrx_group;
+	dev->class  = dect_class;
+	dev->parent = &vtrx->group->dev;
+
+	dev_set_name(dev, "%s", vtrx->trx->name);
+	dev_set_drvdata(dev, vtrx);
+
+	return device_register(dev);
+}
+
+void dect_vtrx_unregister_sysfs(struct dect_vtrx *vtrx)
+{
+	device_del(&vtrx->dev);
+}
+
+/*
+ * Groups
+ */
+
+#define GROUP_ATTR(_name, _mode, _show, _store)						\
+	struct device_attribute group_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+#define GROUP_SHOW(name, field, fmt)							\
+static ssize_t group_show_##name(struct device *dev, struct device_attribute *attr,	\
+				 char *buf)						\
+{											\
+	struct dect_vtrx_group *group = dev_get_drvdata(dev);				\
+	return sprintf(buf, fmt, group->field);						\
+}											\
+											\
+static ssize_t group_store_##name(struct device *dev, struct device_attribute *attr,	\
+				  const char *buf, size_t count)			\
+{											\
+	struct dect_vtrx_group *group = dev_get_drvdata(dev);				\
+	char *ptr;									\
+	u32 val;									\
+											\
+	val = simple_strtoul(buf, &ptr, 10);						\
+	if (ptr == buf)									\
+		return -EINVAL;								\
+	group->field = val;								\
+	return count;									\
+}											\
+static GROUP_ATTR(name, S_IRUGO | S_IWUSR, group_show_##name, group_store_##name)
+
+static ssize_t group_store_new(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct dect_vtrx_group *group = dev_get_drvdata(dev);
+	int err;
+
+	err = dect_vtrx_init(group);
+	return err ? err : count;
+}
+
+static GROUP_ATTR(new_trx, S_IWUSR, NULL, group_store_new);
+
+static struct attribute *group_attrs[] = {
+	&group_attr_new_trx.attr,
+	NULL
+};
+
+static struct attribute_group group_attr_group = {
+	.attrs		= group_attrs,
+};
+
+static const struct attribute_group *group_attr_groups[] = {
+	&group_attr_group,
+	NULL,
+};
+
+static void dect_vtrx_group_release(struct device *dev)
+{
+	printk("%s\n", __func__);
+}
+
+static struct device_type dect_vtrx_group_group = {
+	.name		= "vtrx-group",
+	.groups		= group_attr_groups,
+	.release	= dect_vtrx_group_release,
+};
+
+int dect_vtrx_group_register_sysfs(struct dect_vtrx_group *group)
+{
+	struct device *dev = &group->dev;
+
+	dev->type   = &dect_vtrx_group_group;
+	dev->class  = dect_class;
+	dev->parent = NULL;
+
+	dev_set_name(dev, "%s", group->name);
+	dev_set_drvdata(dev, group);
+
+	return device_register(dev);
+}
+
+static ssize_t store_new_group(struct class *dev, struct class_attribute *attr,
+			       const char *buf, size_t count)
+{
+	char name[16];
+
+	sscanf(buf, "%16s", name);
+	if (!dect_vtrx_group_init(name))
+		return -ENOMEM;
+	return count;
+}
+
+static CLASS_ATTR(new_group, S_IWUSR, NULL, store_new_group);
+
+void dect_vtrx_group_unregister_sysfs(struct dect_vtrx_group *group)
+{
+	device_del(&group->dev);
+}
+
+int dect_vtrx_sysfs_init(void)
+{
+	int err;
+
+	dect_class = class_create(THIS_MODULE, "dect");
+	if (dect_class == NULL)
+		return -ENOMEM;
+
+	err = class_create_file(dect_class, &class_attr_new_group);
+	if (err < 0)
+		class_destroy(dect_class);
+
+	return err;
+}
+
+void dect_vtrx_sysfs_exit(void)
+{
+	class_remove_file(dect_class, &class_attr_new_group);
+	class_destroy(dect_class);
+}
diff --git a/target/linux/generic/files/drivers/dect/vtrx/vtrx.c b/target/linux/generic/files/drivers/dect/vtrx/vtrx.c
new file mode 100644
index 0000000..d6a9084
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/vtrx.c
@@ -0,0 +1,397 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/hrtimer.h>
+#include <linux/skbuff.h>
+#include <net/dect/transceiver.h>
+#include "vtrx.h"
+
+#define vtrx_debug(vtrx, fmt, args...) \
+	pr_debug("vtrx %s: " fmt, (vtrx)->trx->name, ## args)
+
+#define DECT_SLOTS_PER_SECOND	(DECT_FRAMES_PER_SECOND * DECT_FRAME_SIZE)
+#define DECT_VTRX_RATE		(NSEC_PER_SEC / DECT_SLOTS_PER_SECOND)
+#define DECT_VTRX_DEFAULT_TRX	2
+
+#define DECT_WAVELEN_SCALE	13
+#define DECT_WAVELEN		160 /* mm */
+
+struct dect_skb_vtrx_cb {
+	struct dect_vtrx	*vtrx;
+	u8			rssi;
+	u8			carrier;
+};
+
+static LIST_HEAD(vtrx_groups);
+
+static inline struct dect_skb_vtrx_cb *DECT_VTRX_CB(const struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(struct dect_skb_vtrx_cb) > sizeof(skb->cb));
+	return (struct dect_skb_vtrx_cb *)skb->cb;
+}
+
+static unsigned int dect_vtrx_distance(const struct dect_vtrx *vtrx1,
+				       const struct dect_vtrx *vtrx2)
+{
+	int dx, dy;
+
+	dx = vtrx1->pos_x - vtrx2->pos_x;
+	dy = vtrx1->pos_y - vtrx2->pos_y;
+
+	return int_sqrt(dx * dx + dy * dy);
+}
+
+static u8 dect_vtrx_receive_rssi(const struct dect_vtrx *rx_vtrx,
+				 const struct sk_buff *skb)
+{
+	const struct dect_vtrx *tx_vtrx = DECT_VTRX_CB(skb)->vtrx;
+	unsigned int distance;
+	u64 rx_power, tmp;
+	int dbm = 0;
+
+	distance = dect_vtrx_distance(rx_vtrx, tx_vtrx);
+	if (distance == 0)
+		goto out;
+
+	tmp = 1000 * (DECT_WAVELEN << DECT_WAVELEN_SCALE) / (4 * 3141 * distance);
+	rx_power = (tx_vtrx->tx_power * tmp * tmp) >> (2 * DECT_WAVELEN_SCALE);
+	dbm = dect_mw_to_dbm(rx_power);
+out:
+	if (dbm > -33)
+		dbm = -33;
+
+	return dect_dbm_to_rssi(dbm);
+}
+
+static void dect_vtrx_process_slot(struct dect_vtrx_group *group,
+				   struct dect_vtrx *vtrx)
+{
+	struct dect_transceiver_event *event;
+	struct dect_transceiver_slot *ts;
+	struct dect_transceiver *trx = vtrx->trx;
+	struct sk_buff *skb, *best;
+	u8 slot = group->slot, rcvslot;
+	u8 rssi, best_rssi = 0;
+
+	event = dect_transceiver_event(trx, slot % 12, slot);
+	if (event == NULL)
+		return;
+
+	if (trx->state == DECT_TRANSCEIVER_UNLOCKED ||
+	    trx->state == DECT_TRANSCEIVER_LOCK_PENDING)
+		rcvslot = DECT_SCAN_SLOT;
+	else
+		rcvslot = slot;
+
+	rssi = dect_dbm_to_rssi(-80);
+	best = NULL;
+
+	ts = &trx->slots[rcvslot];
+	if (ts->state != DECT_SLOT_RX &&
+	    ts->state != DECT_SLOT_SCANNING)
+		goto queue;
+
+	skb_queue_walk(&group->txq[slot], skb) {
+		if (DECT_VTRX_CB(skb)->carrier != ts->chd.carrier)
+			continue;
+
+		rssi = dect_vtrx_receive_rssi(vtrx, skb);
+		if (best == NULL || rssi > best_rssi) {
+			best	  = skb;
+			best_rssi = rssi;
+		}
+	}
+
+	if (best == NULL)
+		goto rssi;
+	rssi = best_rssi;
+
+	skb = skb_clone(best, GFP_ATOMIC);
+	if (skb == NULL)
+		goto rssi;
+
+	DECT_TRX_CB(skb)->trx  = trx;
+	DECT_TRX_CB(skb)->slot = rcvslot;
+	DECT_TRX_CB(skb)->csum = DECT_CHECKSUM_A_CRC_OK | DECT_CHECKSUM_X_CRC_OK;
+	DECT_TRX_CB(skb)->rssi = rssi;
+	__skb_queue_tail(&event->rx_queue, skb);
+
+	ts->rx_bytes += skb->len;
+	ts->rx_packets++;
+rssi:
+	ts->rssi = dect_average_rssi(ts->rssi, rssi);
+	dect_transceiver_record_rssi(event, rcvslot, rssi);
+queue:
+	if (rcvslot != slot && best == NULL)
+		dect_release_transceiver_event(event);
+	else
+		dect_transceiver_queue_event(trx, event);
+}
+
+static enum hrtimer_restart dect_vtrx_timer(struct hrtimer *timer)
+{
+	struct dect_vtrx_group *group = container_of(timer, struct dect_vtrx_group, timer);
+	struct dect_vtrx *vtrx;
+	ktime_t time;
+
+	list_for_each_entry(vtrx, &group->act_list, list)
+		dect_vtrx_process_slot(group, vtrx);
+
+	skb_queue_purge(&group->txq[group->slot]);
+	group->slot = dect_next_slotnum(group->slot);
+
+	time = ktime_set(0, DECT_VTRX_RATE);
+	hrtimer_forward(timer, hrtimer_cb_get_time(timer), time);
+
+	return HRTIMER_RESTART;
+}
+
+/*
+ * Transceiver operations
+ */
+
+static void dect_vtrx_enable(const struct dect_transceiver *trx)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+	struct dect_vtrx_group *group = vtrx->group;
+	ktime_t time;
+
+	vtrx_debug(vtrx, "enable");
+	if (list_empty(&group->act_list)) {
+		time = ktime_set(0, DECT_VTRX_RATE);
+		hrtimer_start(&group->timer, time, HRTIMER_MODE_ABS);
+	}
+	list_move_tail(&vtrx->list, &group->act_list);
+}
+
+static void dect_vtrx_disable(const struct dect_transceiver *trx)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+	struct dect_vtrx_group *group = vtrx->group;
+
+	vtrx_debug(vtrx, "disable");
+	list_move_tail(&vtrx->list, &group->trx_list);
+	if (list_empty(&group->act_list))
+		hrtimer_cancel(&group->timer);
+}
+
+static void dect_vtrx_confirm(const struct dect_transceiver *trx)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "confirm");
+}
+
+static void dect_vtrx_unlock(const struct dect_transceiver *trx)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "unlock");
+}
+
+static void dect_vtrx_lock(const struct dect_transceiver *trx, u8 slot)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "lock");
+}
+
+static void dect_vtrx_set_mode(const struct dect_transceiver *trx,
+			       const struct dect_channel_desc *chd,
+			       enum dect_slot_states mode)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "set_mode: slot: %u mode: %u",
+		   chd->slot, mode);
+}
+
+static void dect_vtrx_set_carrier(const struct dect_transceiver *trx,
+				  u8 slot, u8 carrier)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "set carrier: slot: %u carrier: %u\n",
+		   slot, carrier);
+}
+
+static u64 dect_vtrx_set_band(const struct dect_transceiver *trx,
+			      const struct dect_band *band)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+
+	vtrx_debug(vtrx, "set band: %u\n", band->band);
+	return band->carriers;
+}
+
+static void dect_vtrx_tx(const struct dect_transceiver *trx, struct sk_buff *skb)
+{
+	struct dect_vtrx *vtrx = dect_transceiver_priv(trx);
+	struct dect_vtrx_group *group = vtrx->group;
+	u8 slot = DECT_TRX_CB(skb)->slot;
+
+	vtrx_debug(vtrx, "vtrx tx: slot: %u skb: %p\n", slot, skb);
+	DECT_VTRX_CB(skb)->vtrx    = vtrx;
+	DECT_VTRX_CB(skb)->rssi    = vtrx->tx_power;
+	DECT_VTRX_CB(skb)->carrier = trx->slots[slot].chd.carrier;
+	skb_queue_tail(&group->txq[slot], skb);
+}
+
+static const struct dect_transceiver_ops vtrx_transceiver_ops = {
+	.name			= "vtrx",
+	.eventrate		= 1,
+	.latency		= 1,
+	.enable			= dect_vtrx_enable,
+	.disable		= dect_vtrx_disable,
+	.confirm		= dect_vtrx_confirm,
+	.unlock			= dect_vtrx_unlock,
+	.lock			= dect_vtrx_lock,
+	.set_mode		= dect_vtrx_set_mode,
+	.set_carrier		= dect_vtrx_set_carrier,
+	.set_band		= dect_vtrx_set_band,
+	.tx			= dect_vtrx_tx,
+	.destructor		= dect_transceiver_free,
+};
+
+int dect_vtrx_init(struct dect_vtrx_group *group)
+{
+	struct dect_transceiver *trx;
+	struct dect_vtrx *vtrx;
+	int err;
+
+	trx = dect_transceiver_alloc(&vtrx_transceiver_ops, sizeof(*vtrx));
+	if (trx == NULL)
+		return -ENOMEM;
+
+	err = dect_register_transceiver(trx);
+	if (err < 0)
+		goto err1;
+
+	vtrx = dect_transceiver_priv(trx);
+	vtrx->group	= group;
+	vtrx->trx	= trx;
+	vtrx->tx_power	= 2 * DECT_VTRX_POWER_SCALE;
+	list_add_tail(&vtrx->list, &group->trx_list);
+
+	dect_vtrx_register_sysfs(vtrx);
+	return 0;
+
+err1:
+	dect_transceiver_free(trx);
+	return err;
+}
+
+void dect_vtrx_free(struct dect_vtrx *vtrx)
+{
+	dect_vtrx_unregister_sysfs(vtrx);
+	dect_unregister_transceiver(vtrx->trx);
+}
+
+struct dect_vtrx_group *dect_vtrx_group_init(const char *name)
+{
+	struct dect_vtrx_group *group;
+	unsigned int i;
+	int err;
+
+	group = kzalloc(sizeof(*group), GFP_KERNEL);
+	if (group == NULL)
+		goto err1;
+
+	strlcpy(group->name, name, sizeof(group->name));
+	INIT_LIST_HEAD(&group->trx_list);
+	INIT_LIST_HEAD(&group->act_list);
+	hrtimer_init(&group->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	group->timer.function = dect_vtrx_timer;
+
+	for (i = 0; i < ARRAY_SIZE(group->txq); i++)
+		skb_queue_head_init(&group->txq[i]);
+
+	err = dect_vtrx_group_register_sysfs(group);
+	if (err < 0)
+		goto err2;
+
+	list_add_tail(&group->list, &vtrx_groups);
+	return group;
+
+err2:
+	kfree(group);
+err1:
+	return NULL;
+}
+
+void dect_vtrx_group_free(struct dect_vtrx_group *group)
+{
+	struct dect_vtrx *vtrx, *next;
+	unsigned int i;
+
+	list_for_each_entry_safe(vtrx, next, &group->act_list, list)
+		dect_vtrx_free(vtrx);
+	list_for_each_entry_safe(vtrx, next, &group->trx_list, list)
+		dect_vtrx_free(vtrx);
+
+	dect_vtrx_group_unregister_sysfs(group);
+
+	for (i = 0; i < ARRAY_SIZE(group->txq); i++)
+		__skb_queue_purge(&group->txq[i]);
+
+	kfree(group);
+}
+
+static int __init vtrx_init(void)
+{
+	struct dect_vtrx_group *group;
+	unsigned int i;
+	int err;
+
+	err = dect_vtrx_sysfs_init();
+	if (err < 0)
+		goto err1;
+
+	group = dect_vtrx_group_init("group-1");
+	if (group == NULL) {
+		err = -ENOMEM;
+		goto err2;
+	}
+
+	for (i = 0; i < DECT_VTRX_DEFAULT_TRX; i++) {
+		err = dect_vtrx_init(group);
+		if (err < 0)
+			goto err3;
+	}
+
+	return 0;
+
+err3:
+	dect_vtrx_group_free(group);
+err2:
+	dect_vtrx_sysfs_exit();
+err1:
+	return err;
+}
+
+static void __exit vtrx_exit(void)
+{
+	struct dect_vtrx_group *group, *next;
+
+	list_for_each_entry_safe(group, next, &vtrx_groups, list)
+		dect_vtrx_group_free(group);
+
+	dect_vtrx_sysfs_exit();
+}
+
+module_init(vtrx_init);
+module_exit(vtrx_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/dect/vtrx/vtrx.h b/target/linux/generic/files/drivers/dect/vtrx/vtrx.h
new file mode 100644
index 0000000..67aaba6
--- /dev/null
+++ b/target/linux/generic/files/drivers/dect/vtrx/vtrx.h
@@ -0,0 +1,42 @@
+#ifndef _DECT_VTRX_H
+#define _DECT_VTRX_H
+
+struct dect_vtrx_group {
+	struct list_head	list;
+	struct device		dev;
+	char			name[16];
+	struct hrtimer		timer;
+	struct list_head	trx_list;
+	struct list_head	act_list;
+	struct sk_buff_head	txq[DECT_FRAME_SIZE];
+	unsigned int		slot;
+};
+
+struct dect_vtrx {
+	struct list_head	list;
+	struct device		dev;
+	struct dect_vtrx_group	*group;
+	struct dect_transceiver	*trx;
+	u64			tx_power;
+	unsigned int		pos_x;
+	unsigned int		pos_y;
+};
+
+extern struct dect_vtrx_group *dect_vtrx_group_init(const char *name);
+extern void	dect_vtrx_group_free(struct dect_vtrx_group *group);
+extern int	dect_vtrx_group_register_sysfs(struct dect_vtrx_group *group);
+extern void	dect_vtrx_group_unregister_sysfs(struct dect_vtrx_group *group);
+
+extern int	dect_vtrx_register_sysfs(struct dect_vtrx *vtrx);
+extern void	dect_vtrx_unregister_sysfs(struct dect_vtrx *vtrx);
+extern int	dect_vtrx_init(struct dect_vtrx_group *group);
+extern void	dect_vtrx_free(struct dect_vtrx *vtrx);
+
+extern int	dect_vtrx_sysfs_init(void);
+extern void	dect_vtrx_sysfs_exit(void);
+
+#define DECT_VTRX_POWER_SCALE		10000000000ULL
+
+extern int	dect_mw_to_dbm(u64 mw);
+
+#endif /* _DECT_VTRX_H */
diff --git a/target/linux/generic/files/include/linux/dect.h b/target/linux/generic/files/include/linux/dect.h
new file mode 100644
index 0000000..7366289
--- /dev/null
+++ b/target/linux/generic/files/include/linux/dect.h
@@ -0,0 +1,167 @@
+#ifndef _LINUX_DECT_H
+#define _LINUX_DECT_H
+
+#define DECTNAMSIZ	16
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* these have to be macros in order to be usable for module aliases */
+#define DECT_RAW	0	/* raw frames */
+#define DECT_B_SAP	1	/* DLC Broadcast Service */
+#define DECT_S_SAP	2	/* DLC Data Link Service */
+#define DECT_LU1_SAP	3	/* LU1 sockets */
+#define DECT_PROTO_NUM	4
+
+/**
+ * struct sockaddr_dect
+ *
+ * @dect_family:	address family (AF_DECT)
+ * @dect_index:		cluster index
+ */
+struct sockaddr_dect {
+	sa_family_t	dect_family;
+	int		dect_index;
+};
+
+/* raw sockets */
+
+#define DECT_RAW_AUXDATA	0
+
+/**
+ * struct dect_raw_auxdata - raw socket auxiliary frame data
+ *
+ * @mfn:	multi-frame number
+ * @frame:	frame number
+ * @slot:	slot numer
+ * @rssi:	receive signal strength indicator
+ */
+struct dect_raw_auxdata {
+	__u32		mfn;
+	__u8		frame;
+	__u8		slot;
+	__u8		rssi;
+};
+
+#define DECT_BSAP_AUXDATA	0
+
+/**
+ * struct dect_bsap_auxdata
+ *
+ * @long_page:	message contains a long page
+ */
+struct dect_bsap_auxdata {
+	__u8		long_page;
+};
+
+/**
+ * enum dect_sapis - S SAP Identifier
+ *
+ * @DECT_SAPI_CO_SIGNALLING:	connection oriented signalling
+ * @DECT_SAPI_CL_SIGNALLING:	connectionless signalling
+ * @DECT_SAPI_ANY:		wildcard
+ */
+enum dect_sapis {
+	DECT_SAPI_CO_SIGNALLING = 0,
+	DECT_SAPI_CL_SIGNALLING = 3,
+	DECT_SAPI_ANY		= 7,
+};
+
+/**
+ * enum dect_llns - Logical Link Numbers
+ *
+ * @DECT_LLN_CLASS_U:		Class U operation
+ * @DECT_LLN_CLASS_A:		Class A operation
+ * @DECT_LLN_ASSIGNABLE*:	Assignable LLN (class B operation)
+ * @DECT_LLN_UNASSIGNED:	LLN unassigned (class B operation
+ * @DECT_LLN_ANY:		wildcard
+ */
+enum dect_llns {
+	DECT_LLN_CLASS_U	= 0,
+	DECT_LLN_CLASS_A	= 1,
+	DECT_LLN_ASSIGNABLE_MIN	= 2,
+	DECT_LLN_ASSIGNABLE_MAX	= 6,
+	DECT_LLN_UNASSIGNED	= 7,
+	DECT_LLN_ANY		= 15,
+};
+
+/**
+ * struct sockaddr_dect_ssap
+ *
+ * @dect_family:	family (AF_DECT)
+ * @dect_lln:		logical link number
+ * @dect_sapi:		service access point identifier
+ * @dect_class:		class A/B
+ * @dect_index:		cluster index
+ * @dect_ari:		ARI
+ * @dect_pmid:		PMID
+ * @dect_lcn:		logical connection number
+ */
+struct sockaddr_dect_ssap {
+	sa_family_t	dect_family;
+	__u8		dect_lln:4,
+			dect_sapi:3;
+	__u8		dect_class;
+	int		dect_index;
+	__u64		dect_ari:40,
+			dect_pmid:20,
+			dect_lcn:3;
+};
+
+/* S-SAP primitives */
+#define DECT_DL_ENC_KEY	1
+#define DECT_DL_ENCRYPT	2
+
+enum dect_cipher_states {
+	DECT_CIPHER_DISABLED,
+	DECT_CIPHER_ENABLED,
+};
+
+enum dect_mac_service_types {
+	DECT_SERVICE_IN_MIN_DELAY		= 0x0,
+	DECT_SERVICE_IPX_ENCODED_PROTECTED	= 0x1,
+	DECT_SERVICE_IN_NORMAL_DELAY		= 0x2,
+	DECT_SERVICE_UNKNOWN			= 0x4,
+	DECT_SERVICE_C_CHANNEL_ONLY		= 0x5,
+	DECT_SERVICE_IP_ERROR_DETECTION		= 0x10,
+	DECT_SERVICE_IPQ_ERROR_DETECTION	= 0x14,
+	/* Lifetime encoded in low three bits */
+	DECT_SERVICE_IP_ERROR_CORRECTION	= 0x18,
+	DECT_SERVICE_IPQ_ERROR_CORRECTION	= 0x38,
+};
+
+/**
+ * struct dect_dl_encrypt - DL_ENCRYPT primitive arguments
+ *
+ * @status:		desired/achieved encryption status
+ */
+struct dect_dl_encrypt {
+	enum dect_cipher_states	status;
+};
+
+/**
+ * struct sockaddr_dect_lu - DLC U-plane LUx service instance address
+ *
+ * @dect_family:	address family (AF_DECT)
+ * @dect_mci:		MAC Connection Identifier
+ */
+struct sockaddr_dect_lu {
+	sa_family_t	dect_family;
+	int		dect_index;
+	__u64		dect_ari:40,
+			dect_pmid:20,
+			dect_lcn:3;
+};
+
+/* LU1 SAP */
+
+#define DECT_LU1_QUEUE_STATS	0
+
+struct dect_lu1_queue_stats {
+	__u32		rx_bytes;
+	__u32		rx_underflow;
+	__u32		tx_bytes;
+	__u32		tx_underflow;
+};
+
+#endif /* _LINUX_DECT_H */
diff --git a/target/linux/generic/files/include/linux/dect_netlink.h b/target/linux/generic/files/include/linux/dect_netlink.h
new file mode 100644
index 0000000..df170fb
--- /dev/null
+++ b/target/linux/generic/files/include/linux/dect_netlink.h
@@ -0,0 +1,395 @@
+#ifndef _LINUX_DECT_NETLINK_H
+#define _LINUX_DECT_NETLINK_H
+
+struct dectmsg {
+	int	dm_index;
+};
+
+enum dect_nlgroups {
+	DECTNLGRP_NONE,
+	DECTNLGRP_TRANSCEIVER,
+	DECTNLGRP_CELL,
+	DECTNLGRP_CLUSTER,
+	DECTNLGRP_LLME,
+	__DECTNLGRP_MAX
+};
+#define DECTNLGRP_MAX		(__DECTNLGRP_MAX - 1)
+
+enum dect_netlink_msg_types {
+	DECT_MSG_BASE = 0x10,
+	DECT_NEW_TRANSCEIVER,
+	DECT_DEL_TRANSCEIVER,
+	DECT_GET_TRANSCEIVER,
+	DECT_NEW_CELL,
+	DECT_DEL_CELL,
+	DECT_GET_CELL,
+	DECT_NEW_CLUSTER,
+	DECT_DEL_CLUSTER,
+	DECT_GET_CLUSTER,
+	DECT_LLME_MSG,
+	__DECT_MSG_MAX
+};
+#define DECT_MSG_MAX		(__DECT_MSG_MAX - 1)
+
+#define DECT_NR_MSGTYPES	(DECT_MSG_MAX + 1 - DECT_MSG_BASE)
+
+enum dect_list_attrs {
+	DECTA_LIST_UNSPEC,
+	DECTA_LIST_ELEM,
+	__DECTA_LIST_MAX
+};
+#define DECTA_LIST_MAX		(__DECTA_LIST_MAX - 1)
+
+enum dect_slot_states {
+	DECT_SLOT_IDLE,
+	DECT_SLOT_SCANNING,
+	DECT_SLOT_RX,
+	DECT_SLOT_TX,
+};
+
+enum dect_slot_flags {
+	DECT_SLOT_SYNC			= 0x1,
+	DECT_SLOT_CIPHER		= 0x2,
+};
+
+/**
+ * enum dect_packet_types - DECT Physical Packet Types
+ *
+ * @DECT_PACKET_P00:	short physical packet P00, 96 bits, A-field only
+ * @DECT_PACKET_P08:	low capacity physical packet P08j, 180 bits
+ * @DECT_PACKET_P32:	basic physical packet P32, 420 bits
+ * @DECT_PACKET_P80:	high capacity physical packet P80, 900 bits
+ * @DECT_PACKET_P640j:	variable capacity packet P640j, 712 bits
+ * @DECT_PACKET_P672j:	variable capacity packet P640j, 744 bits
+ */
+enum dect_packet_types {
+	DECT_PACKET_P00,
+	DECT_PACKET_P08,
+	DECT_PACKET_P32,
+	DECT_PACKET_P80,
+	DECT_PACKET_P640j,
+	DECT_PACKET_P672j,
+	__DECT_PACKET_MAX
+};
+#define DECT_PACKET_MAX         (__DECT_PACKET_MAX - 1)
+
+#define DECT_PHASE_OFFSET_SCALE		1024
+
+enum dect_slot_attrs {
+	DECTA_SLOT_UNSPEC,
+	DECTA_SLOT_NUM,
+	DECTA_SLOT_STATE,
+	DECTA_SLOT_FLAGS,
+	DECTA_SLOT_PACKET,
+	DECTA_SLOT_CARRIER,
+	DECTA_SLOT_FREQUENCY,
+	DECTA_SLOT_PHASEOFF,
+	DECTA_SLOT_RSSI,
+	DECTA_SLOT_RX_PACKETS,
+	DECTA_SLOT_RX_BYTES,
+	DECTA_SLOT_RX_A_CRC_ERRORS,
+	DECTA_SLOT_RX_X_CRC_ERRORS,
+	DECTA_SLOT_RX_Z_CRC_ERRORS,
+	DECTA_SLOT_TX_PACKETS,
+	DECTA_SLOT_TX_BYTES,
+	__DECTA_SLOT_MAX
+};
+#define DECTA_SLOT_MAX			(__DECTA_SLOT_MAX - 1)
+
+enum dect_transceiver_stats_attrs {
+	DECTA_TRANSCEIVER_STATS_UNSPEC,
+	DECTA_TRANSCEIVER_STATS_EVENT_BUSY,
+	DECTA_TRANSCEIVER_STATS_EVENT_LATE,
+	__DECTA_TRANSCEIVER_STATS_MAX
+};
+#define DECTA_TRANSCEIVER_STATS_MAX	(__DECTA_TRANSCEIVER_STATS_MAX - 1)
+
+/**
+ * @DECT_TRANSCEIVER_SLOW_HOPPING:	transceiver has slow hopping radio
+ */
+enum dect_transceiver_features {
+	DECT_TRANSCEIVER_SLOW_HOPPING	= 0x1,
+};
+
+enum dect_transceiver_attrs {
+	DECTA_TRANSCEIVER_UNSPEC,
+	DECTA_TRANSCEIVER_NAME,
+	DECTA_TRANSCEIVER_TYPE,
+	DECTA_TRANSCEIVER_FEATURES,
+	DECTA_TRANSCEIVER_LINK,
+	DECTA_TRANSCEIVER_STATS,
+	DECTA_TRANSCEIVER_BAND,
+	DECTA_TRANSCEIVER_SLOTS,
+	__DECTA_TRANSCEIVER_MAX
+};
+#define DECTA_TRANSCEIVER_MAX		(__DECTA_TRANSCEIVER_MAX - 1)
+
+enum dect_cell_flags {
+	DECT_CELL_CCP		= (1 << 0),
+	DECT_CELL_SLAVE		= (1 << 1),
+	DECT_CELL_MONITOR	= (1 << 2),
+};
+
+enum dect_cell_attrs {
+	DECTA_CELL_UNSPEC,
+	DECTA_CELL_NAME,
+	DECTA_CELL_FLAGS,
+	DECTA_CELL_TRANSCEIVERS,
+	DECTA_CELL_CLUSTER,
+	__DECTA_CELL_MAX
+};
+#define DECTA_CELL_MAX		(__DECTA_CELL_MAX - 1)
+
+enum dect_mbc_state {
+	DECT_MBC_NONE,
+	DECT_MBC_INITIATED,
+	DECT_MBC_ESTABLISHED,
+	DECT_MBC_RELEASED,
+};
+
+enum dect_mbc_tb_attrs {
+	DECTA_MBC_TB_UNSPEC,
+	DECTA_MBC_TB_LBN,
+	DECTA_MBC_TB_ECN,
+	DECTA_MBC_TB_CELL,
+	DECTA_MBC_TB_RX_SLOT,
+	DECTA_MBC_TB_TX_SLOT,
+	__DECTA_MBC_TB_MAX,
+};
+#define DECTA_MBC_TB_MAX	(__DECTA_MBC_TB_MAX - 1)
+
+enum dect_mbc_stats_attrs {
+	DECTA_MBC_STATS_UNSPEC,
+	DECTA_MBC_STATS_CS_RX_BYTES,
+	DECTA_MBC_STATS_CS_TX_BYTES,
+	DECTA_MBC_STATS_I_RX_BYTES,
+	DECTA_MBC_STATS_I_TX_BYTES,
+	DECTA_MBC_STATS_HANDOVERS,
+	__DECTA_MBC_STATS_MAX,
+};
+#define DECTA_MBC_STATS_MAX	(__DECTA_MBC_STATS_MAX - 1)
+
+enum dect_mbc_attrs {
+	DECTA_MBC_UNSPEC,
+	DECTA_MBC_MCEI,
+	DECTA_MBC_SERVICE,
+	DECTA_MBC_STATE,
+	DECTA_MBC_CIPHER_STATE,
+	DECTA_MBC_STATS,
+	DECTA_MBC_TBS,
+	__DECTA_MBC_MAX,
+};
+#define DECTA_MBC_MAX		(__DECTA_MBC_MAX - 1)
+
+enum dect_cluster_attrs {
+	DECTA_CLUSTER_UNSPEC,
+	DECTA_CLUSTER_NAME,
+	DECTA_CLUSTER_MODE,
+	DECTA_CLUSTER_PARI,
+	DECTA_CLUSTER_CELLS,
+	DECTA_CLUSTER_MBCS,
+	__DECTA_CLUSTER_MAX
+};
+#define DECTA_CLUSTER_MAX	(__DECTA_CLUSTER_MAX - 1)
+
+enum dect_cluster_modes {
+	DECT_MODE_FP,
+	DECT_MODE_PP,
+};
+
+/**
+ * DECT ARI classes
+ *
+ * @DECT_ARC_A:	Residential and private (PBX) single- and small multiple cell systems
+ * @DECT_ARC_B:	Private (PABXs) multiple cell
+ * @DECT_ARC_C: Public single and multiple cell systems
+ * @DECT_ARC_D: Public DECT access to a GSM network
+ * @DECT_ARC_E: PP to PP direct communication (private)
+ */
+enum dect_ari_classes {
+	DECT_ARC_A,
+	DECT_ARC_B,
+	DECT_ARC_C,
+	DECT_ARC_D,
+	DECT_ARC_E,
+};
+
+enum dect_ari_attrs {
+	DECTA_ARI_UNSPEC,
+	DECTA_ARI_CLASS,
+	DECTA_ARI_FPN,
+	DECTA_ARI_FPS,
+	DECTA_ARI_EMC,
+	DECTA_ARI_EIC,
+	DECTA_ARI_POC,
+	DECTA_ARI_GOP,
+	DECTA_ARI_FIL,
+	__DECTA_ARI_MAX
+};
+#define DECTA_ARI_MAX		(__DECTA_ARI_MAX - 1)
+
+enum decta_sari_attrs {
+	DECTA_SARI_UNSPEC,
+	DECTA_SARI_ARI,
+	DECTA_SARI_BLACK,
+	DECTA_SARI_TARI,
+	__DECTA_SARI_MAX
+};
+#define DECTA_SARI_MAX		(__DECTA_SARI_MAX - 1)
+
+enum dect_fixed_part_capabilities {
+	DECT_FPC_EXTENDED_FP_INFO		= 0x80000,
+	DECT_FPC_DOUBLE_DUPLEX_BEARER_CONNECTION= 0x40000,
+	DECT_FPC_RESERVED			= 0x20000,
+	DECT_FPC_DOUBLE_SLOT			= 0x10000,
+	DECT_FPC_HALF_SLOT			= 0x08000,
+	DECT_FPC_FULL_SLOT			= 0x04000,
+	DECT_FPC_FREQ_CONTROL			= 0x02000,
+	DECT_FPC_PAGE_REPETITION		= 0x01000,
+	DECT_FPC_CO_SETUP_ON_DUMMY		= 0x00800,
+	DECT_FPC_CL_UPLINK			= 0x00400,
+	DECT_FPC_CL_DOWNLINK			= 0x00200,
+	DECT_FPC_BASIC_A_FIELD_SETUP		= 0x00100,
+	DECT_FPC_ADV_A_FIELD_SETUP		= 0x00080,
+	DECT_FPC_B_FIELD_SETUP			= 0x00040,
+	DECT_FPC_CF_MESSAGES			= 0x00020,
+	DECT_FPC_IN_MIN_DELAY			= 0x00010,
+	DECT_FPC_IN_NORM_DELAY			= 0x00008,
+	DECT_FPC_IP_ERROR_DETECTION		= 0x00004,
+	DECT_FPC_IP_ERROR_CORRECTION		= 0x00002,
+	DECT_FPC_MULTIBEARER_CONNECTIONS	= 0x00001,
+};
+
+enum dect_higher_layer_capabilities {
+	DECT_HLC_ADPCM_G721_VOICE		= 0x8000,
+	DECT_HLC_GAP_PAP_BASIC_SPEECH		= 0x4000,
+	DECT_HLC_NON_VOICE_CIRCUIT_SWITCHED	= 0x2000,
+	DECT_HLC_NON_VOICE_PACKET_SWITCHED	= 0x1000,
+	DECT_HLC_STANDARD_AUTHENTICATION	= 0x0800,
+	DECT_HLC_STANDARD_CIPHERING		= 0x0400,
+	DECT_HLC_LOCATION_REGISTRATION		= 0x0200,
+	DECT_HLC_SIM_SERVICES			= 0x0100,
+	DECT_HLC_NON_STATIC_FIXED_PART		= 0x0080,
+	DECT_HLC_CISS_SERVICE			= 0x0040,
+	DECT_HLC_CLMS_SERVICE			= 0x0020,
+	DECT_HLC_COMS_SERVICE			= 0x0010,
+	DECT_HLC_ACCESS_RIGHTS_REQUESTS		= 0x0008,
+	DECT_HLC_EXTERNAL_HANDOVER		= 0x0004,
+	DECT_HLC_CONNECTION_HANDOVER		= 0x0002,
+	DECT_HLC_RESERVED			= 0x0001,
+};
+
+enum dect_extended_fixed_part_capabilities {
+	DECT_EFPC_WRS_MASK			= 0x1f80,
+	DECT_EFPC_WRS_CRFP_HOPS_MASK		= 0x1800,
+	DECT_EFPC_WRS_CRFP_HOPS_1		= 0x0000,
+	DECT_EFPC_WRS_CRFP_HOPS_2		= 0x0800,
+	DECT_EFPC_WRS_CRFP_HOPS_3		= 0x1000,
+	DECT_EFPC_WRS_CRFP_HOPS_NONE		= 0x1800,
+	DECT_EFPC_WRS_CRFP_ENCRYPTION		= 0x0400,
+	DECT_EFPC_WRS_REP_HOPS_MASK		= 0x0300,
+	DECT_EFPC_WRS_REP_HOPS_NONE		= 0x0000,
+	DECT_EFPC_WRS_REP_HOPS_1		= 0x0100,
+	DECT_EFPC_WRS_REP_HOPS_2		= 0x0200,
+	DECT_EFPC_WRS_REP_HOPS_3		= 0x0300,
+	DECT_EFPC_WRS_REP_INTERLACING		= 0x0080,
+	DECT_EFPC_SYNC_MASK			= 0x0060,
+	DECT_EFPC_SYNC_PROLONGED_PREAMBLE	= 0x0020,
+	DECT_EFPC_SYNC_RESERVED1		= 0x0010,
+	DECT_EFPC_MAC_SUSPEND_RESUME		= 0x0008,
+	DECT_EFPC_MAC_IP_Q_SERVICE		= 0x0004,
+	DECT_EFPC_EXTENDED_FP_INFO2		= 0x0002,
+	DECT_EFPC_RESERVED2			= 0x0001,
+};
+
+enum dect_extended_higher_layer_capabilities {
+	DECT_EHLC_ISDN_DATA_SERVICE		= 0x000001,
+	DECT_EHLC_DPRS_FREL			= 0x000002,
+	DECT_EHLC_DPRS_STREAM			= 0x000004,
+	DECT_EHLC_DATA_SERVICE_PROFILE_D	= 0x000008,
+	DECT_EHLC_LRMS				= 0x000010,
+	DECT_EHLC_ASYMETRIC_BEARERS		= 0x000040,
+	DECT_EHLC_EMERGENCY_CALLS		= 0x000080,
+	DECT_EHLC_TPUI_LOCATION_REGISTRATION	= 0x000100,
+	DECT_EHLC_GPS_SYNCHRONIZED		= 0x000200,
+	DECT_EHLC_ISDN_INTERMEDIATE_SYSTEM	= 0x000400,
+	DECT_EHLC_RAP_PART_1_PROFILE		= 0x000800,
+	DECT_EHLC_V_24				= 0x004000,
+	DECT_EHLC_PPP				= 0x008000,
+	DECT_EHLC_IP				= 0x010000,
+	DECT_EHLC_TOKEN_RING			= 0x020000,
+	DECT_EHLC_ETHERNET			= 0x040000,
+	DECT_EHLC_IP_ROAMING			= 0x080000,
+	DECT_EHLC_GENERIC_MEDIA_ENCAPSULATION	= 0x100000,
+	DECT_EHLC_BASIC_ODAP			= 0x200000,
+	DECT_EHLC_F_MMS_INTERWORKING_PROFILE	= 0x400000,
+};
+
+enum dect_extended_fixed_part_capabilities2 {
+	DECT_EFPC2_LONG_SLOT_J640		= 0x800,
+	DECT_EFPC2_LONG_SLOT_J672		= 0x400,
+	DECT_EFPC2_IP_F				= 0x200,
+	DECT_EFPC2_SI_PF			= 0x100,
+	DECT_EFPC2_GF				= 0x080,
+	DECT_EFPC2_NO_EMISSION_CARRIER		= 0x001,
+};
+
+enum dect_extended_higher_layer_capabilities2 {
+	DECT_EHLC2_NG_DECT_PERMANENT_CLIR	= 0x000100,
+	DECT_EHLC2_NG_DECT_MULTIPLE_CALLS	= 0x000200,
+	DECT_EHLC2_NG_DECT_MULTIPLE_LINES	= 0x000400,
+	DECT_EHLC2_EASY_PAIRING			= 0x000800,
+	DECT_EHLC2_LIST_ACCESS_FEATURES		= 0x001000,
+	DECT_EHLC2_NO_EMISSION_MODE		= 0x002000,
+	DECT_EHLC2_NG_DECT_CALL_DEFLECTION	= 0x004000,
+	DECT_EHLC2_NG_DECT_INTRUSION_CALL	= 0x008000,
+	DECT_EHLC2_NG_DECT_CONFERENCE_CALL	= 0x010000,
+	DECT_EHLC2_NG_DECT_PARALLEL_CALLS	= 0x020000,
+	DECT_EHLC2_NG_DECT_CALL_TRANSFER	= 0x040000,
+	DECT_EHLC2_NG_DECT_EXTENDED_WIDEBAND	= 0x080000,
+	DECT_EHLC2_PACKET_DATA_CATEGORY_MASK	= 0x700000,
+	DECT_EHLC2_NG_DECT_WIDEBAND		= 0x800000,
+};
+
+enum dect_mac_info_attrs {
+	DECTA_MAC_INFO_UNSPEC,
+	DECTA_MAC_INFO_PARI,
+	DECTA_MAC_INFO_RPN,
+	DECTA_MAC_INFO_RSSI,
+	DECTA_MAC_INFO_SARI_LIST,
+	DECTA_MAC_INFO_FPC,
+	DECTA_MAC_INFO_HLC,
+	DECTA_MAC_INFO_EFPC,
+	DECTA_MAC_INFO_EHLC,
+	DECTA_MAC_INFO_EFPC2,
+	DECTA_MAC_INFO_EHLC2,
+	DECTA_MAC_INFO_MFN,
+	__DECTA_MAC_INFO_MAX
+};
+#define DECTA_MAC_INFO_MAX	(__DECTA_MAC_INFO_MAX - 1)
+
+enum dect_llme_ops {
+	DECT_LLME_REQUEST,
+	DECT_LLME_INDICATE,
+	DECT_LLME_RESPONSE,
+	DECT_LLME_CONFIRM,
+};
+
+enum dect_llme_msg_types {
+	DECT_LLME_SCAN,
+	DECT_LLME_MAC_INFO,
+	DECT_LLME_MAC_RFP_PRELOAD,
+	__DECT_LLME_MAX
+};
+#define DECT_LLME_MAX		(__DECT_LLME_MAX - 1)
+
+enum dect_llme_msg_attrs {
+	DECTA_LLME_UNSPEC,
+	DECTA_LLME_OP,
+	DECTA_LLME_TYPE,
+	DECTA_LLME_DATA,
+	__DECTA_LLME_MAX
+};
+#define DECTA_LLME_MAX		(__DECTA_LLME_MAX - 1)
+
+#endif /* _LINUX_DECT_NETLINK_H */
diff --git a/target/linux/generic/files/include/net/dect/ccp.h b/target/linux/generic/files/include/net/dect/ccp.h
new file mode 100644
index 0000000..5234c7d
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/ccp.h
@@ -0,0 +1,110 @@
+/*
+ * DECT MAC Layer - Cell Control Protocol (CCP)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_CCP
+#define _NET_DECT_CCP
+
+#define DECT_CCP_TIPC_TYPE		TIPC_RESERVED_TYPES
+#define DECT_CCP_CELL_PORT		1000
+#define DECT_CCP_CLUSTER_PORT_BASE	1000
+
+enum dect_ccp_primitives {
+	/* CCF -> CSF */
+	DECT_CCP_SET_MODE,
+	DECT_CCP_SCAN,
+	DECT_CCP_ENABLE,
+	DECT_CCP_PRELOAD,
+	DECT_CCP_MAC_INFO_IND,
+	DECT_CCP_PAGE_REQ,
+	DECT_CCP_TBC_ESTABLISH_REQ,
+	DECT_CCP_TBC_ESTABLISH_RES,
+	DECT_CCP_TBC_DATA_REQ,
+	DECT_CCP_TBC_DIS_REQ,
+	DECT_CCP_TBC_ENC_KEY_REQ,
+	DECT_CCP_TBC_ENC_EKS_REQ,
+	/* CSF -> CCF */
+	DECT_CCP_TBC_ESTABLISH_IND,
+	DECT_CCP_TBC_ESTABLISH_CFM,
+	DECT_CCP_TBC_EVENT_IND,
+	DECT_CCP_TBC_DATA_IND,
+	DECT_CCP_TBC_DIS_IND,
+};
+
+struct dect_ccp_msg_hdr {
+	u8		primitive;
+} __attribute__((packed));
+
+struct dect_ccp_ari {
+	__be64		ari;
+};
+
+struct dect_ccp_mode_msg {
+	u8		mode;
+} __attribute__((packed));
+
+struct dect_ccp_scan_msg {
+	__be64		ari;
+	__be64		ari_mask;
+} __attribute__((packed));
+
+struct dect_ccp_sysinfo_msg {
+	__be64		pari;
+	__be64		sari[DECT_SARI_CYCLE_MAX];
+	__be64		fpc;
+	__be64		hlc;
+	__be64		efpc;
+	__be32		mfn;
+	u8		num_saris;
+	u8		rpn;
+} __attribute__((packed));
+
+struct dect_ccp_page_msg {
+	u8		fast_page;
+	u8		long_page;
+} __attribute__((packed));
+
+struct dect_ccp_tbc_msg {
+	__be32		tbei;
+	__be32		pmid;
+	__be64		ari;
+	u8		ecn;
+	u8		data;
+} __attribute__((packed));
+
+struct dect_ccp_enc_key_msg {
+	__be64		key;
+} __attribute__((packed));
+
+struct dect_ccp_data_msg {
+	u8		channel;
+	u8		data[];
+} __attribute__((packed));
+
+#ifdef CONFIG_DECT_CCP
+extern int dect_ccp_cluster_init(struct dect_cluster *cl);
+extern void dect_ccp_cluster_shutdown(struct dect_cluster *cl);
+
+extern struct dect_cluster_handle *dect_ccp_cell_init(struct dect_cell *cell,
+						      u8 clindex);
+#else
+static inline int dect_ccp_cluster_init(struct dect_cluster *cl)
+{
+	return 0;
+}
+
+static inline void dect_ccp_cluster_shutdown(struct dect_cluster *cl)
+{
+	return;
+}
+
+static inline struct dect_cluster_handle *
+dect_ccp_cell_init(struct dect_cell *cell, u8 clindex)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+#endif
+
+#endif /* _NET_DECT_CPP */
diff --git a/target/linux/generic/files/include/net/dect/dect.h b/target/linux/generic/files/include/net/dect/dect.h
new file mode 100644
index 0000000..e253308
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/dect.h
@@ -0,0 +1,319 @@
+#ifndef _NET_DECT_DECT_H
+#define _NET_DECT_DECT_H
+
+#define DECT_FRAMES_PER_MULTIFRAME	16
+
+static inline u8 dect_next_framenum(u8 framenum)
+{
+	if (++framenum == DECT_FRAMES_PER_MULTIFRAME)
+		framenum = 0;
+	return framenum;
+}
+
+static inline u8 dect_framenum_add(u8 f1, u8 f2)
+{
+	return (f1 + f2) % DECT_FRAMES_PER_MULTIFRAME;
+}
+
+#define DECT_MULTIFRAME_MASK		0x00ffffff
+
+static inline u32 dect_next_mfn(u32 mfn)
+{
+	if (++mfn == (1 << 24) - 1)
+		mfn = 0;
+	return mfn;
+}
+
+static inline u32 dect_mfn_add(u32 mfn1, u32 mfn2)
+{
+	return (mfn1 + mfn2) & DECT_MULTIFRAME_MASK;
+}
+
+/* Compare multiframe numbers, considering overflows */
+static inline bool dect_mfn_before(u32 mfn1, u32 mfn2)
+{
+	return (s32)((mfn2 << 8) - (mfn1 << 8)) > 0;
+}
+
+static inline bool dect_mfn_after(u32 mfn1, u32 mfn2)
+{
+	return dect_mfn_before(mfn2, mfn1);
+}
+
+#include <linux/list.h>
+
+/**
+ * enum dect_timer_bases - timer bases for DECT timers
+ *
+ * @DECT_TIMER_RX:	receive time base
+ * @DECT_TIMER_TX:	send time base
+ */
+enum dect_timer_bases {
+	DECT_TIMER_RX,
+	DECT_TIMER_TX,
+	__DECT_TIMER_BASE_MAX
+};
+#define DECT_TIMER_BASE_MAX	(__DECT_TIMER_BASE_MAX - 1)
+
+/**
+ * struct dect_timer_base - timer base
+ *
+ * @timers:		list of active timers
+ * @slot:		slot position
+ * @framenum:		frame number
+ * @mfn:		multiframe number
+ */
+struct dect_timer_base {
+	struct list_head	timers;
+	u8			base;
+	u8			slot;
+	u8			framenum;
+	u32			mfn;
+};
+
+static inline void dect_timer_base_init(struct dect_timer_base base[],
+					enum dect_timer_bases b)
+{
+	INIT_LIST_HEAD(&base[b].timers);
+	base->base = b;
+}
+
+static inline u8 __dect_slotnum(const struct dect_timer_base *base)
+{
+	return base->slot;
+}
+
+static inline u8 __dect_framenum(const struct dect_timer_base *base)
+{
+	return base->framenum;
+}
+
+static inline u32 __dect_mfn(const struct dect_timer_base *base)
+{
+	return base->mfn;
+}
+
+extern void __dect_run_timers(const char *name, struct dect_timer_base *base);
+
+/**
+ * struct dect_timer - DECT TDMA frame timer
+ *
+ * @list:		timer list node
+ * @base:		timer base
+ * @mfn:		expiration time: multiframe number
+ * @frame:		expiration time: frame number
+ * @slot:		expiration time: slot number
+ * @func:		timer function
+ * @data:		timer data
+ */
+struct dect_cell;
+struct dect_cluster;
+
+struct dect_timer {
+	struct list_head		list;
+
+	enum dect_timer_bases		base;
+	u32				mfn;
+	u8				frame;
+	u8				slot;
+
+	union {
+		void			(*cell)(struct dect_cell *, void *);
+		void			(*cluster)(struct dect_cluster *, void *);
+		void			(*cb)(void *, void *);
+	} cb;
+	union {
+		struct dect_cell	*cell;
+		struct dect_cluster	*cluster;
+		void			*obj;
+	};
+	void				*data;
+};
+
+static inline void dect_timer_init(struct dect_timer *timer)
+{
+	INIT_LIST_HEAD(&timer->list);
+}
+
+static inline void dect_timer_del(struct dect_timer *timer)
+{
+	list_del_init(&timer->list);
+}
+
+extern void __dect_timer_add(const char *name, struct dect_timer_base *base,
+			     struct dect_timer *timer, u32 frame, u8 slot);
+
+#include <linux/dect.h>
+#include <net/dect/identities.h>
+#include <net/dect/mac_ccf.h>
+#include <net/dect/dlc.h>
+
+extern void __acquires(dect_cfg_mutex) dect_lock(void);
+extern void __releases(dect_cfg_mutex) dect_unlock(void);
+
+/**
+ * struct dect_cluster - DECT cluster of up to 8/256 cells
+ *
+ * @list:		device list node
+ * @name:		device identifier
+ * @index:		unique numeric cluster identifier
+ * @mode:		device mode (FP/PP/monitor)
+ * @pari:		primary access rights identifier
+ * @si:			system information
+ * @bmc:		Broadcast Message Control
+ * @cmc:		Connectionless Message Control
+ * @mbcs:		Multi-Bearer Controllers
+ * @cells:		DECT cells
+ */
+struct dect_cluster {
+	struct list_head		list;
+	char				name[DECTNAMSIZ];
+	int				index;
+
+	u32				tipc_id;
+	u32				tipc_portref;
+	struct dect_cluster_handle	handle;
+
+	enum dect_cluster_modes		mode;
+
+	spinlock_t			lock;
+
+	struct dect_ari			pari;
+	struct dect_si			si;
+	u8				rpn;
+
+	u32				pmid;
+
+	struct list_head		cells;
+	struct dect_bmc			bmc;
+	struct dect_cmc			cmc;
+	struct list_head		mbcs;
+
+	u32				mcei_rover;
+	struct list_head		mac_connections;
+
+	struct dect_timer_base		timer_base[DECT_TIMER_BASE_MAX + 1];
+};
+
+extern struct list_head dect_cluster_list;
+extern struct dect_cluster *dect_cluster_get_by_index(int index);
+
+struct dect_netlink_handler {
+	int (*doit)(const struct sk_buff *, const struct nlmsghdr *,
+		    const struct nlattr *[]);
+	int (*dump)(struct sk_buff *, struct netlink_callback *);
+	int (*done)(struct netlink_callback *);
+	const struct nla_policy *policy;
+	unsigned int maxtype;
+};
+
+extern void dect_netlink_register_handlers(const struct dect_netlink_handler *handler,
+					   unsigned int base, unsigned int n);
+extern void dect_netlink_unregister_handlers(unsigned int base, unsigned int n);
+
+extern struct sock *dect_nlsk;
+
+/**
+ * struct dect_llme_req - LLME netlink request
+ *
+ * @nlh:		netlink header
+ * @nlpid:		netlink socket PID
+ */
+struct dect_llme_req {
+	struct nlmsghdr		nlh;
+	u32			nlpid;
+};
+
+#include <net/sock.h>
+
+extern const struct proto_ops dect_stream_ops;
+extern const struct proto_ops dect_dgram_ops;
+
+struct dect_proto {
+	unsigned int		type;
+	unsigned int		protocol;
+	int			capability;
+	const struct proto_ops	*ops;
+	int			(*getname)(struct sock *sk,
+					   struct sockaddr *uaddr, int *len,
+					   int peer);
+	struct proto		proto;
+};
+
+#include <net/tcp_states.h>
+
+enum {
+	DECT_SK_ESTABLISHED		= TCP_ESTABLISHED,
+	DECT_SK_ESTABLISH_PENDING	= TCP_SYN_SENT,
+	DECT_SK_RELEASED		= TCP_CLOSE,
+	DECT_SK_RELEASE_PENDING		= TCP_CLOSING,
+	DECT_SK_LISTEN			= TCP_LISTEN,
+};
+
+struct dect_csk {
+	struct sock		sk;
+	struct hlist_head	accept_queue;
+};
+
+static inline struct dect_csk *dect_csk(const struct sock *sk)
+{
+	return (struct dect_csk *)sk;
+}
+
+extern int dect_proto_register(struct dect_proto *proto);
+extern void dect_proto_unregister(struct dect_proto *proto);
+
+struct dect_skb_sk_cb {
+	//struct dect_skb_trx_cb	cb;
+	int			index;
+};
+
+#define DECT_SK_CB(skb)		((struct dect_skb_sk_cb *)(skb)->cb)
+
+static inline int dect_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	/*
+	 * Release the transceiver reference, it is only valid in IRQ and
+	 * softirq context.
+	 */
+	//FIXME
+	//DECT_SK_CB(skb)->index = DECT_CB(skb)->trx->dev->index;
+	return sock_queue_rcv_skb(sk, skb);
+}
+
+struct dect_notification {
+	u32		type;
+};
+
+#define DECT_NOTIFY_CB(skb)	((struct dect_notification *)(skb)->cb)
+
+extern struct sk_buff *dect_alloc_notification(u32 type, const void *data,
+					       unsigned int size);
+
+extern void (*dect_raw_rcv_hook)(struct sk_buff *skb);
+static inline void dect_raw_rcv(struct sk_buff *skb)
+{
+	typeof(dect_raw_rcv_hook) dect_raw_rcv;
+
+	rcu_read_lock();
+	dect_raw_rcv = dect_raw_rcv_hook;
+	if (dect_raw_rcv != NULL)
+		dect_raw_rcv(skb);
+	rcu_read_unlock();
+}
+
+extern int dect_af_module_init(void);
+extern void dect_af_module_exit(void);
+
+extern int dect_bsap_module_init(void);
+extern void dect_bsap_module_exit(void);
+extern int dect_ssap_module_init(void);
+extern void dect_ssap_module_exit(void);
+
+extern int dect_netlink_module_init(void);
+extern void dect_netlink_module_exit(void);
+
+extern struct sk_buff *skb_append_frag(struct sk_buff *head, struct sk_buff *skb);
+extern unsigned int skb_queue_pull(struct sk_buff_head *list, unsigned int len);
+
+#endif /* _NET_DECT_DECT_H */
diff --git a/target/linux/generic/files/include/net/dect/dlc.h b/target/linux/generic/files/include/net/dect/dlc.h
new file mode 100644
index 0000000..f38f4d4
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/dlc.h
@@ -0,0 +1,462 @@
+/*
+ * DECT DLC Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_DLC_H
+#define _NET_DECT_DLC_H
+
+#include <linux/timer.h>
+
+/*
+ * C-Plane data link service
+ */
+
+/*
+ * FA-Frame
+ */
+
+#define DECT_FA_HDR_SIZE	3
+
+struct dect_fa_hdr {
+	u8	addr;
+	u8	ctrl;
+	u8	li;
+};
+
+/*
+ * Address field
+ */
+
+#define DECT_FA_ADDR_OFF	0
+
+/* New link flag */
+#define DECT_FA_ADDR_NLF_FLAG	0x80
+
+/* Logical Link Number */
+#define DECT_FA_ADDR_LLN_MASK	0x70
+#define DECT_FA_ADDR_LLN_SHIFT	4
+
+/* Service Access Point Identifier */
+#define DECT_FA_ADDR_SAPI_MASK	0x0c
+#define DECT_FA_ADDR_SAPI_SHIFT	2
+
+/* Command/Response flag */
+#define DECT_FA_ADDR_CR_FLAG	0x02
+
+/* Reserved bit */
+#define DECT_FA_ADDR_RES_BIT	0x01
+
+/*
+ * Control field
+ */
+
+#define DECT_FA_CTRL_OFF	1
+
+/*
+ * I-Format: numbered information
+ */
+
+#define DECT_FA_CTRL_I_FMT_MASK	0x01
+#define DECT_FA_CTRL_I_FMT_ID	0x00
+
+/* Receive sequence number */
+#define DECT_FA_CTRL_I_NR_MASK	0xe0
+#define DECT_FA_CTRL_I_NR_SHIFT	5
+
+/* Poll bit */
+#define DECT_FA_CTRL_I_P_FLAG	0x10
+
+/* Send sequence number */
+#define DECT_FA_CTRL_I_NS_MASK	0x0e
+#define DECT_FA_CTRL_I_NS_SHIFT	1
+
+/* Command */
+#define DECT_FA_CTRL_I_CMD_I	(0x0)
+
+/*
+ * S-Format: supervisory functions
+ */
+
+#define DECT_FA_CTRL_S_FMT_MASK	0x03
+#define DECT_FA_CTRL_S_FMT_ID	0x01
+
+/* Receive sequence number */
+#define DECT_FA_CTRL_S_NR_MASK	0xe0
+#define DECT_FA_CTRL_S_NR_SHIFT	5
+
+/* Poll/final bit */
+#define DECT_FA_CTRL_S_PF_FLAG	0x10
+
+/* Command/Response */
+#define DECT_FA_CTRL_S_CR_MASK	0x0c
+
+#define DECT_FA_CTRL_S_CR_RR	0x00
+#define DECT_FA_CTRL_S_CR_RNR	0x40
+#define DECT_FA_CTRL_S_CR_REJ	0x80
+
+/*
+ *  U-Format: unnumbered information
+ */
+
+#define DECT_FA_CTRL_U_FMT_MASK	0x03
+#define DECT_FA_CTRL_U_FMT_ID	0x03
+
+/* Unnumbered function bits */
+#define DECT_FA_CTRL_U_U1_MASK	0xec
+
+/* Poll/final bit */
+#define DECT_FA_CTRL_U_PF_FLAG	0x10
+
+/* Command/Response */
+#define DECT_FA_CTRL_U_CR_MASK	0xef
+
+#define DECT_FA_CTRL_U_CR_SABM	0x2c
+#define DECT_FA_CTRL_U_CR_DM	0x0c
+#define DECT_FA_CTRL_U_CR_UI	0x00
+#define DECT_FA_CTRL_U_CR_DISC	0x40
+#define DECT_FA_CTRL_U_CR_UA	0x60
+
+/*
+ * Length Indicator
+ */
+
+#define DECT_FA_LI_OFF		2
+
+/* Length (octets) */
+#define DECT_FA_LI_LENGTH_MASK	0xfc
+#define DECT_FA_LI_LENGTH_SHIFT	2
+
+/* More data flag */
+#define DECT_FA_LI_M_FLAG	0x02
+
+/* Extended length indicator bit */
+#define DECT_FA_LI_EXT_FLAG	0x01
+
+/* maximum length value */
+#define DECT_FA_LI_MAX		63
+
+/*
+ * Extended Length indicator
+ */
+
+#define DECT_FA_ELI_OFF		3
+
+/* Length (octets) */
+#define DECT_FA_ELI_LENGTH_MASK	0xfc
+#define DECT_FA_ELI_LENGTH_SHIFT 2
+
+struct dect_fa_len {
+	u8		len;
+	bool		more;
+};
+
+/*
+ * Fill Field
+ */
+
+#define DECT_FA_FILL_PATTERN	0xf0
+
+/*
+ * Checksum field
+ */
+
+#define DECT_FA_CSUM_SIZE	2
+
+/*
+ * Information field
+ */
+
+#define DECT_FA_I_MAX		(DECT_FA_LI_MAX - DECT_FA_HDR_SIZE - DECT_FA_CSUM_SIZE)
+
+
+/**
+ * struct dect_dli - DECT Data Link Identifier (DLI)
+ *
+ * @lln:	Logical Link Number
+ * @mci:	Mac Connection Identifier
+ */
+struct dect_dli {
+	enum dect_llns	lln;
+	struct dect_mci	mci;
+};
+
+/**
+ * @DECT_LAPC_ULI:	unassigned link identifier state (class U/A)
+ * @DECT_LAPC_ALI:	assigned link identifier state (class B established)
+ * @DECT_LAPC_ASM:	assigned Link Identifier/multiple frame state (class B suspended)
+ */
+enum dect_lapc_states {
+	DECT_LAPC_ULI,
+	DECT_LAPC_ALI,
+	DECT_LAPC_ASM,
+};
+
+/**
+ * struct dect_lapc - DECT LAPC entity
+ *
+ * @lc:			Associated Lc entity
+ * @dli:		Data Link Identifier
+ * @sapi:		Service Access Point Identifier
+ * @cmd:		CR bit setting for commands (PT: 1, FT: 0)
+ * @nlf:		New link flag
+ * @v_s:		Send state Variable V(S): sequence number of next I-frame
+ * @v_a:		Acknowledge state Variable V(A): last I-frame that has been acknowledged
+ * @v_r:		Receive state Variable V(R): next expected sequence number
+ * busy:		LAPC is in receiver busy condition
+ * @peer_busy:		Peer is in receiver busy condition
+ * @window:		maximum number of oustanding unacknowledged I-frames
+ * @mod:		modulus for sequence number calculations
+ * @retransmit_cnt:	Retransmission counter
+ * @retransmit_queue:	Retransmission queue
+ * @timer:		Retransmission timer (DL.04)
+ */
+struct dect_lapc {
+	struct sock		*sk;
+	struct dect_lc		*lc;
+	struct dect_dli		dli;
+	enum dect_sapis		sapi;
+
+	bool			cmd;
+
+	enum dect_lapc_states	state;
+	bool			nlf;
+	u8			v_s;
+	u8			v_a;
+	u8			v_r;
+
+	bool			busy;
+	bool			peer_busy;
+
+	u8			window;
+	u8			mod;
+
+	u8			retransmit_cnt;
+	struct sk_buff_head	retransmit_queue;
+	struct timer_list	timer;
+
+	struct sk_buff		*rcv_head;
+};
+
+/* class A window size and sequence number modulus */
+#define DECT_LAPC_CLASS_A_WINDOW		1
+#define DECT_LAPC_CLASS_A_MOD			2
+
+/* class B window size and sequence number modulus */
+#define DECT_LAPC_CLASS_B_INITIAL_WINDOW	1
+#define DECT_LAPC_CLASS_B_WINDOW		3
+#define DECT_LAPC_CLASS_B_MOD			8
+
+/* maximum number of retransmissions */
+#define DECT_LAPC_RETRANSMIT_MAX		3
+
+/* various timer parameters specified in Annex A */
+#define DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT	(2 * HZ)
+#define DECT_LAPC_CLASS_B_ESTABLISH_TIMEOUT	(2 * HZ)
+#define DECT_LAPC_RETRANSMISSION_TIMEOUT	(1 * HZ)
+#define DECT_LAPC_LINK_RELEASE_TIMEOUT		(2 * HZ)
+#define DECT_LAPC_LINK_SUSPEND_TIMEOUT		(2 * HZ)
+#define DECT_LAPC_LINK_RESUME_TIMEOUT		(2 * HZ)
+#define DECT_LAPC_CONNECTION_HANDOVER_TIMEOUT	(10 * HZ)
+#define DECT_LAPC_CONNECTION_HANDOVER_INTERVAL	(4 * HZ)
+
+extern struct dect_lapc *dect_lapc_init(struct sock *sk, const struct dect_dli *dli,
+					enum dect_sapis sapi, struct dect_lc *lc,
+					gfp_t gfp);
+extern void dect_lapc_destroy(struct dect_lapc *lapc);
+
+extern int dect_lapc_establish(struct dect_lapc *lapc);
+extern void dect_lapc_release(struct dect_lapc *lapc, bool normal);
+extern int dect_lapc_transmit(struct dect_lapc *lapc);
+
+extern struct dect_lapc *dect_ssap_rcv_request(struct dect_lc *lc,
+					       const struct dect_dli *dli,
+					       enum dect_sapis sapi);
+
+/**
+ * struct dect_lc - DECT Lc entity
+ *
+ * @mc:		MAC connection
+ * @lsig:	link signature for checksumming (lower 16 bits of PMID or 0)
+ * @rx_head:	reassembly queue head
+ * @rx_len:	target length of current reassembly buffer
+ * @txq:	transmit queue
+ * @tx_head:	current TX LAPC frame
+ * @tx_len:	TX target fragment length
+ * @use:	usage count
+ * @lapcs:	LAPC entities associated with the Lc
+ * @e_lapc:	LAPC performing establishment procedures
+ *
+ * The Lc entity is responsible for framing, logical channel selection and
+ * fragmenting of LAPC PDUs. There is one Lc entity per MAC connection.
+ */
+struct dect_lc {
+	struct dect_mac_conn	*mc;
+	u16			lsig;
+
+	struct sk_buff		*rx_head;
+	u8			rx_len;
+
+	struct sk_buff_head	txq;
+	struct sk_buff		*tx_head;
+	u8			tx_len;
+
+	u8			use;
+	struct dect_lapc	*lapcs[DECT_LLN_UNASSIGNED + 1];
+	struct dect_lapc	*elapc;
+};
+
+#define DECT_LC_LSIG_MASK	0xffff
+
+extern struct dect_lc *dect_lc_init(struct dect_mac_conn *mc, gfp_t gfp);
+extern void dect_lc_destroy(struct dect_lc *lc);
+
+extern void dect_lc_bind(struct dect_lc *lc, struct dect_lapc *lapc);
+extern void dect_lc_unbind(struct dect_lc *lc, struct dect_lapc *lapc);
+
+/**
+ * struct dect_lb - DECT Lb entity (C-plane broadcast service)
+ *
+ *
+ */
+struct dect_lb {
+};
+
+#define DECT_LB_SHORT_FRAME_SIZE	3
+#define DECT_LB_LONG_FRAME_SIZE		5
+#define DECT_LB_EXTENDED_FRAME_SIZE_MAX	(6 * DECT_LB_LONG_FRAME_SIZE)
+
+#include <net/sock.h>
+
+/**
+ * struct dect_dlc_fbx_ops - DLC U-plane lower (FBx) entity ops
+ *
+ */
+struct dect_fbx;
+struct dect_fbx_ops {
+	struct sk_buff			*(*dequeue)(struct dect_fbx *fbx);
+	void				(*enqueue)(struct dect_fbx *fbx,
+						   struct sk_buff *skb);
+};
+
+struct dect_fbx {
+	const struct dect_fbx_ops	*ops;
+};
+
+extern const struct dect_fbx_ops dect_fbn_ops;
+
+struct dect_lux;
+struct dect_lux_ops {
+	struct sk_buff			*(*dequeue)(struct dect_lux *lux);
+	void				(*enqueue)(struct dect_lux *lux,
+						   struct sk_buff *skb);
+	void				(*disconnect)(struct dect_lux *lux);
+};
+
+/**
+ * struct dect_lux - DLC U-plane upper (LUx) entity
+ *
+ * @fpx:	FBx entity
+ */
+struct dect_lux {
+	const struct dect_lux_ops	*ops;
+	struct dect_fbx			fbx;
+};
+
+/**
+ * dect_mac_connection_states - DECT MAC connection states as viewed by the DLC
+ *
+ * @DECT_MAC_CONN_CLOSED:
+ * @DECT_MAC_CONN_OPEN_PENDING:
+ * @DECT_MAC_CONN_OPEN:
+ */
+enum dect_mac_conn_states {
+	DECT_MAC_CONN_CLOSED,
+	DECT_MAC_CONN_OPEN_PENDING,
+	DECT_MAC_CONN_OPEN,
+};
+
+/**
+ * struct dect_mac_conn - DECT MAC connection as viewed by the DLC
+ *
+ * @list:	Cluster connection list node
+ * @cl:		Cluster
+ * @mcei:	MAC Connection Endpoint Identification
+ * @mci:	MAC Connection Identifier (BMCI or AMCI)
+ * @state:	Connection state
+ * @service:	Service offered by the connection
+ * @ck:		cipher key
+ */
+struct dect_mac_conn {
+	struct list_head		list;
+	struct dect_cluster		*cl;
+
+	u32				mcei;
+	struct dect_mci			mci;
+	enum dect_mac_conn_states	state;
+	enum dect_mac_service_types	service;
+	u64				ck;
+
+	u8				use;
+	struct dect_lc			*lc;
+	struct dect_fbx			*fbx;
+};
+
+extern struct dect_mac_conn *dect_mac_conn_init(struct dect_cluster *cl,
+						const struct dect_mci *mci,
+						const struct dect_mbc_id *id);
+extern void dect_dlc_mac_conn_destroy(struct dect_mac_conn *mc);
+extern struct dect_mac_conn *dect_mac_conn_get_by_mci(const struct dect_cluster *cl,
+						      const struct dect_mci *mci);
+
+extern void dect_dlc_mac_conn_bind(struct dect_mac_conn *mc);
+extern void dect_dlc_mac_conn_unbind(struct dect_mac_conn *mc);
+extern int dect_dlc_mac_conn_establish(struct dect_mac_conn *mc);
+
+extern int dect_mac_con_cfm(struct dect_cluster *cl, u32 mcei,
+			    enum dect_mac_service_types service);
+extern int dect_mac_con_ind(struct dect_cluster *cl,
+			    const struct dect_mbc_id *id,
+			    enum dect_mac_service_types service);
+
+extern int dect_dlc_mac_conn_enc_key_req(struct dect_mac_conn *mc, u64 key);
+extern int dect_dlc_mac_conn_enc_eks_req(struct dect_mac_conn *mc,
+					 enum dect_cipher_states status);
+extern void dect_mac_enc_eks_cfm(struct dect_cluster *cl, u32 mcei,
+				 enum dect_cipher_states status);
+extern void dect_mac_enc_eks_ind(struct dect_cluster *cl, u32 mcei,
+				 enum dect_cipher_states status);
+
+extern void dect_dlc_mac_dis_req(struct dect_mac_conn *mc);
+extern int dect_mac_dis_ind(struct dect_cluster *cl, u32 mcei,
+			    enum dect_release_reasons reason);
+
+extern void dect_cplane_notify_state_change(struct dect_mac_conn *mc);
+extern void dect_cplane_mac_dis_ind(const struct dect_mac_conn *mc,
+				    enum dect_release_reasons reason);
+extern void dect_cplane_mac_enc_eks_ind(const struct dect_mac_conn *mc,
+					enum dect_cipher_states status);
+
+extern void dect_cplane_rcv(struct dect_mac_conn *mc,
+			    enum dect_data_channels chan,
+			    struct sk_buff *skb);
+extern struct sk_buff *dect_cplane_dtr(struct dect_mac_conn *mc,
+				       enum dect_data_channels chan);
+
+extern void dect_uplane_rcv(struct dect_mac_conn *mc,
+			    enum dect_data_channels chan,
+			    struct sk_buff *skb);
+extern struct sk_buff *dect_uplane_dtr(struct dect_mac_conn *mc,
+				       enum dect_data_channels chan);
+
+extern void dect_mac_co_data_ind(struct dect_cluster *cl, u32 mcei,
+				 enum dect_data_channels chan,
+				 struct sk_buff *skb);
+extern struct sk_buff *dect_mac_co_dtr_ind(struct dect_cluster *cl, u32 mcei,
+					   enum dect_data_channels chan);
+
+extern void dect_bsap_rcv(const struct dect_cluster *cl, struct sk_buff *skb);
+extern void dect_mac_page_ind(struct dect_cluster *cl, struct sk_buff *skb);
+
+#endif /* _NET_DECT_DLC_H */
diff --git a/target/linux/generic/files/include/net/dect/dsc.h b/target/linux/generic/files/include/net/dect/dsc.h
new file mode 100644
index 0000000..423a646
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/dsc.h
@@ -0,0 +1,12 @@
+#ifndef _NET_DECT_DSC_H
+#define _NET_DECT_DSC_H
+
+static inline __le64 dect_dsc_iv(u32 mfn, u8 framenum)
+{
+	return cpu_to_le64((mfn << 4) + framenum);
+}
+
+extern void dect_dsc_keystream(uint64_t iv, const uint8_t *key,
+			       uint8_t *output, unsigned int len);
+
+#endif /* _NET_DECT_DSC_H */
diff --git a/target/linux/generic/files/include/net/dect/identities.h b/target/linux/generic/files/include/net/dect/identities.h
new file mode 100644
index 0000000..a924d35
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/identities.h
@@ -0,0 +1,194 @@
+#ifndef _NET_DECT_IDENTITIES_H
+#define _NET_DECT_IDENTITIES_H
+
+/*
+ * Acess Rights Identity (ARI)
+ */
+
+#define DECT_ARI_ARC_MASK	0xe000000000000000ULL
+#define DECT_ARI_ARC_SHIFT	61
+
+/* Class A */
+#define DECT_ARI_A_EMC_MASK	0x1fffe00000000000ULL
+#define DECT_ARI_A_EMC_SHIFT	45
+
+#define DECT_ARI_A_FPN_MASK	0x00001ffff0000000ULL
+#define DECT_ARI_A_FPN_SHIFT	28
+
+/* Class B */
+#define DECT_ARI_B_EIC_MASK	0x1fffe00000000000ULL
+#define DECT_ARI_B_EIC_SHIFT	45
+
+#define DECT_ARI_B_FPN_MASK	0x00001fe000000000ULL
+#define DECT_ARI_B_FPN_SHIFT	37
+
+#define DECT_ARI_B_FPS_MASK	0x0000001e00000000ULL
+#define DECT_ARI_B_FPS_SHIFT	33
+
+/* Class C */
+#define DECT_ARI_C_POC_MASK	0x1fffe00000000000ULL
+#define DECT_ARI_C_POC_SHIFT	45
+
+#define DECT_ARI_C_FPN_MASK	0x00001fe000000000ULL
+#define DECT_ARI_C_FPN_SHIFT	37
+
+#define DECT_ARI_C_FPS_MASK	0x0000001e00000000ULL
+#define DECT_ARI_C_FPS_SHIFT	33
+
+/* Class D */
+#define DECT_ARI_D_GOP_MASK	0x1ffffe0000000000ULL
+#define DECT_ARI_D_GOP_SHIFT	41
+
+#define DECT_ARI_D_FPN_MASK	0x000001fe00000000ULL
+#define DECT_ARI_D_FPN_SHIFT	33
+
+/* Class E */
+#define DECT_ARI_E_FIL_MASK	0x1fffe00000000000ULL
+#define DECT_ARI_E_FIL_SHIFT	45
+
+#define DECT_ARI_E_FPN_MASK	0x00001ffe00000000ULL
+#define DECT_ARI_E_FPN_SHIFT	33
+
+#include <linux/dect_netlink.h>
+
+struct dect_ari {
+	enum dect_ari_classes	arc;
+	u32			fpn;
+	u32			fps;
+	union {
+		u16		emc;
+		u16		eic;
+		u16		poc;
+		u32		gop;
+		u16		fil;
+	};
+};
+
+enum dect_ari_lengths {
+	DECT_ARC_A_LEN		= 36,
+	DECT_ARC_B_LEN		= 31,
+	DECT_ARC_C_LEN		= 31,
+	DECT_ARC_D_LEN		= 31,
+	DECT_ARC_E_LEN		= 31,
+};
+
+extern bool dect_ari_masked_cmp(const struct dect_ari *a1,
+				const struct dect_ari *a2,
+				const struct dect_ari *m);
+extern bool dect_ari_cmp(const struct dect_ari *a1, const struct dect_ari *a2);
+extern u8 dect_parse_ari(struct dect_ari *ari, u64 a);
+extern u64 dect_build_ari(const struct dect_ari *ari);
+
+/*
+ * RFPI
+ */
+
+#define DECT_RFPI_E_FLAG	0x0080000000000000ULL
+#define DECT_RFPI_ARI_SHIFT	9
+#define DECT_RFPI_RPN_SHIFT	16
+
+struct dect_idi;
+extern bool dect_rfpi_cmp(const struct dect_idi *i1, const struct dect_idi *i2);
+extern u64 dect_build_rfpi(const struct dect_idi *idi);
+
+/*
+ * FMID (Fixed MAC Identifier)
+ */
+
+#define DECT_FMID_MASK		0x0fff
+#define DECT_FMID_SIZE		12
+
+extern u16 dect_build_fmid(const struct dect_idi *idi);
+
+/*
+ * PMID (Portable MAC Identifier)
+ */
+
+#define DECT_PMID_MASK			0x000fffff
+#define DECT_PMID_SIZE			20
+
+#define DECT_PMID_DEFAULT_ID_MASK	0x000f0000
+#define DECT_PMID_DEFAULT_ID		0x000e0000
+#define DECT_PMID_DEFAULT_NUM_MASK	0x0000ffff
+
+#define DECT_PMID_EMERGENCY_ID_MASK	0x000ff000
+#define DECT_PMID_EMERGENCY_ID		0x000f1000
+#define DECT_PMID_EMERGENCY_TPUI_MASK	0x00000fff
+
+#define DECT_PMID_ASSIGNED_TPUI_MASK	0x000fffff
+
+/**
+ * @DECT_PMID_DEFAULT:		1110 + arbitrary number (16 bits)
+ * @DECT_PMID_ASSIGNED:		Assigned individual TPUI
+ * @DECT_PMID_EMERGENCY:	1111 0001 + 12 bits of emergency TPUI
+ */
+enum dect_pmid_types {
+	DECT_PMID_DEFAULT,
+	DECT_PMID_ASSIGNED,
+	DECT_PMID_EMERGENCY,
+};
+
+struct dect_pmid {
+	enum dect_pmid_types	type;
+	union {
+		u32		tpui;
+		u32		num;
+	};
+};
+
+extern void dect_parse_pmid(struct dect_pmid *pmid, u32 p);
+extern u32 dect_build_pmid(const struct dect_pmid *pmid);
+extern bool dect_pmid_cmp(const struct dect_pmid *p1, const struct dect_pmid *p2);
+
+/*
+ * ECN (Exchanged Connection Number)
+ */
+
+#define DECT_ECN_MASK		0xf
+#define DECT_ECN_SIZE		4
+
+/*
+ * LCN (Logical Connection Number)
+ */
+
+#define DECT_LCN_MASK		0x7
+#define DECT_LCN_SIZE		3
+
+/**
+ * struct dect_mci - MAC connection identifier
+ *
+ * @ari:	DECT ARI
+ * @pmid:	Portable MAC Identity
+ * @lcn:	Logical Connection Number
+ */
+struct dect_mci {
+	struct dect_ari		ari;
+	struct dect_pmid	pmid;
+	u8			lcn;
+};
+
+extern int dect_parse_mci(struct dect_mci *mci, u64 m);
+extern u64 dect_build_mci(const struct dect_mci *mci);
+
+/*
+ * Data Link Identifier
+ */
+
+/**
+ * struct dect_dlei - DECT Data Link Endpoint Identifier (DLEI)
+ *
+ */
+struct dect_dlei {
+	struct dect_mci	mci;
+	enum dect_sapis	sapi;
+	enum dect_llns	lln;
+};
+
+/**
+ * struct dect_ulei - DECT U-Plane Link Endpoint Identifier
+ */
+struct dect_ulei {
+	struct dect_mci	mci;
+};
+
+#endif /* _NET_DECT_IDENTITIES_H */
diff --git a/target/linux/generic/files/include/net/dect/mac.h b/target/linux/generic/files/include/net/dect/mac.h
new file mode 100644
index 0000000..1f5c6e1
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/mac.h
@@ -0,0 +1,861 @@
+/*
+ * DECT MAC Layer - Header and global definitions
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_MAC_H
+#define _NET_DECT_MAC_H
+
+#include <net/dect/identities.h>
+
+/*
+ * A-Field
+ */
+
+#define DECT_A_FIELD_SIZE	8
+
+#define DECT_RA_FIELD_SIZE	2
+#define DECT_RA_FIELD_OFF	6
+
+/*
+ * Header field
+ */
+
+#define DECT_HDR_FIELD_SIZE	1
+#define DECT_HDR_FIELD_OFF	0
+
+#define DECT_HDR_TA_OFF		0
+#define DECT_HDR_TA_MASK	0xe0
+#define DECT_HDR_TA_SHIFT	5
+
+#define DECT_HDR_Q1_OFF		0
+#define DECT_HDR_Q1_FLAG	0x10
+
+#define DECT_HDR_BA_OFF		0
+#define DECT_HDR_BA_MASK	0x0e
+#define DECT_HDR_BA_SHIFT	1
+
+#define DECT_HDR_Q2_OFF		0
+#define DECT_HDR_Q2_FLAG	0x01
+
+/*
+ * T-Field
+ */
+
+#define DECT_T_FIELD_OFF	1
+#define DECT_T_FIELD_SIZE	5
+
+/**
+ * dect_tail_identification - MAC layer T-Field identification
+ *
+ * @DECT_TI_CT_PKT_0:		C_T data packet number 0
+ * @DECT_TI_CT_PKT_1:		C_T data packet number 1
+ * @DECT_TI_NT_CL:		Identities information on connectionless bearer
+ * @DECT_TI_NT:			Identities information
+ * @DECT_TI_QT:			Multiframe synchronisation und system information
+ * @DECT_TI_RESERVED:		Reserved
+ * @DECT_TI_MT:			MAC layer control
+ * @DECT_TI_PT:			Paging tail (RFP only)
+ * @DECT_TI_MT_PKT_0:		MAC layer control (first PP transmission, PP only)
+ */
+enum dect_tail_identifications {
+	DECT_TI_CT_PKT_0	= 0x0 << DECT_HDR_TA_SHIFT,
+	DECT_TI_CT_PKT_1	= 0x1 << DECT_HDR_TA_SHIFT,
+	DECT_TI_NT_CL		= 0x2 << DECT_HDR_TA_SHIFT,
+	DECT_TI_NT		= 0x3 << DECT_HDR_TA_SHIFT,
+	DECT_TI_QT		= 0x4 << DECT_HDR_TA_SHIFT,
+	DECT_TI_RESERVED	= 0x5 << DECT_HDR_TA_SHIFT,
+	DECT_TI_MT		= 0x6 << DECT_HDR_TA_SHIFT,
+	DECT_TI_PT		= 0x7 << DECT_HDR_TA_SHIFT,
+	DECT_TI_MT_PKT_0	= 0x7 << DECT_HDR_TA_SHIFT,
+};
+
+struct dect_skb_a_cb {
+	enum dect_tail_identifications	id;
+};
+
+#define DECT_A_CB(skb)		((struct dect_skb_a_cb *)(skb)->cb)
+
+/*
+ * Identities channel (N-channel)
+ */
+
+/* Identities information */
+#define DECT_NT_ID_RFPI_LEN	5
+
+/**
+ * @e:		indicates whether SARIs are available
+ * @pari:	primary access rights identifier
+ * @rpn:	radio part number
+ */
+struct dect_idi {
+	bool		e;
+	struct dect_ari	pari;
+	u8		rpn;
+};
+
+/*
+ * System information and multiframe marker (Q-channel)
+ */
+
+/* RFP Q-channel T-MUX rules: only frame 8 */
+#define DECT_Q_CHANNEL_FRAME	8
+
+/* System information header */
+#define DECT_QT_H_MASK		0x00f0000000000000ULL
+#define DECT_QT_H_SHIFT		52
+
+/**
+ * dect_system_information_types - codes for system information messages
+ *
+ * @DECT_QT_SI_SSI:		static system information
+ * @DECT_QT_SI_ERFC:		extended RF carriers
+ * @DECT_QT_SI_FPC:		fixed part capabilities
+ * @DECT_QT_SI_EFPC:		extended fixed part capabilities
+ * @DECT_QT_SI_SARI:		SARI list contents
+ * @DECT_QT_SI_MFN:		multi-frame number
+ * @DECT_QT_SI_ESC:		escape
+ * @DECT_QT_SI_ERFC2:		extended RF carriers part 2
+ * @DECT_QT_SI_TXI		transmit information
+ * @DECT_QT_SI_EFPC2:		extended fixed part capabilities part 2
+ */
+enum dect_mac_system_information_types {
+	DECT_QT_SI_SSI		= 0x0ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_SSI2		= 0x1ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_ERFC		= 0x2ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_FPC		= 0x3ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_EFPC		= 0x4ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_SARI		= 0x5ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_MFN		= 0x6ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_ESC		= 0x7ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_ERFC2	= 0x9ULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_TXI		= 0xbULL << DECT_QT_H_SHIFT,
+	DECT_QT_SI_EFPC2	= 0xcULL << DECT_QT_H_SHIFT,
+};
+
+/*
+ * Static system information - repeated every 8 multiframes
+ */
+
+#define DECT_QT_SSI_FREQ	8
+
+/* normal reverse */
+#define DECT_QT_SSI_NR_FLAG	0x0010000000000000ULL
+
+/* slot number */
+#define DECT_QT_SSI_SN_MASK	0x000f000000000000ULL
+#define DECT_QT_SSI_SN_SHIFT	48
+
+/* start position */
+#define DECT_QT_SSI_SP_MASK	0x0000c00000000000ULL
+#define DECT_QT_SSI_SP_SHIFT	46
+
+/* escape bit */
+#define DECT_QT_SSI_ESC_FLAG	0x0000200000000000ULL
+
+/* number of transceivers */
+#define DECT_QT_SSI_TXS_MASK	0x0000180000000000ULL
+#define DECT_QT_SSI_TXS_SHIFT	43
+
+/* extended RF carrier information available */
+#define DECT_QT_SSI_MC_FLAG	0x0000040000000000ULL
+
+/* RF carriers available */
+#define DECT_QT_SSI_RFCARS_MASK	0x000003ff00000000ULL
+#define DECT_QT_SSI_RFCARS_SHIFT 32
+
+/* carrier number */
+#define DECT_QT_SSI_CN_MASK	0x000000003f000000ULL
+#define DECT_QT_SSI_CN_SHIFT	24
+
+/* primary scan carrier number */
+#define DECT_QT_SSI_PSCN_MASK	0x00000000003f0000ULL
+#define DECT_QT_SSI_PSCN_SHIFT	16
+
+struct dect_ssi {
+	bool	nr;
+	bool	mc;
+	u16	rfcars;
+	u8	sn;
+	u8	sp;
+	u8	txs;
+	u8	cn;
+	u8	pscn;
+};
+
+/*
+ * Extended RF carrier information
+ */
+
+#define DECT_QT_ERFC_FREQ		8
+
+#define DECT_QT_ERFC_RFCARS_MASK 	0x000fffffe0000000ULL
+#define DECT_QT_ERFC_RFCARS_SHIFT	1
+
+#define DECT_QT_ERFC_RFBAND_MASK	0x000000001f000000ULL
+#define DECT_QT_ERFC_RFBAND_SHIFT	24
+
+#define DECT_QT_ERFC_ERFC2_FLAG		0x0000000000800000ULL
+
+#define DECT_QT_ERFC_NUM_RFCARS_MASK	0x00000000003f0000ULL
+#define DECT_QT_ERFC_NUM_RFCARS_SHIFT	16
+
+struct dect_erfc {
+	u32	rfcars;
+	u8	band;
+	u8	num_rfcars;
+	bool	erfc2;
+};
+
+/*
+ * Fixed Part capabilities
+ */
+
+#define DECT_QT_FPC_FREQ		8
+
+#define DECT_QT_FPC_CAPABILITY_MASK	0x000fffff00000000ULL
+#define DECT_QT_FPC_CAPABILITY_SHIFT	32
+
+#define DECT_QT_FPC_HLC_MASK		0x00000000ffff0000ULL
+#define DECT_QT_FPC_HLC_SHIFT		16
+
+struct dect_fpc {
+	u32	fpc;
+	u16	hlc;
+};
+
+/*
+ * Extended Fixed Part capabilities
+ */
+
+#define DECT_QT_EFPC_EFPC_MASK		0x000fff8000000000ULL
+#define DECT_QT_EFPC_EFPC_SHIFT		39
+
+#define DECT_QT_EFPC_EHLC_MASK		0x0000007fffff0000ULL
+#define DECT_QT_EFPC_EHLC_SHIFT		16
+
+struct dect_efpc {
+	u16	fpc;
+	u32	hlc;
+};
+
+#define DECT_QT_EFPC2_FPC_MASK		0x000fff0000000000ULL
+#define DECT_QT_EFPC2_FPC_SHIFT		40
+
+#define DECT_QT_EFPC2_HLC_MASK		0x000000ffffff0000ULL
+#define DECT_QT_EFPC2_HLC_SHIFT		16
+
+struct dect_efpc2 {
+	u16	fpc;
+	u32	hlc;
+};
+
+/*
+ * SARI message
+ */
+
+#define DECT_QT_SARI_FREQ		4
+
+#define DECT_QT_SARI_LIST_CYCLE_MASK	0x00000e0000000000ULL
+#define DECT_QT_SARI_LIST_CYCLE_SHIFT	41
+
+#define DECT_QT_SARI_TARI_FLAG		0x0000010000000000ULL
+
+#define DECT_QT_SARI_BLACK_FLAG		0x0000008000000000ULL
+
+#define DECT_QT_SARI_ARI_MASK		0x0000007fffffff00ULL
+#define DECT_QT_SARI_ARI_SHIFT		25
+
+struct dect_sari {
+	u8		list_cycle;
+	bool		tari;
+	bool		black;
+	struct dect_ari	ari;
+};
+
+#define DECT_SARI_CYCLE_MAX		16
+
+/*
+ * Multiframe number - repeated every 8 multiframes if supported
+ */
+
+#define DECT_QT_MFN_FREQ		8
+
+#define DECT_QT_MFN_MASK		0x000000ffffff0000ULL
+#define DECT_QT_MFN_SHIFT		16
+
+struct dect_mfn {
+	u32	num;
+};
+
+/*
+ * Extended RF carrier information part 2
+ */
+
+#define DECT_QT_TXI_ERFC2_FREQ		8
+
+#define DECT_QT_ERFC2_RFCARS_MASK	0x000fffffffe00000ULL
+#define DECT_QT_ERFC2_RFCARS_SHIFT	21
+
+struct dect_erfc2 {
+	u32	rfcars;
+};
+
+/*
+ * Transmit Information
+ */
+
+#define DECT_QT_TXI_FREQ		8
+
+#define DECT_QT_TXI_TYPE_MASK		0x000f000000000000ULL
+#define DECT_QT_TXI_TYPE_SHIFT		48
+
+#define DECT_QT_TXI_PWL_MASK		0x0000ff0000000000ULL
+#define DECT_QT_TXI_PWL_SHIFT		40
+
+/*
+ * Extended fixed part capabilitiees part 2
+ */
+
+/*
+ * Paging Tail (P-channel)
+ */
+
+#define DECT_PT_HDR_EXTEND_FLAG		0x0080000000000000ULL
+
+#define DECT_PT_HDR_LENGTH_MASK		0x0070000000000000ULL
+#define DECT_PT_HDR_LENGTH_SHIFT	52
+
+/**
+ * @DECT_PT_ZERO_PAGE:		zero length page
+ * @DECT_PT_SHORT_PAGE:		short page
+ * @DECT_PT_FULL_PAGE:		full page
+ * @DECT_PT_MAX_RESUME_PAGE:	MAC resume and control page
+ * @DECT_PT_LONG_PAGE:		not the last 36 bits of a long page
+ * @DECT_PT_LONG_PAGE_FIRST:	the first 36 bits of a long page
+ * @DECT_PT_LONG_PAGE_LAST:	the last 36 bits of a long page
+ * @DECT_PT_LONG_PAGE_ALL:	all of a long page (first and last)
+ *
+ */
+enum dect_page_lengths {
+	DECT_PT_ZERO_PAGE		= 0x0ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_SHORT_PAGE		= 0x1ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_FULL_PAGE		= 0x2ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_RESUME_PAGE		= 0x3ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_LONG_PAGE		= 0x4ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_LONG_PAGE_FIRST		= 0x5ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_LONG_PAGE_LAST		= 0x6ULL << DECT_PT_HDR_LENGTH_SHIFT,
+	DECT_PT_LONG_PAGE_ALL		= 0x7ULL << DECT_PT_HDR_LENGTH_SHIFT,
+};
+
+/* zero length pages */
+#define DECT_PT_ZP_RFPI_MASK		0x000fffff00000000ULL
+#define DECT_PT_ZP_RFPI_SHIFT		32
+
+/* short page B_S channel data */
+#define DECT_PT_SP_BS_DATA_MASK		0x000fffff00000000ULL
+#define DECT_PT_SP_BS_DATA_SHIFT	32
+#define DECT_PT_SP_BS_DATA_SIZE		3
+
+/* long and full page B_S channel data */
+#define DECT_PT_LFP_BS_DATA_MASK	0x000fffffffff0000ULL
+#define DECT_PT_LFP_BS_DATA_SHIFT	16
+#define DECT_PT_LFP_BS_DATA_SIZE	5
+
+struct dect_page {
+	bool			extend;
+	enum dect_page_lengths	length;
+	u32			rfpi;
+};
+
+/* MAC layer information */
+#define DECT_PT_INFO_TYPE_MASK		0x00000000f0000000ULL
+#define DECT_PT_INFO_TYPE_SHIFT		28
+#define DECT_PT_INFO_TYPE_SIZE		2
+
+/**
+ * @DECT_PT_IT_FILL_BITS_OR_BLIND_LONG_SLOTS:	fill bits/blind long slots if bit 47 set
+ * @DECT_PT_IT_BLIND_FULL_SLOT:			blind full slot information
+ * @DECT_PT_IT_OTHER_BEARER:
+ * @DECT_PT_IT_RECOMMENDED_OTHER_BEARER:
+ * @DECT_PT_IT_GOOD_RFP_BEARER:
+ * @DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION:
+ * @DECT_PT_IT_RFP_IDENTITY:
+ * @DECT_PT_IT_ESCAPE:
+ * @DECT_PT_IT_DUMMY_OR_CL_BEARER_MARKER:
+ * @DECT_PT_IT_BEARER_HANDOVER_INFO:
+ * @DECT_PT_IT_RFP_STATUS:
+ * @DECT_PT_IT_ACTIVE_CARRIERS:
+ * @DECT_PT_IT_CL_BEARER_POSITION:
+ * @DECT_PT_IT_RECOMMENDED_POWER_LEVEL:
+ * @DECT_PT_IT_BLIND_DOUBLE_SLOT:
+ * @DECT_PT_IT_BLIND_FULL_SLOT_PACKET_MODE:
+ *
+ */
+enum dect_pt_info_types {
+	DECT_PT_IT_FILL_BITS_OR_BLIND_LONG_SLOTS= 0x0ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_BLIND_FULL_SLOT		= 0x1ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_OTHER_BEARER			= 0x2ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_RECOMMENDED_OTHER_BEARER	= 0x3ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_GOOD_RFP_BEARER		= 0x4ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION	= 0x5ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_RFP_IDENTITY			= 0x6ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_ESCAPE			= 0x7ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_DUMMY_OR_CL_BEARER_MARKER	= 0x8ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_BEARER_HANDOVER_INFO		= 0x9ULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_RFP_STATUS			= 0xaULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_ACTIVE_CARRIERS		= 0xbULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_CL_BEARER_POSITION		= 0xcULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_RECOMMENDED_POWER_LEVEL	= 0xdULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_BLIND_DOUBLE_SLOT		= 0xeULL << DECT_PT_INFO_TYPE_SHIFT,
+	DECT_PT_IT_BLIND_FULL_SLOT_PACKET_MODE	= 0xfULL << DECT_PT_INFO_TYPE_SHIFT,
+};
+
+/* blind full slot information */
+#define DECT_PT_BFS_MASK		0x000000000fff0000ULL
+#define DECT_PT_BFS_SHIFT		16
+
+struct dect_bfs {
+	struct dect_page	page;
+	u16			mask;
+};
+
+/* Bearer description */
+#define DECT_PT_BEARER_SN_MASK		0x000000000f000000ULL
+#define DECT_PT_BEARER_SN_SHIFT		24
+
+#define DECT_PT_BEARER_SP_MASK		0x0000000000c00000ULL
+#define DECT_PT_BEARER_SP_SHIFT		22
+
+#define DECT_PT_BEARER_CN_MASK		0x00000000003f0000ULL
+#define DECT_PT_BEARER_CN_SHIFT		16
+
+struct dect_bearer_desc {
+	struct dect_page	page;
+	enum dect_pt_info_types	bt;
+	u8			sn;
+	u8			sp;
+	u8			cn;
+};
+
+/* RFP identity */
+#define DECT_PT_RFP_ID_MASK		0x000000000fff0000ULL
+#define DECT_PT_RFP_ID_SHIFT		16
+
+struct dect_rfp_id {
+	struct dect_page	page;
+	u16			id;
+};
+
+/* RFP status */
+#define DECT_PT_RFPS_RFP_BUSY_FLAG	0x0000000001000000ULL
+#define DECT_PT_RFPS_SYS_BUSY_FLAG	0x0000000002000000ULL
+
+struct dect_rfp_status {
+	struct dect_page	page;
+	bool			rfp_busy;
+	bool			sys_busy;
+};
+
+/* Active carriers */
+#define DECT_PT_ACTIVE_CARRIERS_MASK	0x000000000ffc0000ULL
+#define DECT_PT_ACTIVE_CARRIERS_SHIFT	18
+
+struct dect_active_carriers {
+	struct dect_page	page;
+	u16			active;
+};
+
+/*
+ * MAC control (M-channel)
+ */
+
+#define DECT_MT_FRAME_RATE		2
+
+#define DECT_MT_HDR_MASK		0x00f0000000000000ULL
+#define DECT_MT_HDR_SHIFT		52
+
+#define DECT_MT_CMD_MASK		0x000f000000000000ULL
+#define DECT_MT_CMD_SHIFT		48
+
+/**
+ * enum dect_mt_hdr_type - MAC tail header types
+ */
+enum dect_mt_hdr_type {
+	DECT_MT_BASIC_CCTRL			= 0x0ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_ADV_CCTRL			= 0x1ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_MAC_TEST			= 0x2ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_QUALITY_CTRL			= 0x3ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_BRD_CL_SERVICE			= 0x4ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_ENC_CTRL			= 0x5ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_XYZ				= 0x6ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_ESC				= 0x7ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_TARI				= 0x8ULL << DECT_MT_HDR_SHIFT,
+	DECT_MT_REP_CCTRL			= 0x9ULL << DECT_MT_HDR_SHIFT,
+};
+
+/* advanced connection control */
+enum dect_cctrl_cmds {
+	DECT_CCTRL_ACCESS_REQ			= 0x0ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_BEARER_HANDOVER_REQ		= 0x1ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_CONNECTION_HANDOVER_REQ	= 0x2ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_UNCONFIRMED_ACCESS_REQ	= 0x3ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_BEARER_CONFIRM		= 0x4ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_WAIT				= 0x5ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_ATTRIBUTES_T_REQUEST		= 0x6ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_ATTRIBUTES_T_CONFIRM		= 0x7ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_BANDWIDTH_T_REQUEST		= 0x8ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_BANDWIDTH_T_CONFIRM		= 0x9ULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_CHANNEL_LIST			= 0xaULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_UNCONFIRMED_DUMMY		= 0xbULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_UNCONFIRMED_HANDOVER		= 0xcULL << DECT_MT_CMD_SHIFT,
+	DECT_CCTRL_RELEASE			= 0xfULL << DECT_MT_CMD_SHIFT,
+};
+
+/* Most messages */
+#define DECT_CCTRL_FMID_MASK			0x0000fff000000000ULL
+#define DECT_CCTRL_FMID_SHIFT			36
+
+#define DECT_CCTRL_PMID_MASK			0x0000000fffff0000ULL
+#define DECT_CCTRL_PMID_SHIFT			16
+
+/* Attributes-T request/confirm */
+#define DECT_CCTRL_ATTR_ECN_MASK		0x0000f00000000000ULL
+#define DECT_CCTRL_ATTR_ECN_SHIFT		44
+
+#define DECT_CCTRL_ATTR_LBN_MASK		0x00000f0000000000ULL
+#define DECT_CCTRL_ATTR_LBN_SHIFT		40
+
+#define DECT_CCTRL_ATTR_TYPE_MASK		0x000000c000000000ULL
+#define DECT_CCTRL_ATTR_TYPE_SHIFT		38
+
+enum dect_cctrl_connection_types {
+	DECT_CCTRL_TYPE_ASYMETRIC_UPLINK	= 0x0,
+	DECT_CCTRL_TYPE_ASYMETRIC_DOWNLINK	= 0x1,
+	DECT_CCTRL_TYPE_SYMETRIC_MULTIBEARER	= 0x2,
+	DECT_CCTRL_TYPE_SYMETRIC_BEARER		= 0x3,
+};
+
+#define DECT_CCTRL_ATTR_SERVICE_MASK		0x0000003f00000000ULL
+#define DECT_CCTRL_ATTR_SERVICE_SHIFT		32
+
+#define DECT_CCTRL_ATTR_SLOT_MASK		0x00000000f0000000ULL
+#define DECT_CCTRL_ATTR_SLOT_SHIFT		28
+
+#define DECT_CCTRL_ATTR_CF_FLAG			0x0000000008000000ULL
+
+#define DECT_CCTRL_ATTR_BZ_EXT_MOD_MASK		0x0000000007000000ULL
+#define DECT_CCTRL_ATTR_BZ_EXT_MOD_SHIFT	24
+
+#define DECT_CCTRL_ATTR_ACR_MASK		0x0000000000f00000ULL
+#define DECT_CCTRL_ATTR_ACR_SHIFT		20
+
+enum dect_adaptive_code_rates {
+	DECT_ACR_NONE				= 0x0,
+};
+
+#define DECT_CCTRL_ATTR_A_MOD_MASK		0x00000000000c0000ULL
+#define DECT_CCTRL_ATTR_A_MOD_SHIFT		18
+
+#define DECT_CCTRL_ATTR_BZ_MOD_MASK		0x0000000000030000ULL
+#define DECT_CCTRL_ATTR_BZ_MOD_SHIFT		16
+
+enum dect_modulation_type {
+	DECT_MODULATION_2_LEVEL			= 0x3,
+	DECT_MODULATION_4_LEVEL			= 0x2,
+	DECT_MODULATION_8_LEVEL			= 0x1,
+};
+
+/* Release */
+
+#define DECT_CCTRL_RELEASE_INFO1_MASK		0x0000f00000000000ULL
+#define DECT_CCTRL_RELEASE_INFO1_SHIFT		44
+
+#define DECT_CCTRL_RELEASE_LBN_MASK		0x00000f0000000000ULL
+#define DECT_CCTRL_RELEASE_LBN_SHIFT		40
+
+#define DECT_CCTRL_RELEASE_REASON_MASK		0x000000f000000000ULL
+#define DECT_CCTRL_RELEASE_REASON_SHIFT		36
+
+enum dect_release_reasons {
+	DECT_REASON_UNKNOWN				= 0x0,
+	DECT_REASON_BEARER_RELEASE			= 0x1,
+	DECT_REASON_CONNECTION_RELEASE			= 0x2,
+	DECT_REASON_BEARER_SETUP_OR_HANDOVER_FAILED	= 0x3,
+	DECT_REASON_BEARER_HANDOVER_COMPLETED		= 0x4,
+	DECT_REASON_BEARER_HANDOVER_CLUSTER		= 0x5,
+	DECT_REASON_TIMEOUT_LOST_SIGNAL			= 0x6,
+	DECT_REASON_TIMEOUT_LOST_HANDSHAKE		= 0x7,
+	DECT_REASON_REQUESTED_UNACCEPTABLE_SLOT_TYPE	= 0x8,
+	DECT_REASON_REQUESTED_UNACCEPTABLE_MAC_SERVICE	= 0x9,
+	DECT_REASON_BASE_STATION_BUSY			= 0xa,
+	DECT_REASON_REVERSE_DIRECTION			= 0xb,
+	DECT_REASON_DUPLICATE_PMID			= 0xc,
+	DECT_REASON_UNACCEPTABLE_PMID			= 0xd,
+	DECT_REASON_STAY_ON_LISTEN			= 0xe,
+};
+
+#define DECT_CCTRL_RELEASE_PMID_MASK		0x0000000fffff0000ULL
+#define DECT_CCTRL_RELEASE_PMID_SHIFT		16
+
+struct dect_cctrl {
+	enum dect_cctrl_cmds		cmd;
+	union {
+		struct {
+			union {
+				struct {
+					u16		fmid;
+				};
+				struct {
+					u8		lbn;
+					union {
+						u8		ecn;
+						u8		reason;
+					};
+				};
+			};
+			union {
+				u32		pmid;
+				struct {
+					u8		type;
+					u8		service;
+					u8		slot;
+					u8		cf;
+					u8		a_mod;
+					u8		bz_mod;
+					u8		bz_ext_mod;
+					u8		acr;
+				};
+			};
+		};
+	};
+};
+
+/* Encryption Control */
+
+#define DECT_ENCCTRL_FILL_MASK			0x0050000000000000ULL
+
+#define DECT_ENCCTRL_CMD_MASK			0x000f000000000000ULL
+#define DECT_ENCCTRL_CMD_SHIFT			48
+
+enum dect_encctrl_cmds {
+	DECT_ENCCTRL_START_REQUEST		= 0x0,
+	DECT_ENCCTRL_START_CONFIRM		= 0x1,
+	DECT_ENCCTRL_START_GRANT		= 0x2,
+	DECT_ENCCTRL_STOP_REQUEST		= 0x4,
+	DECT_ENCCTRL_STOP_CONFIRM		= 0x5,
+	DECT_ENCCTRL_STOP_GRANT			= 0x6,
+};
+
+#define DECT_ENCCTRL_FMID_MASK			0x0000fff000000000ULL
+#define DECT_ENCCTRL_FMID_SHIFT			36
+
+#define DECT_ENCCTRL_PMID_MASK			0x0000000fffff0000ULL
+#define DECT_ENCCTRL_PMID_SHIFT			16
+
+struct dect_encctrl {
+	enum dect_encctrl_cmds	cmd;
+	u32			pmid;
+	u16			fmid;
+};
+
+/* marker for T-MUX exceptions */
+#define DECT_MT_HIGH_PRIORITY		0x1
+
+/*
+ * C_T data
+ */
+
+#define DECT_C_S_SDU_SIZE		5
+
+struct dect_ct_data {
+	u8				seq;
+};
+
+/*
+ * Flat representation of tail message contents
+ */
+enum dect_tail_msg_types {
+	DECT_TM_TYPE_INVALID,
+	DECT_TM_TYPE_ID,
+	DECT_TM_TYPE_SSI,
+	DECT_TM_TYPE_ERFC,
+	DECT_TM_TYPE_FPC,
+	DECT_TM_TYPE_EFPC,
+	DECT_TM_TYPE_EFPC2,
+	DECT_TM_TYPE_SARI,
+	DECT_TM_TYPE_MFN,
+	DECT_TM_TYPE_PAGE,
+	DECT_TM_TYPE_BFS,
+	DECT_TM_TYPE_BD,
+	DECT_TM_TYPE_RFP_ID,
+	DECT_TM_TYPE_RFP_STATUS,
+	DECT_TM_TYPE_ACTIVE_CARRIERS,
+	DECT_TM_TYPE_BCCTRL,
+	DECT_TM_TYPE_ACCTRL,
+	DECT_TM_TYPE_ENCCTRL,
+	DECT_TM_TYPE_CT,
+};
+
+struct dect_tail_msg {
+	enum dect_tail_identifications		ti;
+	enum dect_tail_msg_types		type;
+	union {
+		struct dect_idi			idi;
+		struct dect_ssi			ssi;
+		struct dect_erfc		erfc;
+		struct dect_fpc			fpc;
+		struct dect_efpc		efpc;
+		struct dect_efpc2		efpc2;
+		struct dect_sari		sari;
+		struct dect_mfn			mfn;
+		struct dect_page		page;
+		struct dect_bfs			bfs;
+		struct dect_bearer_desc		bd;
+		struct dect_rfp_id		rfp_id;
+		struct dect_rfp_status		rfp_status;
+		struct dect_active_carriers	active_carriers;
+		struct dect_cctrl		cctl;
+		struct dect_encctrl		encctl;
+		struct dect_ct_data		ctd;
+	};
+};
+
+struct dect_si {
+	u32				mask;
+	struct dect_ssi			ssi;
+	struct dect_erfc		erfc;
+	struct dect_fpc			fpc;
+	struct dect_efpc		efpc;
+	struct dect_efpc2		efpc2;
+	struct dect_sari		sari[DECT_SARI_CYCLE_MAX];
+	struct dect_mfn			mfn;
+	u8				num_saris;
+};
+
+/*
+ * B-Field
+ */
+
+/**
+ * dect_b_identitifications - MAC layer B-Field Identification
+ *
+ * @DECT_BI_UTYPE_0:		U-Type, I_N, SI_N, SI_P or I_P packet number 0
+ * @DECT_BI_UTYPE_1:		U-Type, I_P error detect or I_P packet number 1
+ * @DECT_BI_ETYPE_CF_0:		E-Type, all C_F or CL_F, packet number 0
+ * @DECT_BI_ETYPE_CF_1:		E-Type, all C_F, packet number 1
+ * @DECT_BI_ETYPE_MAC:		E-Type, all MAC control (unnumbered)
+ * @DECT_BI_NONE:		no B-Field
+ */
+enum dect_b_identifications {
+	DECT_BI_UTYPE_0			= 0x0 << DECT_HDR_BA_SHIFT,
+	DECT_BI_UTYPE_1			= 0x1 << DECT_HDR_BA_SHIFT,
+	DECT_BI_ETYPE_CF_0		= 0x2 << DECT_HDR_BA_SHIFT,
+	DECT_BI_DOUBLE_SLOT_REQUIRED	= DECT_BI_ETYPE_CF_0,
+	DECT_BI_ETYPE_CF_1		= 0x3 << DECT_HDR_BA_SHIFT,
+	DECT_BI_ETYPE_NOT_ALL_CF_0	= 0x4 << DECT_HDR_BA_SHIFT,
+	DECT_BI_HALF_SLOT_REQUIRED	= DECT_BI_ETYPE_NOT_ALL_CF_0,
+	DECT_BI_ETYPE_NOT_ALL_CF_1	= 0x5 << DECT_HDR_BA_SHIFT,
+	DECT_BI_LONG_SLOT_640_REQUIRED	= DECT_BI_ETYPE_NOT_ALL_CF_1,
+	DECT_BI_ETYPE_MAC		= 0x6 << DECT_HDR_BA_SHIFT,
+	DECT_BI_LONG_SLOT_672_REQUIRED	= DECT_BI_ETYPE_MAC,
+	DECT_BI_NONE			= 0x7 << DECT_HDR_BA_SHIFT,
+};
+
+struct dect_skb_b_cb {
+	enum dect_b_identifications	id;
+};
+
+#define DECT_B_CB(skb)		((struct dect_skb_b_cb *)(skb)->cb)
+
+#define DECT_C_F_SDU_SIZE	8
+#define DECT_G_F_SDU_SIZE	8
+
+/**
+ * enum dect_mac_channels - internal MAC control channels
+ *
+ * @DECT_MC_Q:		System information and multiframe marker
+ * @DECT_MC_N:		Identities information
+ * @DECT_MC_M:		MAC control channel
+ * @DECT_MC_P:		MAC Paging channel
+ */
+enum dect_mac_channels {
+	DECT_MC_Q,
+	DECT_MC_N,
+	DECT_MC_M,
+	DECT_MC_P,
+};
+
+/**
+ * enum dect_data_channels - logical MAC data channels
+ *
+ * @DECT_MC_G_F:
+ * @DECT_MC_C_S:	Higher layer C-Plane channel (slow)
+ * @DECT_MC_C_F:	Higher layer C-Plane channel (fast)
+ * @DECT_MC_I_N:	Higher layer U-Plane channel (numbered)
+ * @DECT_MC_I_P:	Higher layer U-Plane channel (protected)
+ * @DECT_MC_SI_N:	Higher layer connectionless U-Plane channel (numbered)
+ * @DECT_MC_SI_P:	Higher layer connectionless U-Plane channel (protected)
+ */
+enum dect_data_channels {
+	DECT_MC_G_F,
+	DECT_MC_C_S,
+	DECT_MC_C_F,
+	DECT_MC_I_N,
+	DECT_MC_I_P,
+	DECT_MC_SI_N,
+	DECT_MC_SI_P,
+	__DECT_MC_MAX
+};
+#define DECT_MC_MAX		(__DECT_MC_MAX - 1)
+
+/**
+ * enum dect_mac_connection_types - MAC Connection types
+ *
+ * @DECT_MAC_CONN_BASIC:	Basic connection, always I_N_min_delay service
+ * @DECT_MAC_CONN_ADVANCED:	Advanced connection
+ * @DECT_MAC_CONN_COMPLEMENT:	Complementary connection
+ */
+enum dect_mac_connection_types {
+	DECT_MAC_CONN_BASIC,
+	DECT_MAC_CONN_ADVANCED,
+	DECT_MAC_CONN_COMPLEMENT,
+};
+
+/**
+ * struct dect_tbc_id
+ *
+ * @ari:		FT identifier
+ * @pmid:		Portable MAC identity
+ * @lbn:		Logical Bearer Number
+ * @enc:		Exchanged connection number
+ * @tbei:		Traffic Bearer Endpoint Identifier
+ */
+struct dect_tbc_id {
+	struct dect_ari			ari;
+	struct dect_pmid		pmid;
+	u8				lbn;
+	u8				ecn;
+	u32				tbei;
+};
+
+/**
+ * struct dect_mbc_id
+ *
+ * @mcei:		MAC Connection Endpoint Identifier
+ * @type:		Connection Type (Basic/Advanced)
+ * @ari:		FT identifier
+ * @pmid:		Portable MAC Identity
+ * @ecn:		Exchanged Connection Number
+ * @service:		Service type
+ */
+struct dect_mbc_id {
+	u32				mcei;
+	enum dect_mac_connection_types	type;
+	struct dect_ari			ari;
+	struct dect_pmid		pmid;
+	u8				ecn;
+};
+
+#endif /* _NET_DECT_MAC_H */
diff --git a/target/linux/generic/files/include/net/dect/mac_ccf.h b/target/linux/generic/files/include/net/dect/mac_ccf.h
new file mode 100644
index 0000000..e3cb1ba
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/mac_ccf.h
@@ -0,0 +1,250 @@
+/*
+ * DECT MAC Layer - Cluster Control Functions (CCF)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_MAC_CCF_H
+#define _NET_DECT_MAC_CCF_H
+
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <net/dect/mac.h>
+
+/**
+ * struct dect_bmc_skb_cb
+ *
+ * @fast_page:		page message is a fast page
+ * @long_page:		page message is a long page
+ * @stamp:		multiframe number at time of TX request
+ * @repetitions:	number of page repetitions
+ */
+struct dect_bmc_skb_cb {
+	bool				fast_page;
+	bool				long_page;
+	u32				stamp;
+	u8				repetitions;
+};
+#define DECT_BMC_CB(skb)		((struct dect_bmc_skb_cb *)(skb)->cb)
+
+#define DECT_PAGE_LIFETIME		6	/* multiframes */
+
+/**
+ * struct dect_bmc - broadcast message control
+ *
+ * @bcs:		broadcast controller list
+ * @index:		system information round robin index
+ */
+struct dect_bmc {
+	struct list_head		bcs;
+	unsigned int			index;
+};
+
+struct dect_cmc {
+
+};
+
+struct dect_cs_skb_cb {
+	u8				seq;
+};
+#define DECT_CS_CB(skb)			((struct dect_cs_skb_cb *)(skb)->cb)
+
+/**
+ * struct dect_tb - DECT Traffic Bearer
+ *
+ * @list:		MBC traffic bearer list node
+ * @mbc:		MBC controlling the traffic bearer
+ * @ch:			Cell handling the traffic bearer
+ * @id:			Traffic Bearer Controller ID
+ * @handover:		Handover yes/no
+ * @handover_timer:	Handover timer
+ * @rx_slot:		Receive slot
+ * @tx_slot:		Transmit slot
+ * @slot_rx_timer:	Receive slot timer
+ * @slot_tx_timer:	Transmit slot timer
+ * @b_rx_skb:		B-Field receive skb
+ */
+struct dect_tb {
+	struct list_head		list;
+	struct dect_mbc			*mbc;
+	const struct dect_cell_handle	*ch;
+	struct dect_tbc_id		id;
+	bool				handover;
+
+	/* FP: handover release timer */
+	struct dect_timer		handover_timer;
+
+	/* Slot transmit/receive timers */
+	u8				rx_slot;
+	u8				tx_slot;
+	struct dect_timer		slot_rx_timer;
+	struct dect_timer		slot_tx_timer;
+
+	/* I channel data */
+	struct sk_buff			*b_rx_skb;
+};
+
+struct dect_mbc_stats {
+	unsigned int			cs_rx_bytes;
+	unsigned int			cs_tx_bytes;
+	unsigned int			i_rx_bytes;
+	unsigned int			i_tx_bytes;
+	unsigned int			handovers;
+};
+
+/**
+ * struct dect_mbc - DECT Multi-Bearer Control
+ *
+ * @list:		Cluster connection list node
+ * @cl:			Cluster the MBC is contained in
+ * @refcnt:		Reference count
+ * @type:		connection type
+ * @id:			MBC identity
+ * @state:		MBC state
+ * @timer:		Connection setup timer (T200)
+ * @setup_cnt:		number of setup attempts (N200)
+ * @tbs:		List of traffic bearers
+ * @ho_stamp:		Handover token bucket refill timestamp
+ * @ho_tokens:		Handover token bucket tokens
+ * @normal_rx_timer:	Normal receive half frame timer
+ * @onrmal_tx_timer:	Normal transmit half frame timer
+ * @ck:			Cipher key
+ * @cipher_state:	Ciphering state
+ * @cs_rx_seq:		C_S receive sequence number
+ * @cs_tx_seq:		C_S transmit sequence number
+ * @cs_tx_ok:		C_S segment transmit OK
+ * @cs_rx_ok:		C_S segment reception OK
+ * @cs_tx_skb:		C_S segment queued for transmission
+ * @cs_tx_skb:		C_S segment queued for delivery to DLC
+ */
+struct dect_mbc {
+	struct list_head		list;
+	struct dect_cluster		*cl;
+	unsigned int			refcnt;
+
+	enum dect_mac_connection_types	type;
+	struct dect_mbc_id		id;
+	enum dect_mbc_state		state;
+	enum dect_mac_service_types	service;
+
+	struct timer_list		timer;
+	u8				setup_cnt;
+
+	struct list_head		tbs;
+
+	/* Handover rate limiting */
+	unsigned long			ho_stamp;
+	u8				ho_tokens;
+
+	/* Normal transmit/receive timers */
+	struct dect_timer		normal_rx_timer;
+	struct dect_timer		normal_tx_timer;
+
+	/* Encryption */
+	u64				ck;
+	enum dect_cipher_states		cipher_state;
+
+	/* C_S channel */
+	u8				cs_rx_seq;
+	u8				cs_tx_seq;
+	bool				cs_tx_ok;
+	bool				cs_rx_ok;
+	struct sk_buff			*cs_rx_skb;
+	struct sk_buff			*cs_tx_skb;
+
+	struct dect_mbc_stats		stats;
+};
+
+#define DECT_MBC_SETUP_TIMEOUT		(5 * HZ)	/* T200: 5 seconds */
+#define DECT_MBC_SETUP_MAX_ATTEMPTS	10		/* N200: 10 attempts */
+#define DECT_MBC_HANDOVER_TIMER		(3 * HZ)	/* T202: 3 seconds */
+#define DECT_MBC_TB_HANDOVER_TIMEOUT	16		/* T203: 16 frames */
+
+#define DECT_MBC_HANDOVER_LIMIT		2		/* per N202 seconds */
+#define DECT_MBC_HANDOVER_REATTEMPTS	15		/* N201: 15 */
+
+extern u32 dect_mbc_alloc_mcei(struct dect_cluster *cl);
+extern int dect_mac_con_req(struct dect_cluster *cl,
+			    const struct dect_mbc_id *id);
+extern void dect_mac_dis_req(struct dect_cluster *cl, u32 mcei);
+
+extern int dect_mac_enc_key_req(const struct dect_cluster *cl, u32 mcei, u64 ck);
+extern int dect_mac_enc_eks_req(const struct dect_cluster *cl, u32 mcei,
+				enum dect_cipher_states status);
+
+extern void dect_bmc_mac_page_req(struct dect_cluster *cl, struct sk_buff *skb);
+
+struct dect_llme_req;
+
+/**
+ * struct dect_ccf_ops - Cluster Control Ops
+ *
+ * @bind:			bind cell to cluster
+ * @unbind:			unbind cell from cluster
+ * @mac_info_indicate:		indicate FP mac layer information (PP only)
+ * @mbc_conn_indicate:		indicate a new TBC connection
+ * @mbc_conn_notify:		notify MBC of TBC events
+ * @mbc_data_indicate:		indicate new data to MBC
+ * @bmc_page_indicate:		indicate reception of a page message to the BMC
+ */
+struct dect_cluster_handle;
+struct dect_scan_result;
+enum dect_tbc_event;
+struct dect_ccf_ops {
+	int	(*bind)(struct dect_cluster_handle *,
+			struct dect_cell_handle *);
+	void	(*unbind)(struct dect_cluster_handle *,
+			  struct dect_cell_handle *);
+
+	void	(*time_ind)(struct dect_cluster_handle *,
+			    enum dect_timer_bases, u32, u8, u8);
+
+	void	(*scan_report)(const struct dect_cluster_handle *,
+			       const struct dect_scan_result *);
+
+	void	(*mac_info_ind)(const struct dect_cluster_handle *,
+				const struct dect_idi *,
+				const struct dect_si *);
+
+	int	(*tbc_establish_ind)(const struct dect_cluster_handle *,
+				     const struct dect_cell_handle *,
+				     const struct dect_tbc_id *,
+				     enum dect_mac_service_types, bool);
+	int	(*tbc_establish_cfm)(const struct dect_cluster_handle *,
+				     const struct dect_tbc_id *, bool, u8);
+	void	(*tbc_dis_ind)(const struct dect_cluster_handle *,
+			       const struct dect_tbc_id *,
+			       enum dect_release_reasons);
+	int	(*tbc_event_ind)(const struct dect_cluster_handle *,
+				 const struct dect_tbc_id *,
+				 enum dect_tbc_event);
+	void	(*tbc_data_ind)(const struct dect_cluster_handle *,
+				const struct dect_tbc_id *,
+				enum dect_data_channels chan,
+				struct sk_buff *);
+	int	(*tbc_handover_req)(const struct dect_cluster_handle *,
+				    const struct dect_tbc_id *);
+
+	void	(*bmc_page_ind)(const struct dect_cluster_handle *,
+				struct sk_buff *);
+};
+
+/**
+ * struct dect_cluster_handle - Cell's view of a cluster
+ *
+ * @ops:		Cluster Control Function ops
+ * @index:		Cluster index
+ * @tipc_id:		Cluster TIPC user ID
+ * @tportref:		Topology Service port reference (remote cluster only)
+ * @portref:		Cell Control Protocol port reference (remote cluster only)
+ */
+struct dect_cluster_handle {
+	const struct dect_ccf_ops	*ops;
+	u8				index;
+
+	u32				tipc_id;
+	u32				tportref;
+	u32				portref;
+};
+
+#endif /* _NET_DECT_MAC_CCF_H */
diff --git a/target/linux/generic/files/include/net/dect/mac_csf.h b/target/linux/generic/files/include/net/dect/mac_csf.h
new file mode 100644
index 0000000..4a182f3
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/mac_csf.h
@@ -0,0 +1,596 @@
+/*
+ * DECT MAC Layer - Cell Site Functions (CSF)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_MAC_CSF_H
+#define _NET_DECT_MAC_CSF_H
+
+#include <net/dect/mac.h>
+#include <net/dect/transceiver.h>
+#define DECT_CHANNEL_LIST_DBM_RES	6
+#define DECT_CHANNEL_LIST_BINS		(DECT_RSSI_DBM_RANGE / DECT_CHANNEL_LIST_DBM_RES)
+
+/**
+ * struct dect_channel_list_entry
+ *
+ * @list:		channel list bin node
+ * @slot:		slot number
+ * @carrier:		RF-carrier
+ * @rssi:		measured RSSI value
+ */
+struct dect_channel_list_entry {
+	struct list_head	list;
+	u8			slot;
+	u8			carrier;
+	u8			rssi;
+};
+
+/**
+ * struct dect_channel_list - Basic channel list
+ *
+ * @list:		cell's channel lists list node
+ * @pkt:		packet type used for RSSI measurement
+ * @status:		bitmask of completed carriers
+ * @timer:		update timer
+ * @available:		number of available entries
+ * @bins:		channels ordered by RSSI value
+ * @entries:		channel list entries
+ *
+ * A channel list contains channel descriptions of all physical channels
+ * able to carry the packet type, sorted into multiple bins based on the
+ * maximum RSSI value of the TDD slot pair.
+ */
+struct dect_channel_list {
+	struct list_head		list;
+	enum dect_packet_types		pkt;
+	u64				status;
+
+	struct dect_timer		timer;
+	u16				available;
+	struct list_head		bins[DECT_CHANNEL_LIST_BINS];
+	struct dect_channel_list_entry	entries[];
+};
+
+#define DECT_CHANNEL_LIST_MAX_AGE	30	/* T209: 30 seconds */
+#define DECT_CHANNEL_LIST_MAX_DBM	-50	/* dBm */
+#define DECT_CHANNEL_LIST_LOW_WATERMARK	20	/* channels */
+
+#define DECT_CHANNEL_MIN_DELAY		2	/* frames */
+
+enum dect_bearer_states {
+	DECT_DUMMY_BEARER,
+	DECT_TRAFFIC_BEARER,
+	DECT_CL_BEARER,
+	DECT_MONITOR_BEARER,
+};
+
+enum dect_bearer_modes {
+	DECT_BEARER_RX,
+	DECT_BEARER_TX,
+};
+
+/**
+ * enum dect_bearer_state - DECT MAC bearer states
+ *
+ * @DECT_BEARER_INACTIVE:	bearer inactive
+ * @DECT_BEARER_SCHEDULED:	bearer is scheduled for activation
+ * @DECT_BEARER_RSSI_CONFIRM:	bearer is scheduled for RSSI confirmation
+ * @DECT_BEARER_RSSI_CONFIRMED:	RSSI is confirmed, bearer is scheduled for e
+ * @DECT_BEARER_ENABLED:	bearer is enabled
+ */
+enum dect_bearer_state {
+	DECT_BEARER_INACTIVE,
+	DECT_BEARER_SCHEDULED,
+	DECT_BEARER_RSSI_CONFIRM,
+	DECT_BEARER_RSSI_CONFIRMED,
+	DECT_BEARER_ENABLED,
+};
+
+struct dect_bearer;
+struct dect_bearer_ops {
+	enum dect_bearer_states	state;
+	void			(*enable)(struct dect_cell *, struct dect_bearer *);
+	void			(*report_rssi)(struct dect_cell *, struct dect_bearer *,
+					       u8 slot, u8 rssi);
+	void			(*rcv)(struct dect_cell *cell, struct dect_bearer *,
+				       struct sk_buff *);
+};
+
+/**
+ * struct dect_bearer - DECT MAC Bearer
+ *
+ * @type:		bearer type
+ * @state:		operational state
+ * @trx:		DECT transceiver
+ * @chd:		channel description
+ * @mode:		bearer mode (RX/TX)
+ * @tx_timer:		TX enable timer
+ * @rssi:		last measured RSSI of selected channel
+ * @m_tx_queue:		M-channel TX queue
+ * @q:			Hdr-field MUX for Q1/Q2 bit settings
+ * @union:		bearer type specific data
+ */
+struct dect_bearer {
+	const struct dect_bearer_ops	*ops;
+	struct dect_transceiver		*trx;
+	struct dect_channel_desc	chd;
+	enum dect_bearer_modes		mode;
+	enum dect_bearer_state		state;
+	struct dect_timer		tx_timer;
+	u8				rssi;
+
+	struct sk_buff_head		m_tx_queue;
+	u8				q;
+
+	union {
+		struct dect_dbc		*dbc;
+		struct dect_cbc		*cbc;
+		struct dect_tbc		*tbc;
+		struct dect_dmb		*dmb;
+		struct dect_irc		*irc;
+		void			*data;
+	};
+};
+
+/**
+ * struct dect_bc - broadcast controller
+ *
+ * @list:		broadcast message control BC list node
+ * @p_rx_skb:		current RX P-channel message
+ * @p_tx_mask:		bitmask of scheduled mac layer pages
+ */
+struct dect_bc {
+	struct list_head		list;
+	struct sk_buff			*p_rx_skb;
+	u32				p_tx_mask;
+};
+
+/*
+ * enum dect_bearer_qctrl_state - DECT bearer quality control state
+ *
+ * @DECT_BEARER_QCTRL_WAIT:	waiting for next quality control event
+ * @DECT_BEARER_QCTRL_CONFIRM:	performing quality control
+ */
+enum dect_bearer_qctrl_state {
+	DECT_BEARER_QCTRL_WAIT,
+	DECT_BEARER_QCTRL_CONFIRM,
+};
+
+#define DECT_BEARER_QCTRL_FRAMENUM	15	/* must not affect paging */
+#define DECT_BEARER_QCTRL_PERIOD	256	/* frames */
+
+/**
+ * struct dect_dbc - dummy bearer control
+ *
+ * @list:		cell dbc list node
+ * @cell:		DECT cell
+ * @bearer:		dummy bearer
+ * @qctrl_timer:	quality control timer
+ * @qctrl_state:	qaulity control state
+ * @bc:			broadcast controller
+ */
+struct dect_dbc {
+	struct list_head		list;
+	struct dect_cell		*cell;
+	struct dect_bearer		bearer;
+	struct dect_timer		qctrl_timer;
+	enum dect_bearer_qctrl_state	qctrl;
+	struct dect_bc			bc;
+};
+
+/*
+ * struct dect_cbc - connectionless bearer control
+ *
+ * @cell:		DECT cell
+ * @dl_bearer:		connectionless downlink bearer
+ * @ul_bearer:		connectionless uplink bearer, if present
+ * @bc:			broadcast controller
+ */
+struct dect_cbc {
+	struct dect_cell		*cell;
+	struct dect_bearer		dl_bearer;
+	struct dect_bearer		ul_bearer;
+	struct dect_bc			bc;
+};
+
+/**
+ * enum dect_tbc_state - DECT Traffic Bearer Controller state
+ *
+ * @DECT_TBC_NONE:		Initial state
+ * @DECT_TBC_REQ_SENT:		Initiator: bearer request sent
+ * @DECT_TBC_WAIT_RCVD:		Initiator: intermediate state
+ * @DECT_TBC_REQ_RCVD:		Responder: request received
+ * @DECT_TBC_RESPONSE_SENT:	Responder: immediate response to request sent
+ * @DECT_TBC_OTHER_WAIT:	Waiting for "other" message
+ * @DECT_TBC_ESTABLISHED	Established
+ * @DECT_TBC_RELEASING		First RELEASE message sent
+ * @DECT_TBC_RELEASED:		Second RELEASE message sent
+ */
+enum dect_tbc_state {
+	DECT_TBC_NONE,
+	DECT_TBC_REQ_SENT,
+	DECT_TBC_WAIT_RCVD,
+	DECT_TBC_REQ_RCVD,
+	DECT_TBC_RESPONSE_SENT,
+	DECT_TBC_OTHER_WAIT,
+	DECT_TBC_ESTABLISHED,
+	DECT_TBC_RELEASING,
+	DECT_TBC_RELEASED,
+};
+
+/**
+ * enum dect_tbc_enc_state - DECT Traffic Bearer encryption state
+ *
+ * @DECT_TBC_ENC_DISABLED:	 Encryption is disabled
+ * @DECT_TBC_ENC_START_REQ_RCVD: Start request received (FP)
+ * @DECT_TBC_ENC_START_REQ_SENT: Start request sent (PP)
+ * @DECT_TBC_ENC_START_CFM_RCVD: Start confirm received (PP)
+ * @DECT_TBC_ENC_START_CFM_SENT: Start confirm sent (FP)
+ * @DECT_TBC_ENC_ENABLED:	 Encryption is enabled
+ */
+enum dect_tbc_enc_state {
+	DECT_TBC_ENC_DISABLED,
+	DECT_TBC_ENC_START_REQ_RCVD,
+	DECT_TBC_ENC_START_REQ_SENT,
+	DECT_TBC_ENC_START_CFM_RCVD,
+	DECT_TBC_ENC_START_CFM_SENT,
+	DECT_TBC_ENC_ENABLED,
+};
+
+/**
+ * enum dect_tbc_event - DECT Traffic Bearer events
+ *
+ * @DECT_TBC_ACK_RECEIVED:	Acknowledgement for C_S data received
+ * @DECT_TBC_CIPHER_ENABLED:	Ciphering enabled
+ * @DECT_TBC_CIPHER_DISABLED:	Ciphering disabled
+ */
+enum dect_tbc_event {
+	DECT_TBC_ACK_RECEIVED,
+	DECT_TBC_CIPHER_ENABLED,
+	DECT_TBC_CIPHER_DISABLED,
+};
+
+/**
+ * struct dect_tbc - DECT Traffic Bearer Control
+ *
+ * @list:		Cell TBC list node
+ * @cell:		DECT cell
+ * @id:			Traffic Bearer ID
+ * @txb:		TX bearer
+ * @rxb:		RX bearer
+ * @state:		Bearer establishment state
+ * @tx_timer:		Transmit activation timer
+ * @wd_timer:		Receive watchdog timer
+ * @release_timer:	Release timer for unacknowledged release procedure
+ * @release_reason:	release reason
+ * @normal_tx_timer:	Normal transmit timer for C-channel/I_N normal delay transmission
+ * @normal_rx_timer:	Normal receive timer for C-channel/I_N normal delay delivery
+ * @rx_timer:		Mimimum delay receive timer
+ * @tx_timer:		Minimum delay transmit timer
+ * @ck:			Cipher key
+ * @enc_timer:		Encryption TX timer
+ * @enc_state:		Encryption state
+ * @enc_msg_cnt:	Encryption message retransmit counter
+ * @c_rx_skb:		C_S segment for delivery to DLC
+ * @c_tx_skb:		C_S segment for transmission in next TDMA frame
+ * @c_tx_ok:		C_S segment was successfully transmitted
+ * @b_rx_skb:		B-field data segment for delivery to DLC
+ * @b_tx_skb:		B-field data segment for transmission in next TDMA frame
+ * @bc:			Broadcast Control
+ */
+struct dect_tbc {
+	struct list_head		list;
+	struct dect_cell		*cell;
+	struct dect_tbc_id		id;
+	enum dect_mac_connection_types	type;
+	enum dect_mac_service_types	service;
+	bool				handover;
+
+	struct dect_bearer		txb;
+	struct dect_bearer		rxb;
+
+	enum dect_tbc_state		state;
+	struct dect_timer		wait_timer;
+	struct dect_timer		wd_timer;
+
+	struct dect_timer		release_timer;
+	enum dect_release_reasons	release_reason;
+
+	/* PP handover trigger */
+	s8				handover_tokens;
+
+	/* Encryption */
+	u64				ck;
+	struct dect_timer		enc_timer;
+	enum dect_tbc_enc_state		enc_state:8;
+	u8				enc_msg_cnt;
+
+	/* C_S channel */
+	struct sk_buff			*cs_tx_skb;
+	bool				cs_tx_ok;
+
+	/* I channel */
+	struct sk_buff			*b_tx_skb;
+
+	struct dect_bc			bc;
+};
+
+#define DECT_TBC_RFPI_TIMEOUT		(5 * DECT_FRAMES_PER_SECOND)
+
+#define DECT_TBC_HO_TOKENS_INITIAL	16
+#define DECT_TBC_HO_TOKENS_OK		1 /* Correct slot adds one token */
+#define DECT_TBC_HO_TOKENS_ERROR	8 /* Error slot subtracts eight tokens */
+#define DECT_TBC_HO_TOKENS_MAX		32
+
+enum dect_scan_status {
+	DECT_SCAN_FAIL,
+	DECT_SCAN_TIMEOUT,
+	DECT_SCAN_COMPLETE,
+};
+
+/**
+ * struct dect_dmb - Monitor Bearer
+ *
+ * @list:	cell dmbs list node
+ * @cell:	DECT cell
+ * @rxb1:	receive bearer 1
+ * @rxb2:	receive bearer 2
+ */
+struct dect_dmb {
+	struct list_head		list;
+	struct dect_cell		*cell;
+
+	struct dect_timer		wd_timer;
+	struct dect_bearer		rxb1;
+	struct dect_bearer		rxb2;
+	struct dect_bc			bc;
+};
+
+/**
+ * struct dect_irc - Idle receiver control
+ *
+ * @cell:		DECT cell
+ * @trx:		DECT transceiver
+ * @ari:		ARI filter
+ * @ari_mask:		ARI filter mask
+ * @idi:		identities information
+ * @si:			system information
+ * @notify:		notification callback
+ * @rx_scn:		Scan carrier number (RX time base)
+ * @tx_scn:		Scan carrier number (TX time base)
+ * @rx_frame_timer:	rx_scn update timer
+ * @tx_frame_timer:	tx_scn update timer
+ */
+struct dect_irc {
+	struct dect_cell	*cell;
+	struct dect_transceiver	*trx;
+
+	struct dect_llme_req	lreq;
+
+	struct dect_ari		ari;
+	struct dect_ari		ari_mask;
+
+	u16			timeout;
+	u16			rssi;
+	struct dect_idi		idi;
+	struct dect_si		si;
+
+	void			(*notify)(struct dect_cell *,
+					  struct dect_transceiver *,
+					  enum dect_scan_status);
+
+	u8			rx_scn;
+	u8			tx_scn;
+	struct dect_timer	rx_frame_timer;
+	struct dect_timer	tx_frame_timer;
+	struct dect_bearer	scan_bearer;
+};
+
+#define DECT_IRC_SCN_OFF	3
+
+struct dect_scan_result {
+	struct dect_llme_req	lreq;
+	struct dect_idi		idi;
+	struct dect_si		si;
+	u16			rssi;
+};
+
+/**
+ * struct dect_csf_ops - Cell Site Function ops
+ *
+ * @set_mode:			set cell to PP/FP mode
+ * @scan:			initiate scan for pari/pari_mask
+ * @preload:			preload system information
+ * @enable:			enable cell
+ * @page_request:		deliver paging message
+ * @tbc_initiate:		initiate a new connection
+ * @tbc_confirm:		confirm an incoming connection
+ * @tbc_release:		release a TBC
+ * @tbc_enc_key_request:	set encryption key
+ * @tbc_enc_eks_request:	enable/disable encryption
+ *
+ * The CSF ops define the interface in the direction CCF -> CSF.
+ */
+struct dect_cell_handle;
+struct dect_csf_ops {
+	int	(*set_mode)(const struct dect_cell_handle *,
+			    enum dect_cluster_modes);
+	int	(*scan)(const struct dect_cell_handle *,
+			const struct dect_llme_req *lreq,
+			const struct dect_ari *, const struct dect_ari *);
+	int	(*preload)(const struct dect_cell_handle *,
+			   const struct dect_ari *, u8,
+			   const struct dect_si *);
+	int	(*enable)(const struct dect_cell_handle *);
+
+	void	(*page_req)(const struct dect_cell_handle *, struct sk_buff *);
+
+	int	(*tbc_establish_req)(const struct dect_cell_handle *,
+				     const struct dect_tbc_id *,
+				     const struct dect_channel_desc *,
+				     enum dect_mac_service_types, bool);
+	int	(*tbc_establish_res)(const struct dect_cell_handle *,
+				     const struct dect_tbc_id *);
+	void	(*tbc_dis_req)(const struct dect_cell_handle *,
+			       const struct dect_tbc_id *,
+			       enum dect_release_reasons);
+	int	(*tbc_enc_key_req)(const struct dect_cell_handle *,
+				   const struct dect_tbc_id *, u64 ck);
+	int	(*tbc_enc_eks_req)(const struct dect_cell_handle *,
+				   const struct dect_tbc_id *,
+				   enum dect_cipher_states status);
+	int	(*tbc_enc_req)(const struct dect_cell_handle *,
+			       const struct dect_tbc_id *, u64 ck);
+	void	(*tbc_data_req)(const struct dect_cell_handle *,
+				const struct dect_tbc_id *,
+				enum dect_data_channels chan,
+				struct sk_buff *);
+
+};
+
+/**
+ * struct dect_cell_handle - DECT cluster view of a cell
+ *
+ * @list:		cluster cell list node
+ * @clh:		bound cluster handle
+ * @ops:		cell site function ops
+ * @rpn:		assigned radio part number
+ * @portref:		cell control protocol port reference (remote cells)
+ */
+struct dect_cell_handle {
+	struct list_head		list;
+	struct dect_cluster_handle	*clh;
+	const struct dect_csf_ops	*ops;
+	u8				rpn;
+
+	u32				portref;
+};
+
+enum dect_cell_states {
+	DECT_CELL_ENABLED		= 1 << 0,
+};
+
+/**
+ * struct dect_cell - DECT cell: one radio system
+ *
+ * @list:		cell list node
+ * @name:		cells' name
+ * @index:		unique numeric cell identifier
+ * @flags:		operational and status flags
+ * @handle:		cell handle
+ * @lock:		lock
+ * @mode:		operational mode (FP/PP)
+ * @state:		bitmask of enum dect_cell_states
+ * @idi:		FP System Identity
+ * @fmid:		FMID (Fixed MAC IDentity)
+ * @si:			FP System Information
+ * @timer_sync_stamp:	Time (multiframe number) of last multiframe number sync
+ * @a_rcv_stamp:	Time (jiffies) of last received A-Field with correct CRC
+ * @nt_rcv_stamp:	Time (jiffies) of last received Nt-Tail containing the PARI
+ * @bcs:		Broadcast Controllers
+ * @cbc:		Connectionless Bearer Controller
+ * @dbcs:		Dummy Bearer Controllers
+ * @tbcs:		list of Traffic Bearer Controllers
+ * @tbc_num_est:	Number of TBCs in ESTABLISHED state
+ * @tbc_last_chd:	Channel description of last TBC leaving ESTABLISHED state
+ * @dmbs:		list of Monitor Bearers
+ * @chanlists:		list of channel lists for different channel types
+ * @timer_base:		RX/TX timer bases
+ * @trg:		DECT transceiver group
+ */
+struct dect_cell {
+	struct list_head		list;
+	char				name[DECTNAMSIZ];
+	u32				index;
+	u32				flags;
+
+	struct dect_cell_handle		handle;
+
+	spinlock_t			lock;
+	enum dect_cluster_modes		mode;
+	u32				state;
+
+	/* identities */
+	struct dect_idi			idi;
+	u16				fmid;
+
+	/* system information */
+	struct dect_si			si;
+	u32				blind_full_slots;
+
+	/* PP state maintenance */
+	u32				timer_sync_stamp;
+	unsigned long			a_rcv_stamp;
+	unsigned long			nt_rcv_stamp;
+
+	/* Broadcast controllers and related data */
+	struct dect_timer		page_timer;
+	struct sk_buff_head		page_queue;
+	struct sk_buff_head		page_fast_queue;
+
+	struct sk_buff			*page_sdu;
+	struct sk_buff_head		page_tx_queue;
+
+	struct list_head		bcs;
+	unsigned int			si_idx;
+	unsigned long			bfs_xmit_stamp;
+
+	struct dect_cbc			cbc;
+	struct list_head		dbcs;
+
+	u32				tbei_rover;
+	struct list_head		tbcs;
+	unsigned int			tbc_num_est;
+	struct dect_channel_desc	tbc_last_chd;
+
+	struct list_head		dmbs;
+
+	/* channel lists */
+	struct list_head		chl_pending;
+	struct list_head		chanlists;
+	struct dect_channel_list	*chl_next;
+	struct dect_channel_list	*chl;
+
+	/* raw transmission queue */
+	struct sk_buff_head		raw_tx_queue;
+
+	struct dect_timer_base		timer_base[DECT_TIMER_BASE_MAX + 1];
+	struct dect_transceiver_group	trg;
+	u32				trg_blind_full_slots;
+};
+
+#define DECT_CELL_TIMER_RESYNC_TIMEOUT	8		/* T216: 8 multiframes */
+#define DECT_CELL_A_RCV_TIMEOUT		(5 * HZ)	/* T207: 5 seconds */
+#define DECT_CELL_NT_RCV_TIMEOUT	(20 * HZ)	/* T208: 20 seconds */
+
+#define dect_foreach_transmit_slot(slot, end, cell) \
+	for ((slot) = dect_normal_transmit_base((cell)->mode), \
+	     (end) = (slot) + DECT_HALF_FRAME_SIZE; \
+	     (slot) < (end); (slot)++)
+
+#define dect_foreach_receive_slot(slot, end, cell) \
+	for ((slot) = dect_normal_receive_base((cell)->mode), \
+	     (end) = (slot) + DECT_HALF_FRAME_SIZE; \
+	     (slot) < (end); (slot)++)
+
+extern struct dect_cell *dect_cell_get_by_index(u32 index);
+
+extern int dect_cell_attach_transceiver(struct dect_cell *cell,
+					struct dect_transceiver *trx);
+extern void dect_cell_detach_transceiver(struct dect_cell *cell,
+					 struct dect_transceiver *trx);
+
+extern void dect_mac_rcv(struct dect_transceiver *trx,
+			 struct dect_transceiver_slot *ts,
+			 struct sk_buff *skb);
+extern void dect_mac_report_rssi(struct dect_transceiver *trx,
+				 struct dect_transceiver_slot *ts, u8 rssi);
+extern void dect_mac_rx_tick(struct dect_transceiver_group *grp, u8 slot);
+extern void dect_mac_tx_tick(struct dect_transceiver_group *grp, u8 slot);
+
+extern void dect_mac_irc_rcv(struct dect_transceiver *trx, struct sk_buff *skb);
+extern void dect_mac_irc_tick(struct dect_transceiver *trx);
+
+#endif /* _NET_DECT_MAC_CSF_H */
diff --git a/target/linux/generic/files/include/net/dect/transceiver.h b/target/linux/generic/files/include/net/dect/transceiver.h
new file mode 100644
index 0000000..f5d95d1
--- /dev/null
+++ b/target/linux/generic/files/include/net/dect/transceiver.h
@@ -0,0 +1,726 @@
+/*
+ * DECT Transceiver Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ */
+
+#ifndef _NET_DECT_TRANSCEIVER_H
+#define _NET_DECT_TRANSCEIVER_H
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/dect.h>
+#include <linux/dect_netlink.h>
+
+#define DECT_RSSI_RANGE			255
+#define DECT_RSSI_DBM_LOW		-93
+#define DECT_RSSI_DBM_RANGE		60
+
+static inline u8 dect_dbm_to_rssi_rel(s8 dbm)
+{
+	return dbm * DECT_RSSI_RANGE / DECT_RSSI_DBM_RANGE;
+}
+
+static inline u8 dect_dbm_to_rssi(s8 dbm)
+{
+	return dect_dbm_to_rssi_rel(dbm - DECT_RSSI_DBM_LOW);
+}
+
+#define DECT_RSSI_AVG_SCALE		3
+
+static inline u16 dect_average_rssi(u16 cur, u16 sample)
+{
+	if (cur == 0)
+		cur = sample << DECT_RSSI_AVG_SCALE;
+	else {
+		cur -= cur >> DECT_RSSI_AVG_SCALE;
+		cur += sample;
+	}
+	return cur;
+}
+
+#define DECT_CARRIER_NUM		64
+
+static inline u8 dect_next_carrier(u64 rfcars, u8 carrier)
+{
+	u64 tmp;
+
+	if (WARN_ON(rfcars == 0))
+		return 0;
+	tmp = rfcars & ~((1ULL << (carrier + 1)) - 1);
+	if (tmp == 0)
+		tmp = rfcars;
+	return ffs(tmp) - 1;
+}
+
+static inline u8 dect_prev_carrier(u64 rfcars, u8 carrier)
+{
+	u64 tmp;
+
+	if (WARN_ON(rfcars == 0))
+		return 0;
+	tmp = rfcars & ((1ULL << carrier) - 1);
+	if (tmp == 0)
+		tmp = rfcars;
+	return fls(tmp) - 1;
+}
+
+static inline u8 dect_carrier_sub(u64 rfcars, u8 carrier, u8 n)
+{
+	while (n != 0) {
+		carrier = dect_prev_carrier(rfcars, carrier);
+		n--;
+	}
+	return carrier;
+}
+
+static inline u8 dect_carrier_distance(u64 rfcars, u8 from, u8 to)
+{
+	if (from >= to) {
+		/* clear bits between to and from */
+		rfcars &= ~(((1ULL << (from - to)) - 1) << to);
+	} else {
+		/* clear bits not between from and to */
+		rfcars &= ((1ULL << (to - from)) - 1) << from;
+	}
+	return hweight64(rfcars);
+}
+
+#define DECT_PHASE_OFFSET_EWMA_LOG	(DECT_PHASE_OFFSET_SCALE / 4)
+
+static inline s32 dect_average_phase_offset(s32 cur, s32 phaseoff)
+{
+	cur -= cur / DECT_PHASE_OFFSET_EWMA_LOG;
+	cur += phaseoff / DECT_PHASE_OFFSET_EWMA_LOG;
+	return cur;
+}
+
+#define DECT_BAND_NUM			32
+#define DECT_DEFAULT_BAND		0
+
+#define DECT_FREQUENCY_F0		1897344	/* kHz */
+#define DECT_CARRIER_WIDTH		1728	/* kHz */
+
+/**
+ * struct dect_band - DECT RF-band
+ *
+ * @band:		RF-band number
+ * @carriers:		number of defined carriers
+ * @frequency:		frequency of each carrier in kHz
+ */
+struct dect_band {
+	u8	band;
+	u8	carriers;
+	u32	frequency[];
+};
+
+#define DECT_FRAME_SIZE			24
+#define DECT_HALF_FRAME_SIZE		(DECT_FRAME_SIZE / 2)
+#define DECT_FRAMES_PER_SECOND		100
+
+#define DECT_SCAN_SLOT			0
+#define DECT_SLOT_MASK			0x00ffffff
+
+static inline u8 dect_next_slotnum(u8 slot)
+{
+	if (++slot == DECT_FRAME_SIZE)
+		slot = 0;
+	return slot;
+}
+
+static inline u8 dect_prev_slotnum(u8 slot)
+{
+	if (slot == 0)
+		slot = DECT_FRAME_SIZE;
+	return slot - 1;
+}
+
+static inline u8 dect_slot_add(u8 s1, u8 s2)
+{
+	return (s1 + s2) % DECT_FRAME_SIZE;
+}
+
+static inline u8 dect_slot_sub(u8 s1, u8 s2)
+{
+	return s1 >= s2 ? s1 - s2 : DECT_FRAME_SIZE + s1 - s2;
+}
+
+static inline u8 dect_slot_distance(u8 s1, u8 s2)
+{
+	return s2 >= s1 ? s2 - s1 : DECT_FRAME_SIZE + s2 - s1;
+}
+
+#define dect_foreach_slot(slot) \
+	for ((slot) = 0; (slot) < DECT_FRAME_SIZE; (slot)++)
+
+static inline u8 dect_normal_transmit_base(enum dect_cluster_modes mode)
+{
+	return mode == DECT_MODE_FP ? 0 : DECT_HALF_FRAME_SIZE;
+}
+
+static inline u8 dect_normal_receive_base(enum dect_cluster_modes mode)
+{
+	return mode == DECT_MODE_FP ? DECT_HALF_FRAME_SIZE : 0;
+}
+
+static inline u8 dect_normal_receive_end(enum dect_cluster_modes mode)
+{
+	return mode == DECT_MODE_FP ? DECT_FRAME_SIZE - 1 :
+				      DECT_HALF_FRAME_SIZE - 1;
+}
+
+static inline u8 dect_tdd_slot(u8 slot)
+{
+	return slot < DECT_HALF_FRAME_SIZE ? slot + DECT_HALF_FRAME_SIZE :
+					     slot - DECT_HALF_FRAME_SIZE;
+}
+
+/**
+ * enum dect_slot_types - DECT slot types
+ *
+ * @DECT_FULL_SLOT:		Full-slot format (480 bits)
+ * @DECT_HALF_SLOT:		Half-slot format (240 bits)
+ * @DECT_DOUBLE_SLOT:		Double-slot format (960 bits)
+ * @DECT_LONG_SLOT_j640:	Long slot format j=640 (800 bits)
+ * @DECT_LONG_SLOT_j672:	Long slot format j=672 (832 bits)
+ *
+ * The numeric values must match the MAC-layer attributes-T coding.
+ */
+enum dect_slot_types {
+	DECT_FULL_SLOT		= 0x0,
+	DECT_HALF_SLOT		= 0x1,
+	DECT_DOUBLE_SLOT	= 0x2,
+	DECT_LONG_SLOT_640	= 0x3,
+	DECT_LONG_SLOT_672	= 0x4,
+};
+
+enum dect_packet_sizes {
+	DECT_P00_SIZE		= 12,
+	DECT_P08_SIZE		= 23,
+	DECT_P32_SIZE		= 53,
+	DECT_P640j_SIZE		= 89,
+	DECT_P672j_SIZE		= 93,
+	DECT_P80_SIZE		= 113,
+};
+
+#define DECT_PREAMBLE_SIZE	4
+
+/**
+ * enum dect_checksum - DECT hardware checksum results
+ *
+ * @DECT_CHECKSUM_A_CRC_OK:	A-field R-CRC OK
+ * @DECT_CHECKSUM_X_CRC_OK:	Unprotected B-field X-CRC OK
+ * @DECT_CHECKSUM_Z_CRC_OK:	Z-field OK
+ */
+enum dect_checksum {
+	DECT_CHECKSUM_A_CRC_OK	= 0x1,
+	DECT_CHECKSUM_X_CRC_OK	= 0x2,
+	DECT_CHECKSUM_Z_CRC_OK	= 0x4,
+};
+
+/**
+ * enum dect_b_formats - DECT B-Field formats
+ *
+ * @DECT_B_NONE:	No B-field
+ * @DECT_B_UNPROTECTED:	Unprotected B-field format
+ * @DECT_B_PROTECTED:	Protected B-field format
+ *
+ * The B-Field format can be used by a transceiver for offloading X-CRC
+ * calculation.
+ */
+enum dect_b_formats {
+	DECT_B_NONE,
+	DECT_B_UNPROTECTED,
+	DECT_B_PROTECTED,
+	__DECT_B_MAX
+};
+#define DECT_B_MAX		(__DECT_B_MAX - 1)
+
+/**
+ * struct dect_channel_desc - DECT physical channel description
+ *
+ * @pkt:	Packet type in use
+ * @b_fmt:	B-Field format for checksum offloading
+ * @slot:	Slot number
+ * @carrier:	RF-carrier number
+ */
+struct dect_channel_desc {
+	enum dect_packet_types		pkt:8;
+	enum dect_b_formats		b_fmt:8;
+	u8				slot;
+	u8				carrier;
+};
+
+enum dect_channel_priv_flags {
+	DECT_SLOT_RAW_TX		= 0x1,
+};
+
+/**
+ * struct dect_transceiver_slot - Transceiver TDMA slot
+ *
+ * @flags:		slot flags
+ * @priv_flags:		internally used flags
+ * @state:		current state
+ * @desc:		channel description
+ * @bearer:		associated bearer
+ * @ck:			cipher key
+ * @phaseoff:		measured phase offset
+ * @rssi:		averaged RSSI
+ * @rx_bytes:		RX byte count
+ * @rx_packets:		RX packet count
+ * @rx_a_crc_errors:	RX A-field CRC errors
+ * @tx_bytes:		TX byte count
+ * @tx_packets:		TX packet count
+ */
+struct dect_transceiver_slot {
+	u8				flags;
+	u8				priv_flags;
+	u8				blinded;
+	enum dect_slot_states		state:8;
+	struct dect_channel_desc	chd;
+	struct dect_bearer		*bearer;
+	u64				ck;
+
+	s32				phaseoff;
+	u16				rssi;
+	u32				rx_bytes;
+	u32				rx_packets;
+	u32				rx_a_crc_errors;
+	u32				rx_x_crc_errors;
+	u32				rx_z_crc_errors;
+	u32				tx_bytes;
+	u32				tx_packets;
+};
+
+/**
+ * struct dect_transceiver_event - one atomic unit of work for the MAC layer
+ *
+ * @trx:		transceiver
+ * @busy:		synchronizer
+ * @list:		transceiver group events list node
+ * @rx_queue:		received packets
+ * @rssi:		RSSI measurement in scanning slots
+ * @rssi_mask:		RSSI measurement positions
+ * @slotpos:		transceiver slot position in TDMA frame
+ *
+ * A transceiver operates asynchronously to the MAC layer, but the MAC layer's
+ * timing needs to be strictly synchronized to the receiver.
+ *
+ * This structure contains the packets from multiple consequitive slots received
+ * by the receiver in one unit (up to ops->eventrate frames). Slotpos specifies
+ * the transceivers current position in the TDMA frame (== the minimum current
+ * time) and is used for timing purposes and slot maintenance operations of the
+ * upcoming slots. A transceiver uses a fixed amount of these structure and
+ * synchronizes with BH processing through the busy marker. When BH processing
+ * is too slow, frames are dropped.
+ */
+struct dect_transceiver_event {
+	struct dect_transceiver	*trx;
+	atomic_t		busy;
+	struct list_head	list;
+	struct sk_buff_head	rx_queue;
+	u8			rssi[DECT_HALF_FRAME_SIZE / 2];
+	u8			rssi_mask;
+	u8			slotpos;
+};
+
+/**
+ * struct dect_skb_trx_cb - DECT Transceiver skb control block
+ *
+ * @trx:		transceiver
+ * @mfn:		multiframe number
+ * @frame:		frame number
+ * @slot:		slot number
+ * @lbn:		logical bearer number
+ * @csum:		checksum results
+ * @rssi:		RSSI measurement
+ */
+struct dect_skb_trx_cb {
+	struct dect_transceiver	*trx;
+	u32			mfn;
+	u8			frame;
+	u8			slot;
+	u8			lbn;
+	u8			csum;
+	u8			rssi;
+};
+
+static inline struct dect_skb_trx_cb *DECT_TRX_CB(const struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(struct dect_skb_trx_cb) > sizeof(skb->cb));
+	return (struct dect_skb_trx_cb *)skb->cb;
+}
+
+/**
+ * struct dect_transceiver_ops - DECT transceiver operations
+ *
+ * @disable:		shut the transceiver down
+ * @enable:		bring the transceiver to operational state
+ * @confirm:		confirm a received signal in slave mode
+ * @unlock:		release a confirmed signal again
+ * @lock:		lock to a signal
+ * @set_mode:		set the mode (RX/TX/SCANNING) for a slot
+ * @set_carrier:	set the RF-carrier for a slot
+ * @set_band:		set the RF-band
+ * @destructor:		destructor
+ * @name		transceiver driver name
+ * @features:		transceiver features
+ * @eventrate:		rate at which slot events are generated, must be integral
+ * 			divisor of the number of slots per TDMA half frame
+ * @latency:		latency in slots until updates for a slot take effect
+ *
+ * A transceiver provides frame reception and transmission, signal strength
+ * measurement as well as a reference clock for the MAC layer. It can exist
+ * in two basic states:
+ *
+ * - master: doesn't need initial synchronization to a radio signal
+ * - slave: needs to synchronize timing with a signal
+ *
+ * Only the first transceiver of a FP is a master, PPs are always slaves to
+ * a FPs timing. Secondary and further transceivers of a FP also start as
+ * slaves until they have synchronized to one of the already running
+ * transceivers.
+ *
+ * Locking to a new signal works in multiple phases:
+ *
+ * 1) The ->enable() callback is invoked. The driver is expected to initiate a
+ *    scan for a signal, during which it will pass on any received frame to the
+ *    transceiver layer. As no framing has been established, all packets should
+ *    indicate a slot number of zero.
+ *
+ * 2) While scanning for a signal, the ->set_carrier() callback may be invoked
+ *    with a slot number of zero. The driver is expected to adjust the carrier
+ *    on which it is scanning for a signal.
+ *
+ * 3) When the MAC layer determines interest in a received signal, the ->confirm()
+ *    callback is invoked. The driver is expected to continue to pass frames from
+ *    this signal to the MAC layer to establish framing.
+ *
+ * 3a) When the MAC layer is only collecting information for a scan, it may call
+ *     the ->unlock callback to release a previously confirmed signal.
+ *
+ * 4) Once the MAC layer has determined framing relative to the slot timing, the
+ *    ->lock() callback is invoked. At this point, only a single physical channel
+ *    is received. The driver should synchronize the hardware to the framing to
+ *    make it interrupt at the appropriate times.
+ *
+ */
+struct dect_transceiver;
+struct dect_transceiver_ops {
+	void		(*disable)(const struct dect_transceiver *trx);
+	void		(*enable)(const struct dect_transceiver *trx);
+
+	void		(*confirm)(const struct dect_transceiver *trx);
+	void		(*unlock)(const struct dect_transceiver *trx);
+	void		(*lock)(const struct dect_transceiver *trx, u8 slot);
+
+	void		(*set_mode)(const struct dect_transceiver *trx,
+				    const struct dect_channel_desc *chd,
+				    enum dect_slot_states mode);
+	void		(*set_carrier)(const struct dect_transceiver *trx,
+				       u8 slot, u8 carrier);
+	void		(*tx)(const struct dect_transceiver *trx,
+			      struct sk_buff *skb);
+
+	u64		(*set_band)(const struct dect_transceiver *trx,
+				    const struct dect_band *band);
+	void		(*destructor)(struct dect_transceiver *trx);
+	const char	*name;
+
+	u32		features;
+	u8		eventrate;
+	u8		latency;
+};
+
+/**
+ * enum dect_transceiver_modes - Transceiver synchronization modes
+ *
+ * @DECT_TRANSCEIVER_MASTER:	Transceiver determines reference time (FP)
+ * @DECT_TRANSCEIVER_SLAVE:	Transceiver is slave to foreign reference timing
+ */
+enum dect_transceiver_modes {
+	DECT_TRANSCEIVER_MASTER,
+	DECT_TRANSCEIVER_SLAVE,
+};
+
+/**
+ * enum dect_transceiver_states - transceiver synchronization states
+ *
+ * @DECT_TRANSCEIVER_STOPPED:		transceiver is inactive
+ * @DECT_TRANSCEIVER_UNLOCKED:		transceiver is not synchronized to any RFP
+ * @DECT_TRANSCEIVER_LOCK_PENDING:	transceiver is receiving RFP transmissions,
+ * 					but has not obtained frame synchonization
+ * @DECT_TRANSCEIVER_LOCKED:		the transceiver has achieved frame and
+ * 					multiframe lock to an RFP
+ *
+ * These correspond to the ETS 300 175-3 Annex D PT MAC layer states, but are
+ * per transceiver as we also need to synchronize secondary transceivers.
+ */
+enum dect_transceiver_states {
+	DECT_TRANSCEIVER_STOPPED,
+	DECT_TRANSCEIVER_UNLOCKED,
+	DECT_TRANSCEIVER_LOCK_PENDING,
+	DECT_TRANSCEIVER_LOCKED,
+};
+
+/**
+ * struct dect_transceiver_stats - transceiver statistics
+ *
+ * @event_busy:		events lost due to MAC layer busy
+ * @event_late:		events lost due to transceiver late
+ */
+struct dect_transceiver_stats {
+	u32					event_busy;
+	u32					event_late;
+};
+
+/**
+ * struct dect_transceiver - DECT transceiver
+ *
+ * @list:		transceiver list node
+ * @ops:		transceiver ops
+ * @name:		transceiver identity
+ * @stats:		transceiver statistics
+ * @mode:		synchronization mode
+ * @state:		synchronization state
+ * @band:		current RF-band
+ * @carriers:		bitmask of supported carriers in the current band
+ * @slots:		transceiver slot state
+ * @index:		cell transceiver index
+ * @segno:		transceiver receive sequence number
+ * @cell:		cell the transceiver is assigned to
+ * @irc:		idle receiver control
+ * @event:		dynamic amount of transceiver event structures
+ *
+ * Following the event structures is the private driver data.
+ */
+struct dect_transceiver {
+	struct list_head			list;
+	const struct dect_transceiver_ops	*ops;
+	char					name[DECTNAMSIZ];
+
+	struct dect_transceiver_stats		stats;
+	enum dect_transceiver_modes		mode;
+	enum dect_transceiver_states		state;
+
+	const struct dect_band			*band;
+	u64					carriers;
+
+	struct dect_transceiver_slot		slots[DECT_FRAME_SIZE];
+	u32					blind_full_slots;
+
+	u8					index;
+	u32					seqno;
+	struct dect_cell			*cell;
+	struct dect_irc				*irc;
+	struct dect_transceiver_event		event[];
+};
+
+static inline void *dect_transceiver_priv(const struct dect_transceiver *trx)
+{
+	return (void *)&trx->event[DECT_HALF_FRAME_SIZE / trx->ops->eventrate];
+}
+
+extern struct dect_transceiver *dect_transceiver_alloc(const struct dect_transceiver_ops *ops,
+						       unsigned int priv);
+extern void dect_transceiver_free(struct dect_transceiver *trx);
+extern int dect_register_transceiver(struct dect_transceiver *trx);
+extern void dect_unregister_transceiver(struct dect_transceiver *trx);
+
+extern void dect_transceiver_enable(struct dect_transceiver *trx);
+extern void dect_transceiver_disable(struct dect_transceiver *trx);
+
+extern void dect_transceiver_confirm(struct dect_transceiver *trx);
+extern void dect_transceiver_unlock(struct dect_transceiver *trx);
+extern void dect_transceiver_lock(struct dect_transceiver *trx, u8 slot);
+
+extern int dect_transceiver_set_band(struct dect_transceiver *trx, u8 bandnum);
+
+static inline void dect_set_channel_mode(struct dect_transceiver *trx,
+					 const struct dect_channel_desc *chd,
+					 enum dect_slot_states mode)
+{
+	trx->ops->set_mode(trx, chd, mode);
+	trx->slots[chd->slot].state = mode;
+	trx->slots[chd->slot].chd.pkt = chd->pkt;
+	trx->slots[chd->slot].chd.b_fmt = chd->b_fmt;
+}
+
+static inline void dect_set_carrier(struct dect_transceiver *trx,
+				    u8 slot, u8 carrier)
+{
+	trx->slots[slot].chd.carrier = carrier;
+	trx->slots[slot].rssi	  = 0;
+	trx->slots[slot].phaseoff = 0;
+	trx->ops->set_carrier(trx, slot, carrier);
+}
+
+static inline void dect_set_flags(struct dect_transceiver *trx, u8 slot, u32 flags)
+{
+	trx->slots[slot].flags |= flags;
+	trx->ops->set_mode(trx, &trx->slots[slot].chd, trx->slots[slot].state);
+}
+
+static inline void dect_clear_flags(struct dect_transceiver *trx, u8 slot, u32 flags)
+{
+	trx->slots[slot].flags &= ~flags;
+	trx->ops->set_mode(trx, &trx->slots[slot].chd, trx->slots[slot].state);
+}
+
+static inline void dect_enable_cipher(struct dect_transceiver *trx,
+				      u8 slot, u64 ck)
+{
+	trx->slots[slot].ck = ck;
+	dect_set_flags(trx, slot, DECT_SLOT_CIPHER);
+}
+
+static inline void dect_disable_cipher(struct dect_transceiver *trx, u8 slot)
+{
+	dect_clear_flags(trx, slot, DECT_SLOT_CIPHER);
+	trx->slots[slot].ck = 0;
+}
+
+static inline void dect_transceiver_tx(struct dect_transceiver *trx,
+				       struct sk_buff *skb)
+{
+	u8 slot = DECT_TRX_CB(skb)->slot;
+
+	trx->slots[slot].tx_bytes += skb->len;
+	trx->slots[slot].tx_packets++;
+	trx->ops->tx(trx, skb);
+}
+
+extern struct sk_buff *dect_transceiver_alloc_skb(struct dect_transceiver *trx, u8 slot);
+
+static inline struct dect_transceiver_event *
+dect_transceiver_event(struct dect_transceiver *trx, u8 n, u8 slotpos)
+{
+	struct dect_transceiver_event *event;
+
+	event = &trx->event[n];
+	if (unlikely(!atomic_add_unless(&event->busy, 1, 1))) {
+		trx->stats.event_busy++;
+		return NULL;
+	}
+	event->slotpos = slotpos;
+	return event;
+}
+
+static inline void dect_transceiver_record_rssi(struct dect_transceiver_event *event,
+						u8 slot, u8 rssi)
+{
+	u8 idx;
+
+	idx = slot % event->trx->ops->eventrate;
+	event->rssi[idx] = rssi;
+	event->rssi_mask |= 1 << idx;
+}
+
+static inline void dect_release_transceiver_event(struct dect_transceiver_event *event)
+{
+	event->rssi_mask = 0;
+	smp_mb__before_atomic_dec();
+	atomic_dec(&event->busy);
+}
+
+enum dect_transceiver_events {
+	DECT_TRANSCEIVER_REGISTER,
+	DECT_TRANSCEIVER_UNREGISTER,
+};
+
+#define DECT_TRX_GROUP_MAX	16
+
+/**
+ * struct dect_transceiver_group
+ *
+ * @trx:		Transceiver array
+ * @trxmask:		Mask of present transceivers
+ * @latency:		Maximum latency of all transceivers
+ * @blind_full_slots:	combined blind full slots state of all transceivers
+ * @tasklet:		Event processing tasklet
+ * @lock:		Event list lock
+ * @events:		List of queued events
+ * @seqno:		Transceiver event loss detection
+ * @slot_low:		First unhandled slot
+ * @slot_high:		First slot after slot window
+ * @slots:		merged events for window slot_low - slot_high
+ */
+struct dect_transceiver_group {
+	struct dect_transceiver			*trx[DECT_TRX_GROUP_MAX];
+	u16					trxmask;
+	u8					latency;
+	u32					blind_full_slots;
+
+	struct tasklet_struct			tasklet;
+	spinlock_t				lock;
+	struct list_head			events;
+
+	u32					seqno;
+	u8					slot_low;
+	u8					slot_high;
+	struct {
+		struct sk_buff_head		queue;
+		u16				mask;
+		u8				rssi[DECT_TRX_GROUP_MAX];
+	} slots[DECT_HALF_FRAME_SIZE];
+};
+
+extern void dect_transceiver_group_init(struct dect_transceiver_group *trg);
+extern int dect_transceiver_group_add(struct dect_transceiver_group *trg,
+				      struct dect_transceiver *trx);
+extern void dect_transceiver_group_remove(struct dect_transceiver_group *trg,
+					  struct dect_transceiver *trx);
+
+extern bool dect_transceiver_channel_available(const struct dect_transceiver *trx,
+					       const struct dect_channel_desc *chd);
+extern bool dect_transceiver_reserve(struct dect_transceiver_group *trg,
+				     struct dect_transceiver *trx,
+				     const struct dect_channel_desc *chd);
+extern bool dect_transceiver_release(struct dect_transceiver_group *trg,
+				     struct dect_transceiver *trx,
+				     const struct dect_channel_desc *chd);
+
+extern void dect_transceiver_queue_event(struct dect_transceiver *trx,
+					 struct dect_transceiver_event *ev);
+
+#define dect_first_transceiver(trg)					\
+({									\
+	struct dect_transceiver_group *_trg = (void *)(trg);		\
+	u32 mask = _trg->trxmask;					\
+	mask ? (_trg)->trx[ffs(mask) - 1] : NULL; })
+
+#define dect_next_transceiver(trx, trg)					\
+({									\
+	struct dect_transceiver_group *_trg = (void *)(trg);		\
+	u32 mask = _trg->trxmask;					\
+	mask &= ~((1 << ((trx)->index + 1)) - 1);			\
+	mask ? (_trg)->trx[ffs(mask) - 1] : NULL; })
+
+#define dect_foreach_transceiver(trx, trg)				\
+	for ((trx) = dect_first_transceiver(trg);			\
+	     (trx) != NULL;						\
+	     (trx) = dect_next_transceiver(trx, trg))
+
+#define dect_last_transceiver(trg)					\
+({									\
+	struct dect_transceiver_group *_trg = (void *)(trg);		\
+	u32 mask = _trg->trxmask;					\
+	mask ? (_trg)->trx[fls(mask) - 1] : NULL; })
+
+#define dect_prev_transceiver(trx, trg)					\
+({									\
+	struct dect_transceiver_group *_trg = (void *)(trg);		\
+	u32 mask = _trg->trxmask;					\
+	mask &= (1 << (trx)->index) - 1;				\
+	mask ? (_trg)->trx[fls(mask) - 1] : NULL; })
+
+#define dect_foreach_transceiver_reverse(trx, trg)			\
+	for ((trx) = dect_last_transceiver(trg);			\
+	     (trx) != NULL;						\
+	     (trx) = dect_prev_transceiver(trx, trg))
+
+extern int dect_transceiver_module_init(void);
+extern void dect_transceiver_module_exit(void);
+
+#endif /* _NET_DECT_TRANSCEIVER_H */
diff --git a/target/linux/generic/files/net/dect/Kconfig b/target/linux/generic/files/net/dect/Kconfig
new file mode 100644
index 0000000..7e836a9
--- /dev/null
+++ b/target/linux/generic/files/net/dect/Kconfig
@@ -0,0 +1,66 @@
+menuconfig DECT
+	tristate "DECT protocol support"
+	help
+	  This option enables support for the DECT protocol.
+
+	  If unsure, say N.
+
+if DECT
+
+config DECT_DEBUG
+	bool "DECT debugging"
+	help
+	  This option enables support for debugging in the DECT modules.
+
+	  If unsure, say N.
+
+config DECT_CSF
+	tristate "DECT Cell Site Functions (CSF) support"
+	help
+	  This option enables support for DECT Cell Site Functions. A DECT
+	  cell is a radio endpoint containing one or more transceivers.
+
+	  If unsure, say N.
+
+config DECT_RAW
+	tristate "DECT raw sockets"
+	depends on DECT_CSF
+	help
+	  This option enables support for PF_DECT raw sockets. DECT raw
+	  sockets are used to receive raw frames from DECT devices.
+
+	  If unsure, say N.
+
+config DECT_CCF
+	tristate "DECT Cluster Control Functions (CCF) support"
+	help
+	  This option enables support for the DECT Cluster Control Functions.
+
+	  A DECT cluster is a Portable radio Termination (PT), containing a
+	  single cell, or a Fixed radio Termination (FT), containing up to
+	  8 cells.
+
+	  If unsure, say N.
+
+config DECT_LU1_SAP
+	tristate "DECT DLC LU1 SAP sockets"
+	select DECT_CCF
+	help
+	  This option enables support for PF_DECT DLC LU1 SAP sockets. DECT
+	  DLC LU1 SAP sockets are used for the TRUP (TRansparent UnProtected)
+	  service, most commonly used for audio.
+
+	  If unsure, say N.
+
+config DECT_CCP
+	bool "DECT Cell Control Protocol support"
+	depends on ( DECT_CSF || DECT_CCF ) && BROKEN
+	select TIPC
+	help
+	  This option enables support for the DECT Cell Control Protocol.
+	  This can be used to remotely control one or multiple DECT cells
+	  by the DECT cluster control functions.
+
+	  If unsure, say N.
+
+endif
diff --git a/target/linux/generic/files/net/dect/Makefile b/target/linux/generic/files/net/dect/Makefile
new file mode 100644
index 0000000..7d7bb14
--- /dev/null
+++ b/target/linux/generic/files/net/dect/Makefile
@@ -0,0 +1,17 @@
+dect-y				+= core.o identities.o dect_netlink.o af_dect.o
+
+dect_csf-y			+= mac_csf.o transceiver.o
+
+dect_ccf-y			+= mac_ccf.o dsc.o
+dect_ccf-y			+= dlc.o
+dect_ccf-y			+= dlc_cplane.o dlc_b_sap.o dlc_s_sap.o
+dect_ccf-y			+= dlc_uplane.o
+dect_ccf-$(CONFIG_DECT_CCP)	+= ccp.o
+
+dect_raw-y			+= raw.o
+
+obj-$(CONFIG_DECT)		+= dect.o
+obj-$(CONFIG_DECT_CSF)		+= dect_csf.o
+obj-$(CONFIG_DECT_RAW)		+= dect_raw.o
+obj-$(CONFIG_DECT_CCF)		+= dect_ccf.o
+obj-$(CONFIG_DECT_LU1_SAP)	+= dlc_lu1_sap.o
diff --git a/target/linux/generic/files/net/dect/af_dect.c b/target/linux/generic/files/net/dect/af_dect.c
new file mode 100644
index 0000000..452df7f
--- /dev/null
+++ b/target/linux/generic/files/net/dect/af_dect.c
@@ -0,0 +1,456 @@
+/*
+ * DECT sockets
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/poll.h>
+#include <linux/dect.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+
+static struct dect_proto *dect_protos[DECT_PROTO_NUM];
+static DEFINE_SPINLOCK(dect_proto_lock);
+
+void (*dect_raw_rcv_hook)(struct sk_buff *skb);
+EXPORT_SYMBOL_GPL(dect_raw_rcv_hook);
+
+int dect_proto_register(struct dect_proto *proto)
+{
+	int err;
+
+	err = proto_register(&proto->proto, true);
+	if (err < 0)
+		return err;
+
+	spin_lock(&dect_proto_lock);
+	dect_protos[proto->protocol] = proto;
+	spin_unlock(&dect_proto_lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dect_proto_register);
+
+void dect_proto_unregister(struct dect_proto *proto)
+{
+	spin_lock(&dect_proto_lock);
+	dect_protos[proto->protocol] = NULL;
+	spin_unlock(&dect_proto_lock);
+	proto_unregister(&proto->proto);
+}
+EXPORT_SYMBOL_GPL(dect_proto_unregister);
+
+struct sk_buff *dect_alloc_notification(u32 type, const void *data,
+					unsigned int size)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+	DECT_NOTIFY_CB(skb)->type = type;
+	memcpy(skb_put(skb, size), data, size);
+	return skb;
+}
+EXPORT_SYMBOL_GPL(dect_alloc_notification);
+
+static void dect_destruct(struct sock *sk)
+{
+	__skb_queue_purge(&sk->sk_receive_queue);
+	__skb_queue_purge(&sk->sk_error_queue);
+	__skb_queue_purge(&sk->sk_write_queue);
+}
+
+static int dect_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	long timeout;
+
+	if (sk == NULL)
+		return 0;
+
+	timeout = 0;
+	if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING))
+		timeout = sk->sk_lingertime;
+	sock->sk = NULL;
+	sk->sk_prot->close(sk, timeout);
+	return 0;
+}
+
+static int dect_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	err = 0;
+	if (sk->sk_prot->bind != NULL)
+		err = sk->sk_prot->bind(sk, uaddr, len);
+
+	return err;
+}
+
+static int dect_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	lock_sock(sk);
+	err = -EINVAL;
+	if (sock->state != SS_UNCONNECTED ||
+	    (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET))
+		goto out;
+
+	if (sk->sk_state != DECT_SK_RELEASED && sk->sk_state != DECT_SK_LISTEN)
+		goto out;
+
+	if (sk->sk_state != DECT_SK_LISTEN)
+		sk->sk_prot->hash(sk);
+	sk->sk_max_ack_backlog = backlog;
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
+}
+
+static int dect_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk = sock->sk, *newsk;
+	int err;
+
+	newsk = sk->sk_prot->accept(sk, flags, &err);
+	if (newsk == NULL)
+		return err;
+
+	lock_sock(newsk);
+	sock_graft(newsk, newsock);
+	newsock->state = SS_CONNECTED;
+	release_sock(newsk);
+	return 0;
+}
+
+static unsigned int dect_poll(struct file *file, struct socket *sock,
+			      struct poll_table_struct *wait)
+{
+	struct sock *sk = sock->sk;
+	unsigned int mask;
+
+	poll_wait(file, sk_sleep(sk), wait);
+	mask = 0;
+
+	if (sk->sk_state == DECT_SK_LISTEN) {
+		if (!hlist_empty(&dect_csk(sk)->accept_queue))
+			return POLLIN | POLLRDNORM;
+		return 0;
+	}
+
+	/* exceptional events? */
+	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+		mask |= POLLERR;
+	if (sk->sk_shutdown & RCV_SHUTDOWN)
+		mask |= POLLRDHUP;
+	if (sk->sk_shutdown == SHUTDOWN_MASK)
+		mask |= POLLHUP;
+
+	/* readable? */
+	if (!skb_queue_empty(&sk->sk_receive_queue) ||
+	    (sk->sk_shutdown & RCV_SHUTDOWN))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* Connection-based need to check for termination and startup */
+	if (sk->sk_state == DECT_SK_RELEASED)
+		mask |= POLLHUP;
+	/* connection hasn't started yet? */
+	if (sk->sk_state == DECT_SK_ESTABLISH_PENDING)
+		return mask;
+
+	/* writable? */
+	if (sock_writeable(sk))
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+	else
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+	return mask;
+}
+
+static int dect_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	how++;
+	if ((how & ~SHUTDOWN_MASK) || !how)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sock->state == SS_CONNECTING &&
+	    sk->sk_state == DECT_SK_ESTABLISH_PENDING)
+		sock->state = SS_DISCONNECTING;
+
+	switch (sk->sk_state) {
+	case DECT_SK_RELEASED:
+		err = -ENOTCONN;
+		break;
+	case DECT_SK_LISTEN:
+		if (!(how & RCV_SHUTDOWN))
+			break;
+	default:
+		sk->sk_shutdown |= how;
+		if (sk->sk_prot->shutdown != NULL)
+			sk->sk_prot->shutdown(sk, how);
+	}
+
+	/* wake up processes sleeping in poll() */
+	sk->sk_state_change(sk);
+	release_sock(sk);
+	return err;
+}
+
+static int dect_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+			int flags)
+{
+	struct sock *sk = sock->sk;
+	long timeo;
+	int err;
+
+	lock_sock(sk);
+	switch (sock->state) {
+	case SS_CONNECTED:
+		err = -EISCONN;
+		goto out;
+	case SS_CONNECTING:
+		err = -EALREADY;
+		goto out;
+	case SS_UNCONNECTED:
+		err = -EISCONN;
+		if (sk->sk_state != DECT_SK_RELEASED)
+			goto out;
+		err = sk->sk_prot->connect(sk, uaddr, len);
+		if (err < 0)
+			goto out;
+		sock->state = SS_CONNECTING;
+		err = -EINPROGRESS;
+		break;
+	default:
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (sk->sk_state == DECT_SK_ESTABLISH_PENDING) {
+		timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+		err = sk_stream_wait_connect(sk, &timeo);
+		if (err < 0)
+			goto out;
+
+		err = sock_intr_errno(timeo);
+		if (signal_pending(current))
+			goto out;
+	}
+
+	/* Connection establishment was aborted or failed */
+	if (sk->sk_state == DECT_SK_RELEASED)
+		goto sock_error;
+
+	sock->state = SS_CONNECTED;
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
+
+sock_error:
+	err = sock_error(sk) ? : -ECONNABORTED;
+	sock->state = SS_UNCONNECTED;
+	goto out;
+}
+
+static int dect_getname(struct socket *sock, struct sockaddr *uaddr, int *len,
+			int peer)
+{
+	const struct dect_proto *p;
+
+	/* AF_DECT uses different address formats for the different SAPs */
+	p = container_of(sock->sk->sk_prot, struct dect_proto, proto);
+	if (p->getname != NULL)
+		return p->getname(sock->sk, uaddr, len, peer);
+	*len = 0;
+	return 0;
+}
+
+static int dect_sendmsg(struct kiocb *iocb, struct socket *sock,
+			struct msghdr *msg, size_t size)
+{
+	struct sock *sk = sock->sk;
+
+	return sk->sk_prot->sendmsg(iocb, sk, msg, size);
+}
+
+static int dect_setsockopt(struct socket *sock, int level, int optname,
+			   char __user *optval, unsigned int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	if (level != SOL_DECT)
+		return -ENOPROTOOPT;
+
+	switch (optname) {
+	default:
+		if (sk->sk_prot->setsockopt)
+			err = sk->sk_prot->setsockopt(sk, level, optname,
+						      optval, optlen);
+		else
+			err = -ENOPROTOOPT;
+	}
+	return err;
+}
+
+static int dect_getsockopt(struct socket *sock, int level, int optname,
+			   char __user *optval, int __user *optlen)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	if (level != SOL_DECT)
+		return -ENOPROTOOPT;
+
+	switch (optname) {
+	default:
+		if (sk->sk_prot->getsockopt)
+			err = sk->sk_prot->getsockopt(sk, level, optname,
+						      optval, optlen);
+		else
+			err = -ENOPROTOOPT;
+	}
+	return err;
+}
+
+static int dect_create(struct net *net, struct socket *sock, int protocol,
+		       int kern)
+{
+	struct dect_proto *p;
+	struct sock *sk;
+	int err = 0;
+
+	if (protocol < 0 || protocol >= DECT_PROTO_NUM)
+		return -EPROTONOSUPPORT;
+#ifdef CONFIG_MODULES
+	if (dect_protos[protocol] == NULL) {
+		err = request_module("net-pf-%d-proto-%d", PF_DECT, protocol);
+		if (err < 0)
+			return err;
+	}
+#endif
+	spin_lock(&dect_proto_lock);
+	p = dect_protos[protocol];
+	if (p != NULL && !try_module_get(p->proto.owner))
+		p = NULL;
+	spin_unlock(&dect_proto_lock);
+
+	if (p == NULL)
+		return -EPROTONOSUPPORT;
+
+	if (p->type != sock->type) {
+		err = -EPROTONOSUPPORT;
+		goto err;
+	}
+
+	if (cap_valid(p->capability) && !capable(p->capability)) {
+		err = -EACCES;
+		goto err;
+	}
+
+	sock->state = SS_UNCONNECTED;
+	sock->ops = p->ops;
+
+	sk = sk_alloc(net, PF_DECT, GFP_KERNEL, &p->proto);
+	if (sk == NULL) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	sock_init_data(sock, sk);
+	sk->sk_protocol = protocol;
+	sk->sk_destruct = dect_destruct;
+
+	if (sk->sk_prot->init != NULL) {
+		err = sk->sk_prot->init(sk);
+		if (err < 0) {
+			sock_orphan(sk);
+			sock_put(sk);
+		}
+	}
+err:
+	module_put(p->proto.owner);
+	return err;
+}
+
+const struct proto_ops dect_stream_ops = {
+	.family		= PF_DECT,
+	.owner		= THIS_MODULE,
+	.release	= dect_release,
+	.bind		= dect_bind,
+	.connect	= dect_connect,
+	.socketpair	= sock_no_socketpair,
+	.getname	= dect_getname,
+	.poll		= dect_poll,
+	.ioctl		= sock_no_ioctl,
+	.listen		= dect_listen,
+	.accept		= dect_accept,
+	.shutdown	= dect_shutdown,
+	.setsockopt	= dect_setsockopt,
+	.getsockopt	= dect_getsockopt,
+	.sendmsg	= dect_sendmsg,
+	.recvmsg	= sock_common_recvmsg,
+	.mmap		= sock_no_mmap,
+	.sendpage	= sock_no_sendpage,
+};
+EXPORT_SYMBOL_GPL(dect_stream_ops);
+
+const struct proto_ops dect_dgram_ops = {
+	.family		= PF_DECT,
+	.owner		= THIS_MODULE,
+	.release	= dect_release,
+	.bind		= dect_bind,
+	.connect	= sock_no_connect,
+	.socketpair	= sock_no_socketpair,
+	.getname	= dect_getname,
+	.poll		= datagram_poll,
+	.ioctl		= sock_no_ioctl,
+	.listen		= sock_no_listen,
+	.accept		= sock_no_accept,
+	.shutdown	= sock_no_shutdown,
+	.setsockopt	= sock_no_setsockopt,
+	.getsockopt	= sock_no_getsockopt,
+	.sendmsg	= dect_sendmsg,
+	.recvmsg	= sock_common_recvmsg,
+	.mmap		= sock_no_mmap,
+	.sendpage	= sock_no_sendpage,
+};
+EXPORT_SYMBOL_GPL(dect_dgram_ops);
+
+static struct net_proto_family dect_family_ops = {
+	.family		= PF_DECT,
+	.create		= dect_create,
+	.owner		= THIS_MODULE,
+};
+
+int __init dect_af_module_init(void)
+{
+	return sock_register(&dect_family_ops);
+}
+
+void dect_af_module_exit(void)
+{
+	sock_unregister(PF_DECT);
+}
+
+MODULE_ALIAS_NETPROTO(PF_DECT);
diff --git a/target/linux/generic/files/net/dect/ccp.c b/target/linux/generic/files/net/dect/ccp.c
new file mode 100644
index 0000000..d423a96
--- /dev/null
+++ b/target/linux/generic/files/net/dect/ccp.c
@@ -0,0 +1,906 @@
+/*
+ * DECT Cell Control Protocol
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+#include <net/dect/mac_ccf.h>
+#include <net/dect/ccp.h>
+#include <net/tipc/tipc.h>
+
+static struct sk_buff *dect_ccp_msg_alloc(size_t size)
+{
+	struct sk_buff *skb;
+
+	size += sizeof(struct dect_ccp_msg_hdr) + 2 * LL_MAX_HEADER;
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+	skb_reserve(skb, size);
+	return skb;
+}
+
+static void dect_ccp_build_msg(struct sk_buff *skb,
+			       enum dect_ccp_primitives prim)
+{
+	struct dect_ccp_msg_hdr *h;
+
+	h = (struct dect_ccp_msg_hdr *)skb_push(skb, sizeof(*h));
+	h->primitive = prim;
+}
+
+static int dect_ccp_send_to_cell(const struct dect_cell_handle *ch,
+				 struct sk_buff *skb,
+				 enum dect_ccp_primitives prim)
+{
+	int err;
+
+	dect_ccp_build_msg(skb, prim);
+	err = tipc_send_buf(ch->portref, skb, skb->len);
+	if (err < 0 && net_ratelimit())
+		printk("Failed to send DECT CCP message\n");
+	return err;
+}
+
+static int dect_ccp_send_to_cluster(const struct dect_cluster_handle *clh,
+				    struct sk_buff *skb,
+				    enum dect_ccp_primitives prim)
+{
+	int err;
+
+	dect_ccp_build_msg(skb, prim);
+	err = tipc_send_buf(clh->portref, skb, skb->len);
+	if (err < 0 && net_ratelimit())
+		printk("Failed to send DECT CCP message\n");
+	return err;
+}
+
+static void dect_ccp_build_tbc_msg(struct sk_buff *skb, const struct dect_tbc_id *id,
+				   u8 data)
+{
+	struct dect_ccp_tbc_msg *msg;
+
+	msg = (struct dect_ccp_tbc_msg *)__skb_push(skb, sizeof(*msg));
+	msg->tbei = cpu_to_be32(id->tbei);
+	msg->pmid = cpu_to_be32(dect_build_pmid(&id->pmid));
+	msg->ari  = cpu_to_be64(dect_build_ari(&id->ari));
+	msg->ecn  = id->ecn;
+	msg->data = data;
+}
+
+static bool dect_ccp_parse_tbc_msg(struct dect_tbc_id *id, u8 *data,
+				   struct sk_buff *skb)
+{
+	struct dect_ccp_tbc_msg *msg;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return false;
+	msg = (struct dect_ccp_tbc_msg *)skb->data;
+	__skb_pull(skb, sizeof(*msg));
+
+	id->tbei = be32_to_cpu(msg->tbei);
+	dect_parse_pmid(&id->pmid, be32_to_cpu(msg->pmid));
+	if (!dect_parse_ari(&id->ari, be64_to_cpu(msg->ari)))
+		return false;
+	id->ecn = msg->ecn;
+	if (data != NULL)
+		*data = msg->data;
+	return true;
+}
+
+static void dect_ccp_build_sysinfo(struct sk_buff *skb,
+				   const struct dect_ari *pari, u8 rpn,
+				   const struct dect_si *si)
+{
+	struct dect_ccp_sysinfo_msg *msg;
+	unsigned int i;
+
+	msg = (struct dect_ccp_sysinfo_msg *)__skb_push(skb, sizeof(*msg));
+	msg->pari = cpu_to_be64(dect_build_ari(pari));
+	for (i = 0; i < si->num_saris; i++)
+		msg->sari[i] = cpu_to_be64(dect_build_ari(&si->sari[i].ari));
+	msg->num_saris = i;
+	msg->fpc = cpu_to_be64(si->fpc.fpc);
+	msg->hlc = cpu_to_be64(si->fpc.hlc);
+	msg->mfn = cpu_to_be32(si->mfn.num);
+	msg->rpn = rpn;
+}
+
+static bool dect_ccp_parse_sysinfo(struct dect_ari *pari, u8 *rpn,
+				   struct dect_si *si, struct sk_buff *skb)
+{
+	struct dect_ccp_sysinfo_msg *msg;
+	unsigned int i;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return false;
+	msg = (struct dect_ccp_sysinfo_msg *)skb->data;
+	__skb_pull(skb, sizeof(*msg));
+
+	if (!dect_parse_ari(pari, be64_to_cpu(msg->pari)))
+		return false;
+	*rpn = msg->rpn;
+
+	if (msg->num_saris > ARRAY_SIZE(si->sari))
+		return false;
+	for (i = 0; i < msg->num_saris; i++) {
+		if (!dect_parse_ari(&si->sari[i].ari,
+				    be64_to_cpu(msg->sari[i])))
+			return false;
+	}
+	si->fpc.fpc = be64_to_cpu(msg->fpc);
+	si->fpc.hlc = be64_to_cpu(msg->hlc);
+	si->mfn.num = be32_to_cpu(msg->mfn);
+	return true;
+}
+
+static int dect_ccp_send_set_mode(const struct dect_cell_handle *ch,
+				  enum dect_cluster_modes mode)
+{
+	struct dect_ccp_mode_msg *msg;
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(*msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	msg = (struct dect_ccp_mode_msg *)__skb_push(skb, sizeof(*msg));
+	msg->mode = mode;
+
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_SET_MODE);
+}
+
+static void dect_ccp_parse_set_mode(const struct dect_cell_handle *ch,
+				    struct sk_buff *skb)
+{
+	struct dect_ccp_mode_msg *msg;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return;
+	msg = (struct dect_ccp_mode_msg *)skb->data;
+
+	ch->ops->set_mode(ch, msg->mode);
+}
+
+static int dect_ccp_send_scan(const struct dect_cell_handle *ch,
+			      const struct dect_llme_req *lreq,
+			      const struct dect_ari *ari,
+			      const struct dect_ari *ari_mask)
+{
+	struct dect_ccp_scan_msg *msg;
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(*msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	msg = (struct dect_ccp_scan_msg *)__skb_push(skb, sizeof(*msg));
+	msg->ari = cpu_to_be64(dect_build_ari(ari));
+	msg->ari_mask = cpu_to_be64(dect_build_ari(ari_mask));
+
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_SCAN);
+}
+
+static void dect_ccp_parse_scan(const struct dect_cell_handle *ch,
+				struct sk_buff *skb)
+{
+	struct dect_ccp_scan_msg *msg;
+	struct dect_ari ari, ari_mask;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return;
+	msg = (struct dect_ccp_scan_msg *)skb->data;
+
+	if (!dect_parse_ari(&ari, be64_to_cpu(msg->ari)))
+		return;
+	if (!dect_parse_ari(&ari_mask, be64_to_cpu(msg->ari_mask)))
+		return;
+	ch->ops->scan(ch, NULL, &ari, &ari_mask);
+}
+
+static int dect_ccp_send_preload(const struct dect_cell_handle *ch,
+				 const struct dect_ari *pari, u8 rpn,
+				 const struct dect_si *si)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_sysinfo_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_sysinfo(skb, pari, rpn, si);
+
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_PRELOAD);
+}
+
+static void dect_ccp_parse_preload(const struct dect_cell_handle *ch,
+				   struct sk_buff *skb)
+{
+	struct dect_ari pari;
+	struct dect_si si;
+	u8 rpn;
+
+	if (!dect_ccp_parse_sysinfo(&pari, &rpn, &si, skb))
+		return;
+	ch->ops->preload(ch, &pari, rpn, &si);
+}
+
+static int dect_ccp_send_enable(const struct dect_cell_handle *ch)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(0);
+	if (skb == NULL)
+		return -ENOMEM;
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_ENABLE);
+}
+
+static void dect_ccp_parse_enable(const struct dect_cell_handle *ch,
+				  struct sk_buff *skb)
+{
+	ch->ops->enable(ch);
+}
+
+static void dect_ccp_send_page_req(const struct dect_cell_handle *ch,
+				   struct sk_buff *skb)
+{
+	struct dect_ccp_page_msg *msg;
+
+	msg = (struct dect_ccp_page_msg *)__skb_push(skb, sizeof(*msg));
+	msg->fast_page = DECT_BMC_CB(skb)->fast_page;
+	msg->long_page = DECT_BMC_CB(skb)->long_page;
+
+	dect_ccp_send_to_cell(ch, skb, DECT_CCP_PAGE_REQ);
+}
+
+static void dect_ccp_parse_page_req(const struct dect_cell_handle *ch,
+				    struct sk_buff *skb)
+{
+	struct dect_ccp_page_msg *msg;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return;
+	msg = (struct dect_ccp_page_msg *)skb->data;
+	__pskb_pull(skb, sizeof(*msg));
+
+	DECT_BMC_CB(skb)->fast_page = msg->fast_page;
+	DECT_BMC_CB(skb)->long_page = msg->long_page;
+
+	ch->ops->page_req(ch, skb);
+}
+
+static int dect_ccp_send_tbc_establish_req(const struct dect_cell_handle *ch,
+					   const struct dect_tbc_id *id,
+					   const struct dect_channel_desc *chd,
+					   enum dect_mac_service_types service,
+					   bool handover)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, 0);
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_ESTABLISH_REQ);
+}
+
+static void dect_ccp_parse_tbc_establish_req(const struct dect_cell_handle *ch,
+					     struct sk_buff *skb)
+{
+	struct dect_tbc_id id;
+
+	if (!dect_ccp_parse_tbc_msg(&id, NULL, skb))
+		return;
+	ch->ops->tbc_establish_req(ch, &id, NULL, DECT_SERVICE_IN_MIN_DELAY, false);
+}
+
+static void dect_ccp_send_tbc_dis_req(const struct dect_cell_handle *ch,
+				      const struct dect_tbc_id *id,
+				      enum dect_release_reasons reason)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return;
+	dect_ccp_build_tbc_msg(skb, id, reason);
+	dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_DIS_REQ);
+}
+
+static void dect_ccp_parse_tbc_dis_req(const struct dect_cell_handle *ch,
+				       struct sk_buff *skb)
+{
+	struct dect_tbc_id id;
+	u8 reason;
+
+	if (!dect_ccp_parse_tbc_msg(&id, &reason, skb))
+		return;
+	ch->ops->tbc_dis_req(ch, &id, reason);
+}
+
+static int dect_ccp_send_tbc_establish_res(const struct dect_cell_handle *ch,
+					   const struct dect_tbc_id *id)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, 0);
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_ESTABLISH_RES);
+}
+
+static void dect_ccp_parse_tbc_establish_res(const struct dect_cell_handle *ch,
+					     struct sk_buff *skb)
+{
+	struct dect_tbc_id id;
+
+	if (!dect_ccp_parse_tbc_msg(&id, NULL, skb))
+		return;
+	ch->ops->tbc_establish_res(ch, &id);
+}
+
+static void dect_ccp_send_tbc_data_req(const struct dect_cell_handle *ch,
+				       const struct dect_tbc_id *id,
+				       enum dect_data_channels chan,
+				       struct sk_buff *skb)
+{
+	dect_ccp_build_tbc_msg(skb, id, chan);
+	dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_DATA_REQ);
+}
+
+static int dect_ccp_send_tbc_enc_key_req(const struct dect_cell_handle *ch,
+					 const struct dect_tbc_id *id, u64 ck)
+{
+	struct dect_ccp_enc_key_msg *msg;
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg) + sizeof(*msg));
+	if (skb == NULL)
+		return -ENOMEM;
+
+	dect_ccp_build_tbc_msg(skb, id, 0);
+	msg = (struct dect_ccp_enc_key_msg *)skb_tail_pointer(skb);
+	msg->key = cpu_to_be64(ck);
+
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_ENC_KEY_REQ);
+}
+
+static void dect_ccp_parse_tbc_enc_key_req(const struct dect_cell_handle *ch,
+					   struct sk_buff *skb)
+{
+	const struct dect_ccp_enc_key_msg *msg;
+	struct dect_tbc_id id;
+	u64 ck;
+
+	if (!dect_ccp_parse_tbc_msg(&id, NULL, skb))
+		return;
+
+	if (!pskb_may_pull(skb, sizeof(*msg)))
+		return;
+	msg = (struct dect_ccp_enc_key_msg *)skb->data;
+	ck = be64_to_cpu(msg->key);
+
+	ch->ops->tbc_enc_key_req(ch, &id, ck);
+}
+
+static int dect_ccp_send_tbc_enc_eks_req(const struct dect_cell_handle *ch,
+					 const struct dect_tbc_id *id,
+					 enum dect_cipher_states status)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, status);
+	return dect_ccp_send_to_cell(ch, skb, DECT_CCP_TBC_ENC_EKS_REQ);
+}
+
+static void dect_ccp_parse_tbc_enc_eks_req(const struct dect_cell_handle *ch,
+					   struct sk_buff *skb)
+{
+	struct dect_tbc_id id;
+	u8 status;
+
+	if (!dect_ccp_parse_tbc_msg(&id, &status, skb))
+		return;
+
+	switch (status) {
+	case DECT_CIPHER_DISABLED:
+	case DECT_CIPHER_ENABLED:
+		break;
+	default:
+		return;
+	}
+
+	ch->ops->tbc_enc_eks_req(ch, &id, status);
+}
+
+static void dect_ccp_send_scan_report(const struct dect_cluster_handle *clh,
+				      const struct dect_scan_result *res)
+{
+}
+
+static void dect_ccp_send_mac_info_ind(const struct dect_cluster_handle *clh,
+				       const struct dect_idi *idi,
+				       const struct dect_si *si)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_sysinfo_msg));
+	if (skb == NULL)
+		return;
+
+	dect_ccp_build_sysinfo(skb, &idi->pari, idi->rpn, si);
+	dect_ccp_send_to_cluster(clh, skb, DECT_CCP_MAC_INFO_IND);
+}
+
+static void dect_ccp_parse_mac_info_ind(const struct dect_cell_handle *ch,
+					struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_idi idi;
+	struct dect_si si;
+
+	if (!dect_ccp_parse_sysinfo(&idi.pari, &idi.rpn, &si, skb))
+		return;
+	idi.e = si.num_saris ? true : false;
+
+	clh->ops->mac_info_ind(clh, &idi, &si);
+}
+
+static int dect_ccp_send_tbc_establish_ind(const struct dect_cluster_handle *clh,
+					   const struct dect_cell_handle *ch,
+					   const struct dect_tbc_id *id,
+					   enum dect_mac_service_types service,
+					   bool handover)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, 0);
+
+	return dect_ccp_send_to_cluster(clh, skb, DECT_CCP_TBC_ESTABLISH_IND);
+}
+
+static void dect_ccp_parse_tbc_establish_ind(const struct dect_cell_handle *ch,
+					     struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_tbc_id id;
+
+	if (!dect_ccp_parse_tbc_msg(&id, NULL, skb))
+		return;
+	clh->ops->tbc_establish_ind(clh, ch, &id, DECT_SERVICE_IN_MIN_DELAY, false);
+}
+
+static int dect_ccp_send_tbc_establish_cfm(const struct dect_cluster_handle *clh,
+					   const struct dect_tbc_id *id,
+					   bool success, u8 rx_slot)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, 0);
+
+	return dect_ccp_send_to_cluster(clh, skb, DECT_CCP_TBC_ESTABLISH_CFM);
+}
+
+static void dect_ccp_parse_tbc_establish_cfm(const struct dect_cell_handle *ch,
+					     struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_tbc_id id;
+
+	if (!dect_ccp_parse_tbc_msg(&id, NULL, skb))
+		return;
+	clh->ops->tbc_establish_cfm(clh, &id, true, 0);
+}
+
+static int dect_ccp_send_tbc_event_ind(const struct dect_cluster_handle *clh,
+				       const struct dect_tbc_id *id,
+				       enum dect_tbc_event event)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, event);
+
+	return dect_ccp_send_to_cluster(clh, skb, DECT_CCP_TBC_EVENT_IND);
+}
+
+static void dect_ccp_parse_tbc_event_ind(const struct dect_cell_handle *ch,
+					 struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_tbc_id id;
+	u8 event;
+
+	if (!dect_ccp_parse_tbc_msg(&id, &event, skb))
+		return;
+	clh->ops->tbc_event_ind(clh, &id, event);
+}
+
+static void dect_ccp_send_tbc_data_ind(const struct dect_cluster_handle *clh,
+				       const struct dect_tbc_id *id,
+				       enum dect_data_channels chan,
+				       struct sk_buff *skb)
+{
+	dect_ccp_build_tbc_msg(skb, id, chan);
+	dect_ccp_send_to_cluster(clh, skb, DECT_CCP_TBC_DATA_IND);
+}
+
+static void dect_ccp_parse_tbc_data_ind(const struct dect_cell_handle *ch,
+					struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_tbc_id id;
+	u8 chan;
+
+	if (!dect_ccp_parse_tbc_msg(&id, &chan, skb))
+		return;
+	clh->ops->tbc_data_ind(clh, &id, chan, skb);
+}
+
+static void dect_ccp_send_tbc_dis_ind(const struct dect_cluster_handle *clh,
+				      const struct dect_tbc_id *id,
+				      enum dect_release_reasons reason)
+{
+	struct sk_buff *skb;
+
+	skb = dect_ccp_msg_alloc(sizeof(struct dect_ccp_tbc_msg));
+	if (skb == NULL)
+		return;// -ENOMEM;
+	dect_ccp_build_tbc_msg(skb, id, reason);
+
+	dect_ccp_send_to_cluster(clh, skb, DECT_CCP_TBC_DIS_IND);
+}
+
+static void dect_ccp_parse_tbc_dis_ind(const struct dect_cell_handle *ch,
+				       struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = ch->clh;
+	struct dect_tbc_id id;
+	u8 reason;
+
+	if (!dect_ccp_parse_tbc_msg(&id, &reason, skb))
+		return;
+	clh->ops->tbc_dis_ind(clh, &id, reason);
+}
+
+static void dect_ccp_rcv_cell_msg(void *handle, u32 portref,
+				  struct sk_buff **pskb,
+				  const u8 *data, u32 size)
+{
+	struct dect_cell_handle *ch = handle;
+	struct dect_ccp_msg_hdr *h;
+	struct sk_buff *skb = *pskb;
+
+	if (!pskb_may_pull(skb, sizeof(*h)))
+		return;
+	h = (struct dect_ccp_msg_hdr *)skb->data;
+	__skb_pull(skb, sizeof(*h));
+
+	switch (h->primitive) {
+	case DECT_CCP_MAC_INFO_IND:
+		return dect_ccp_parse_mac_info_ind(ch, skb);
+	case DECT_CCP_TBC_ESTABLISH_IND:
+		return dect_ccp_parse_tbc_establish_ind(ch, skb);
+	case DECT_CCP_TBC_ESTABLISH_CFM:
+		return dect_ccp_parse_tbc_establish_cfm(ch, skb);
+	case DECT_CCP_TBC_EVENT_IND:
+		return dect_ccp_parse_tbc_event_ind(ch, skb);
+	case DECT_CCP_TBC_DATA_IND:
+		return dect_ccp_parse_tbc_data_ind(ch, skb);
+	case DECT_CCP_TBC_DIS_IND:
+		return dect_ccp_parse_tbc_dis_ind(ch, skb);
+	}
+}
+
+static void dect_ccp_cl_disconnect(void *handle, u32 portref,
+				   struct sk_buff **pskb,
+				   const u8 *data, u32 size, int reason)
+{
+	struct dect_cell_handle *ch = handle;
+	struct dect_cluster_handle *clh = ch->clh;
+
+	pr_debug("cell disconnected\n");
+	clh->ops->unbind(clh, ch);
+	kfree(ch);
+}
+
+static const struct dect_csf_ops dect_ccp_csf_ops = {
+	.set_mode		= dect_ccp_send_set_mode,
+	.scan			= dect_ccp_send_scan,
+	.enable			= dect_ccp_send_enable,
+	.preload		= dect_ccp_send_preload,
+	.page_req		= dect_ccp_send_page_req,
+	.tbc_establish_req	= dect_ccp_send_tbc_establish_req,
+	.tbc_establish_res	= dect_ccp_send_tbc_establish_res,
+	.tbc_dis_req		= dect_ccp_send_tbc_dis_req,
+	.tbc_enc_key_req	= dect_ccp_send_tbc_enc_key_req,
+	.tbc_enc_eks_req	= dect_ccp_send_tbc_enc_eks_req,
+	.tbc_data_req		= dect_ccp_send_tbc_data_req,
+};
+
+static void dect_ccp_cl_named_msg(void *handle, u32 portref,
+				  struct sk_buff **pskb,
+				  const u8 *data, u32 size,
+				  u32 importance,
+				  const struct tipc_portid *source,
+				  const struct tipc_name_seq *dest)
+{
+	struct dect_cluster *cl = handle;
+	struct dect_cluster_handle *clh = &cl->handle;
+	struct dect_cell_handle *ch;
+	struct iovec ack = { NULL, 0};
+	int err;
+
+	ch = kzalloc(sizeof(*ch), GFP_ATOMIC);
+	if (ch == NULL)
+		goto err1;
+	ch->ops = &dect_ccp_csf_ops;
+
+	err = tipc_createport(cl->tipc_id, ch, TIPC_HIGH_IMPORTANCE,
+			      NULL, NULL, dect_ccp_cl_disconnect,
+			      NULL, NULL, dect_ccp_rcv_cell_msg, NULL,
+			      &ch->portref);
+	if (err < 0)
+		goto err2;
+
+	err = tipc_connect2port(ch->portref, source);
+	if (err < 0)
+		goto err3;
+
+	err = tipc_send(ch->portref, 1, &ack);
+	if (err < 0)
+		goto err3;
+
+	err = clh->ops->bind(clh, ch);
+	if (err < 0)
+		goto err4;
+	return;
+
+err4:
+	tipc_disconnect(ch->portref);
+err3:
+	tipc_deleteport(ch->portref);
+err2:
+	kfree(ch);
+err1:
+	return;
+}
+
+/**
+ * dect_ccp_cluster_init - Initialize a cluster control CCP instance
+ *
+ * @cl:		DECT cluster
+ */
+int dect_ccp_cluster_init(struct dect_cluster *cl)
+{
+	struct tipc_name_seq seq;
+	int err;
+
+	err = tipc_attach(&cl->tipc_id, NULL, NULL);
+	if (err < 0)
+		goto err1;
+
+	err = tipc_createport(cl->tipc_id, cl, TIPC_HIGH_IMPORTANCE,
+			      NULL, NULL, NULL, NULL, dect_ccp_cl_named_msg,
+			      NULL, NULL, &cl->tipc_portref);
+	if (err < 0)
+		goto err2;
+
+	seq.type  = DECT_CCP_TIPC_TYPE;
+	seq.lower = DECT_CCP_CLUSTER_PORT_BASE + cl->index;
+	seq.upper = DECT_CCP_CLUSTER_PORT_BASE + cl->index;
+	err = tipc_publish(cl->tipc_portref, TIPC_CLUSTER_SCOPE, &seq);
+	if (err < 0)
+		goto err3;
+	return 0;
+
+err3:
+	tipc_deleteport(cl->tipc_portref);
+err2:
+	tipc_detach(cl->tipc_id);
+err1:
+	return err;
+}
+
+void dect_ccp_cluster_shutdown(struct dect_cluster *cl)
+{
+	tipc_detach(cl->tipc_id);
+}
+
+static void dect_ccp_rcv_cluster_msg(void *handle, u32 portref,
+				     struct sk_buff **pskb,
+				     const u8 *data, u32 size)
+{
+	struct sk_buff *skb = *pskb;
+	struct dect_cell_handle *ch = handle;
+	struct dect_ccp_msg_hdr *h;
+
+	if (!pskb_may_pull(skb, sizeof(*h)))
+		return;
+	h = (struct dect_ccp_msg_hdr *)skb->data;
+	__skb_pull(skb, sizeof(*h));
+
+	switch (h->primitive) {
+	case DECT_CCP_SET_MODE:
+		return dect_ccp_parse_set_mode(ch, skb);
+	case DECT_CCP_SCAN:
+		return dect_ccp_parse_scan(ch, skb);
+	case DECT_CCP_ENABLE:
+		return dect_ccp_parse_enable(ch, skb);
+	case DECT_CCP_PRELOAD:
+		return dect_ccp_parse_preload(ch, skb);
+	case DECT_CCP_PAGE_REQ:
+		return dect_ccp_parse_page_req(ch, skb);
+	case DECT_CCP_TBC_ESTABLISH_REQ:
+		return dect_ccp_parse_tbc_establish_req(ch, skb);
+	case DECT_CCP_TBC_ESTABLISH_RES:
+		return dect_ccp_parse_tbc_establish_res(ch, skb);
+	case DECT_CCP_TBC_DIS_REQ:
+		return dect_ccp_parse_tbc_dis_req(ch, skb);
+	case DECT_CCP_TBC_ENC_KEY_REQ:
+		return dect_ccp_parse_tbc_enc_key_req(ch, skb);
+	case DECT_CCP_TBC_ENC_EKS_REQ:
+		return dect_ccp_parse_tbc_enc_eks_req(ch, skb);
+	}
+}
+
+static void dect_ccp_cluster_disconnect(void *handle, u32 portref,
+					struct sk_buff **pskb,
+					const u8 *data, u32 size, int reason)
+{
+	pr_debug("Cluster disconnected\n");
+#if 0
+	struct dect_cell_handle *clh = handle;
+
+	clh->ops->unbind(clh);
+#endif
+}
+
+static void dect_ccp_subscr_rcv(void *handle, u32 portref,
+				struct sk_buff **pskb,
+				const u8 *data, u32 size)
+{
+	struct dect_cell_handle *ch = handle;
+	struct dect_cluster_handle *clh = ch->clh;
+	struct sk_buff *skb = *pskb;
+	struct tipc_event *ev;
+	struct tipc_name name;
+	int err;
+
+	if (!pskb_may_pull(skb, sizeof(*ev)))
+		return;
+	ev = (struct tipc_event *)skb->data;
+
+	if (ev->event != TIPC_PUBLISHED)
+		return;
+
+	/* Connect to cluster */
+	err = tipc_createport(clh->tipc_id, ch, TIPC_HIGH_IMPORTANCE,
+			      NULL, NULL, dect_ccp_cluster_disconnect,
+			      NULL, NULL, dect_ccp_rcv_cluster_msg, NULL,
+			      &clh->portref);
+	if (err < 0)
+		goto err1;
+
+	name.type = DECT_CCP_TIPC_TYPE;
+	name.instance = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
+	err = tipc_send2name(clh->portref, &name, 0, 0, NULL);
+	if (err < 0)
+		goto err2;
+	return;
+
+err2:
+	tipc_deleteport(clh->portref);
+err1:
+	return;
+}
+
+/**
+ * dect_ccp_cell_init - Initialize a cell CCP instance
+ *
+ * @cell:	DECT cell
+ */
+static int dect_ccp_bind_cell(struct dect_cluster_handle *clh,
+			      struct dect_cell_handle *ch)
+{
+	struct tipc_subscr subscr;
+	struct iovec iov = { &subscr, sizeof(subscr) };
+	struct tipc_name tname;
+	int err;
+
+	err = tipc_attach(&clh->tipc_id, NULL, NULL);
+	if (err < 0)
+		goto err1;
+	ch->clh = clh;
+
+	/* Connect to topology service and subscribe to cluster port */
+	err = tipc_createport(clh->tipc_id, ch, TIPC_CRITICAL_IMPORTANCE,
+			      NULL, NULL, NULL, NULL, NULL,
+			      dect_ccp_subscr_rcv, NULL, &clh->tportref);
+	if (err < 0)
+		goto err2;
+
+	subscr.seq.type = DECT_CCP_TIPC_TYPE;
+	subscr.seq.lower = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
+	subscr.seq.upper = DECT_CCP_CLUSTER_PORT_BASE + clh->index;
+	subscr.timeout = TIPC_WAIT_FOREVER;
+	subscr.filter = TIPC_SUB_PORTS;
+	memset(&subscr.usr_handle, 0, sizeof(subscr.usr_handle));
+
+	tname.type = TIPC_TOP_SRV;
+	tname.instance = TIPC_TOP_SRV;
+
+	err = tipc_send2name(clh->tportref, &tname, 0, 1, &iov);
+	if (err < 0)
+		goto err3;
+	return 0;
+
+err3:
+	tipc_deleteport(clh->tportref);
+err2:
+	tipc_detach(clh->tipc_id);
+err1:
+	return err;
+
+}
+
+static void dect_ccp_unbind_cell(struct dect_cluster_handle *clh,
+				 struct dect_cell_handle *ch)
+{
+	tipc_detach(clh->tipc_id);
+}
+
+static void dect_ccp_send_bmc_page_ind(const struct dect_cluster_handle *clh,
+				       struct sk_buff *skb)
+{
+}
+
+static const struct dect_ccf_ops dect_ccp_ccf_ops = {
+	.bind			= dect_ccp_bind_cell,
+	.unbind			= dect_ccp_unbind_cell,
+	.scan_report		= dect_ccp_send_scan_report,
+	.mac_info_ind		= dect_ccp_send_mac_info_ind,
+	.tbc_establish_ind	= dect_ccp_send_tbc_establish_ind,
+	.tbc_establish_cfm	= dect_ccp_send_tbc_establish_cfm,
+	.tbc_event_ind		= dect_ccp_send_tbc_event_ind,
+	.tbc_dis_ind		= dect_ccp_send_tbc_dis_ind,
+	.tbc_data_ind		= dect_ccp_send_tbc_data_ind,
+	.bmc_page_ind		= dect_ccp_send_bmc_page_ind,
+};
+
+struct dect_cluster_handle *dect_ccp_cell_init(struct dect_cell *cell, u8 clindex)
+{
+	struct dect_cluster_handle *clh;
+
+	clh = kzalloc(sizeof(*clh), GFP_KERNEL);
+	if (clh == NULL)
+		return ERR_PTR(-ENOMEM);
+	clh->index = clindex;
+	clh->ops = &dect_ccp_ccf_ops;
+	return clh;
+}
diff --git a/target/linux/generic/files/net/dect/core.c b/target/linux/generic/files/net/dect/core.c
new file mode 100644
index 0000000..80a7dd8
--- /dev/null
+++ b/target/linux/generic/files/net/dect/core.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <net/dect/dect.h>
+#include <net/dect/transceiver.h>
+
+static DEFINE_MUTEX(dect_cfg_mutex);
+
+void dect_lock(void)
+{
+	mutex_lock(&dect_cfg_mutex);
+}
+EXPORT_SYMBOL_GPL(dect_lock);
+
+void dect_unlock(void)
+{
+	mutex_unlock(&dect_cfg_mutex);
+}
+EXPORT_SYMBOL_GPL(dect_unlock);
+
+/*
+ * MAC layer timers
+ */
+
+#if 1
+#define timer_debug(name, base, fmt, args...) \
+	pr_debug("%s: %s %u.%.2u.%.2u: " fmt, name, \
+		 (base)->base == DECT_TIMER_TX ? "TX" : "RX", \
+		 base->mfn, base->framenum, base->slot, ## args)
+#else
+#define timer_debug(base, fmt, args...)
+#endif
+
+void __dect_run_timers(const char *name, struct dect_timer_base *base)
+{
+	struct dect_timer *t;
+
+	while (!list_empty(&base->timers)) {
+		t = list_first_entry(&base->timers, struct dect_timer, list);
+
+		if (dect_mfn_after(t->mfn, base->mfn) ||
+		    (t->mfn == base->mfn && t->frame > base->framenum) ||
+		    (t->mfn == base->mfn && t->frame == base->framenum &&
+		     t->slot > base->slot))
+			break;
+
+		timer_debug(name, base, "timer %p: %u.%u.%u\n",
+			    t, t->mfn, t->frame, t->slot);
+		list_del_init(&t->list);
+		t->cb.cb(t->obj, t->data);
+	}
+}
+EXPORT_SYMBOL_GPL(__dect_run_timers);
+
+/**
+ * dect_timer_add - (re)schedule a timer
+ *
+ * Frame numbers are relative to the current time, slot positions are absolute.
+ * A timer scheduled for (1, 2) will expire in slot 2 in the next frame.
+ *
+ * A frame number of zero will expire at the next occurence of the slot, which
+ * can be within the same frame in case the slot is not already in the past, or
+ * in the next frame in case it is.
+ */
+void __dect_timer_add(const char *name, struct dect_timer_base *base,
+		      struct dect_timer *timer, u32 frame, u8 slot)
+{
+	struct dect_timer *t;
+	u32 mfn;
+
+	if (frame == 0 && slot < base->slot)
+		frame++;
+	frame += base->framenum;
+	mfn = dect_mfn_add(base->mfn, frame / DECT_FRAMES_PER_MULTIFRAME);
+	frame %= DECT_FRAMES_PER_MULTIFRAME;
+
+	timer_debug(name, base, "timer %p: schedule for %u.%u.%u\n",
+		    timer, mfn, frame, slot);
+	if (!list_empty(&timer->list))
+		list_del(&timer->list);
+	list_for_each_entry(t, &base->timers, list) {
+		if (dect_mfn_after(t->mfn, mfn) ||
+		    (t->mfn == mfn && t->frame > frame) ||
+		    (t->mfn == mfn && t->frame == frame && t->slot > slot))
+			break;
+	}
+
+	timer->mfn   = mfn;
+	timer->frame = frame;
+	timer->slot  = slot;
+	list_add_tail(&timer->list, &t->list);
+}
+EXPORT_SYMBOL_GPL(__dect_timer_add);
+
+struct sk_buff *skb_append_frag(struct sk_buff *head, struct sk_buff *skb)
+{
+	struct sk_buff **pprev;
+
+	if (head == NULL)
+		return skb;
+
+	pprev = &skb_shinfo(head)->frag_list;
+	while (*pprev != NULL)
+		pprev = &(*pprev)->next;
+	*pprev = skb;
+
+	head->data_len += skb->len;
+	head->len += skb->len;
+	head->truesize += skb->truesize;
+	return head;
+}
+EXPORT_SYMBOL_GPL(skb_append_frag);
+
+unsigned int skb_queue_pull(struct sk_buff_head *list, unsigned int len)
+{
+	unsigned int pulled = 0;
+	unsigned long flags;
+	struct sk_buff *skb;
+
+	spin_lock_irqsave(&list->lock, flags);
+	while (len > pulled) {
+		skb = skb_peek(list);
+		if (skb == NULL)
+			break;
+		if (skb->len <= len) {
+			__skb_unlink(skb, list);
+			pulled += skb->len;
+			kfree_skb(skb);
+		} else {
+			__skb_pull(skb, len);
+			pulled += len;
+		}
+	}
+	spin_unlock_irqrestore(&list->lock, flags);
+	return pulled;
+}
+EXPORT_SYMBOL_GPL(skb_queue_pull);
+
+static int __init dect_module_init(void)
+{
+	int err;
+
+	err = dect_netlink_module_init();
+	if (err < 0)
+		goto err1;
+	err = dect_af_module_init();
+	if (err < 0)
+		goto err2;
+	return 0;
+
+err2:
+	dect_netlink_module_exit();
+err1:
+	return err;
+}
+
+static void __exit dect_module_exit(void)
+{
+	dect_af_module_exit();
+	dect_netlink_module_exit();
+}
+
+module_init(dect_module_init);
+module_exit(dect_module_exit);
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT protocol stack");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/net/dect/dect_netlink.c b/target/linux/generic/files/net/dect/dect_netlink.c
new file mode 100644
index 0000000..7ed631c
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dect_netlink.c
@@ -0,0 +1,150 @@
+/*
+ * DECT netlink control interface
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect_netlink.h>
+#include <linux/dect.h>
+#include <linux/security.h>
+#include <net/netlink.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+
+struct sock *dect_nlsk __read_mostly;
+EXPORT_SYMBOL_GPL(dect_nlsk);
+
+LIST_HEAD(dect_cluster_list);
+EXPORT_SYMBOL_GPL(dect_cluster_list);
+
+struct dect_cluster *dect_cluster_get_by_index(int index)
+{
+	struct dect_cluster *cl;
+
+	list_for_each_entry(cl, &dect_cluster_list, list) {
+		if (cl->index == index)
+			return cl;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(dect_cluster_get_by_index);
+
+static const struct dect_netlink_handler *dect_dispatch[DECT_NR_MSGTYPES];
+
+void dect_netlink_register_handlers(const struct dect_netlink_handler *handler,
+				    unsigned int base, unsigned int n)
+{
+	unsigned int i;
+
+	dect_lock();
+	base -= DECT_MSG_BASE;
+	for (i = 0; i < n; i++)
+		dect_dispatch[base + i] = handler + i;
+	dect_unlock();
+}
+EXPORT_SYMBOL_GPL(dect_netlink_register_handlers);
+
+void dect_netlink_unregister_handlers(unsigned int base, unsigned int n)
+{
+	unsigned int i;
+
+	dect_lock();
+	base -= DECT_MSG_BASE;
+	for (i = 0; i < n; i++)
+		dect_dispatch[base + i] = NULL;
+	dect_unlock();
+}
+EXPORT_SYMBOL_GPL(dect_netlink_unregister_handlers);
+
+static int dect_netlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	const struct dect_netlink_handler *link;
+	u16 type;
+	int err;
+
+	type = nlh->nlmsg_type;
+	if (type > DECT_MSG_MAX)
+		return -EINVAL;
+
+	link = dect_dispatch[type - DECT_MSG_BASE];
+	if (link == NULL) {
+#ifdef CONFIG_MODULES
+		dect_unlock();
+		switch (type) {
+		case DECT_NEW_TRANSCEIVER ... DECT_GET_CELL:
+			request_module("dect_csf");
+			break;
+		case DECT_NEW_CLUSTER ... DECT_LLME_MSG:
+			request_module("dect_ccf");
+			break;
+		}
+		dect_lock();
+
+		link = dect_dispatch[type - DECT_MSG_BASE];
+		if (link == NULL)
+#endif
+			return -EOPNOTSUPP;
+	}
+
+	/* dump and get requests don't require privileges */
+	if (link->dump == NULL && !capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (nlh->nlmsg_flags & NLM_F_DUMP) {
+		struct netlink_dump_control c = {
+			.dump = link->dump,
+			.done = link->done,
+		};
+		if (link->dump == NULL)
+			return -EOPNOTSUPP;
+		return netlink_dump_start(dect_nlsk, skb, nlh, &c);
+	} else {
+		struct nlattr *nla[link->maxtype + 1];
+
+		err = nlmsg_parse(nlh, sizeof(struct dectmsg), nla,
+				  link->maxtype, link->policy);
+		if (err < 0)
+			return err;
+		if (link->doit == NULL)
+			return -EOPNOTSUPP;
+		return link->doit(skb, nlh, (const struct nlattr **)nla);
+	}
+}
+
+static void dect_netlink_rcv(struct sk_buff *skb)
+{
+	dect_lock();
+	netlink_rcv_skb(skb, dect_netlink_rcv_msg);
+	dect_unlock();
+}
+
+int __init dect_netlink_module_init(void)
+{
+	struct sock *sk;
+	struct netlink_kernel_cfg cfg = {
+		.input  = dect_netlink_rcv,
+		.groups = DECTNLGRP_MAX,
+	};
+	sk = netlink_kernel_create(&init_net, NETLINK_DECT, &cfg);
+	if (sk == NULL)
+		return -ENOMEM;
+	dect_nlsk = sk;
+	return 0;
+}
+
+void dect_netlink_module_exit(void)
+{
+	netlink_kernel_release(dect_nlsk);
+}
+
+MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_DECT);
diff --git a/target/linux/generic/files/net/dect/dlc.c b/target/linux/generic/files/net/dect/dlc.c
new file mode 100644
index 0000000..63d2235
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc.c
@@ -0,0 +1,282 @@
+/*
+ * DECT DLC Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+
+#define mc_debug(mc, fmt, args...) \
+	pr_debug("MC (MCEI %u/%s): " fmt, \
+		 (mc)->mcei, dect_mc_states[(mc)->state], ## args)
+
+static const char * const dect_mc_states[] = {
+	[DECT_MAC_CONN_CLOSED]		= "CLOSED",
+	[DECT_MAC_CONN_OPEN_PENDING]	= "OPEN_PENDING",
+	[DECT_MAC_CONN_OPEN]		= "OPEN",
+};
+
+static struct dect_mac_conn *
+dect_mac_conn_get_by_mcei(const struct dect_cluster *cl, u32 mcei)
+{
+	struct dect_mac_conn *mc;
+
+	list_for_each_entry(mc, &cl->mac_connections, list) {
+		if (mc->mcei == mcei)
+			return mc;
+	}
+	return NULL;
+}
+
+struct dect_mac_conn *
+dect_mac_conn_get_by_mci(const struct dect_cluster *cl, const struct dect_mci *mci)
+{
+	struct dect_mac_conn *mc;
+
+	list_for_each_entry(mc, &cl->mac_connections, list) {
+		if (!dect_ari_cmp(&mc->mci.ari, &mci->ari) &&
+		    !dect_pmid_cmp(&mc->mci.pmid, &mci->pmid) &&
+		    mc->mci.lcn == mci->lcn)
+			return mc;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(dect_mac_conn_get_by_mci);
+
+void dect_dlc_mac_conn_destroy(struct dect_mac_conn *mc)
+{
+	mc_debug(mc, "destroy\n");
+	list_del(&mc->list);
+	kfree(mc);
+}
+
+void dect_dlc_mac_conn_bind(struct dect_mac_conn *mc)
+{
+	mc_debug(mc, "bind use %u\n", mc->use);
+	mc->use++;
+}
+EXPORT_SYMBOL_GPL(dect_dlc_mac_conn_bind);
+
+void dect_dlc_mac_conn_unbind(struct dect_mac_conn *mc)
+{
+	mc_debug(mc, "unbind use %u\n", mc->use);
+	if (--mc->use)
+		return;
+
+	if (mc->state == DECT_MAC_CONN_OPEN ||
+	    mc->state == DECT_MAC_CONN_OPEN_PENDING)
+		dect_mac_dis_req(mc->cl, mc->mcei);
+
+	dect_dlc_mac_conn_destroy(mc);
+}
+EXPORT_SYMBOL_GPL(dect_dlc_mac_conn_unbind);
+
+struct dect_mac_conn *dect_mac_conn_init(struct dect_cluster *cl,
+					 const struct dect_mci *mci,
+					 const struct dect_mbc_id *id)
+{
+	struct dect_mac_conn *mc;
+
+	mc = kzalloc(sizeof(*mc), GFP_ATOMIC);
+	if (mc == NULL)
+		return NULL;
+
+	mc->cl    = cl;
+	mc->mcei  = id != NULL ? id->mcei : dect_mbc_alloc_mcei(cl);
+	memcpy(&mc->mci, mci, sizeof(mc->mci));
+	mc->state = DECT_MAC_CONN_CLOSED;
+	mc_debug(mc, "init\n");
+
+	list_add_tail(&mc->list, &cl->mac_connections);
+	return mc;
+}
+
+static void dect_mac_conn_state_change(struct dect_mac_conn *mc,
+				       enum dect_mac_conn_states state)
+{
+	mc_debug(mc, "state change: %s (%u) -> %s (%u)\n",
+		 dect_mc_states[mc->state], mc->state,
+		 dect_mc_states[state], state);
+
+	mc->state = state;
+	dect_cplane_notify_state_change(mc);
+}
+
+int dect_dlc_mac_conn_establish(struct dect_mac_conn *mc)
+{
+	struct dect_mbc_id mid = {
+		.mcei		= mc->mcei,
+		.ari		= mc->mci.ari,
+		.pmid		= mc->mci.pmid,
+		.type		= DECT_MAC_CONN_BASIC,
+		.ecn		= mc->mci.lcn,
+	};
+	int err;
+
+	err = dect_mac_con_req(mc->cl, &mid);
+	if (err < 0)
+		return err;
+	dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN_PENDING);
+	return 0;
+}
+
+int dect_mac_con_cfm(struct dect_cluster *cl, u32 mcei,
+		     enum dect_mac_service_types service)
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (WARN_ON(mc == NULL))
+		return -ENOENT;
+	mc->service = service;
+
+	mc_debug(mc, "MAC_CON-cfm\n");
+	dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN);
+	return 0;
+}
+
+int dect_mac_con_ind(struct dect_cluster *cl, const struct dect_mbc_id *id,
+		     enum dect_mac_service_types service)
+{
+	struct dect_mac_conn *mc;
+	struct dect_mci mci = {
+		.ari		= id->ari,
+		.pmid		= id->pmid,
+		.lcn		= id->ecn & DECT_LCN_MASK,
+	};
+
+	mc = dect_mac_conn_init(cl, &mci, id);
+	if (mc == NULL)
+		return -ENOMEM;
+	mc->service = service;
+
+	mc_debug(mc, "MAC_CON-ind\n");
+	dect_mac_conn_state_change(mc, DECT_MAC_CONN_OPEN);
+	return 0;
+}
+
+int dect_dlc_mac_conn_enc_key_req(struct dect_mac_conn *mc, u64 ck)
+{
+	mc->ck = ck;
+	return dect_mac_enc_key_req(mc->cl, mc->mcei, ck);
+}
+
+int dect_dlc_mac_conn_enc_eks_req(struct dect_mac_conn *mc,
+				  enum dect_cipher_states status)
+{
+	return dect_mac_enc_eks_req(mc->cl, mc->mcei, status);
+}
+
+/* Encryption status change confirmation from CCF */
+void dect_mac_enc_eks_cfm(struct dect_cluster *cl, u32 mcei,
+			  enum dect_cipher_states status)
+
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (WARN_ON(mc == NULL))
+		return;
+	//dect_cplane_mac_enc_eks_ind(mc, status);
+}
+
+/* Encryption status change indication from CCF */
+void dect_mac_enc_eks_ind(struct dect_cluster *cl, u32 mcei,
+			  enum dect_cipher_states status)
+
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (WARN_ON(mc == NULL))
+		return;
+	mc_debug(mc, "MAC_ENC_EKS-ind: status: %u\n", status);
+	dect_cplane_mac_enc_eks_ind(mc, status);
+}
+
+/* Disconnection indication from CCF */
+int dect_mac_dis_ind(struct dect_cluster *cl, u32 mcei,
+		     enum dect_release_reasons reason)
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (WARN_ON(mc == NULL))
+		return -ENOENT;
+
+	mc_debug(mc, "MAC_DIS-ind: reason: %x\n", reason);
+	dect_mac_conn_state_change(mc, DECT_MAC_CONN_CLOSED);
+	/* If nothing is using the connection, release immediately */
+	if (mc->use == 0)
+		dect_dlc_mac_conn_destroy(mc);
+	else
+		dect_cplane_mac_dis_ind(mc, reason);
+	return 0;
+}
+
+/* Data indication from CCF */
+void dect_mac_co_data_ind(struct dect_cluster *cl, u32 mcei,
+			  enum dect_data_channels chan,
+			  struct sk_buff *skb)
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (WARN_ON(mc == NULL))
+		goto err;
+
+	mc_debug(mc, "MAC_CO_DATA-ind: chan: %u len: %u\n", chan, skb->len);
+	switch (chan) {
+	case DECT_MC_C_S:
+	case DECT_MC_C_F:
+		return dect_cplane_rcv(mc, chan, skb);
+	case DECT_MC_I_N:
+	case DECT_MC_I_P:
+		return dect_uplane_rcv(mc, chan, skb);
+	default:
+		goto err;
+	}
+err:
+	kfree_skb(skb);
+}
+
+/* Data-ready indication from CCF */
+struct sk_buff *dect_mac_co_dtr_ind(struct dect_cluster *cl, u32 mcei,
+				    enum dect_data_channels chan)
+{
+	struct dect_mac_conn *mc;
+
+	mc = dect_mac_conn_get_by_mcei(cl, mcei);
+	if (mc == NULL) {
+		if (net_ratelimit())
+			pr_debug("DLC: DTR no connection\n");
+		return NULL;
+	}
+
+	mc_debug(mc, "MAC_CO_DTR-ind: chan: %u\n", chan);
+	switch (chan) {
+	case DECT_MC_C_S:
+	case DECT_MC_C_F:
+		return dect_cplane_dtr(mc, chan);
+	case DECT_MC_I_N:
+	case DECT_MC_I_P:
+		return dect_uplane_dtr(mc, chan);
+	default:
+		return NULL;
+	}
+}
diff --git a/target/linux/generic/files/net/dect/dlc_b_sap.c b/target/linux/generic/files/net/dect/dlc_b_sap.c
new file mode 100644
index 0000000..004db42
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc_b_sap.c
@@ -0,0 +1,277 @@
+/*
+ * DECT DLC B SAP sockets - DLC C-plane broadcast service access
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/dect.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+
+static DEFINE_SPINLOCK(dect_bsap_lock);
+static HLIST_HEAD(dect_bsap_sockets);
+
+struct dect_bsap {
+	struct sock		sk;
+};
+
+static inline struct dect_bsap *dect_bsap(struct sock *sk)
+{
+	return (struct dect_bsap *)sk;
+}
+
+void dect_bsap_rcv(const struct dect_cluster *cl, struct sk_buff *skb)
+{
+	struct sk_buff *skb2;
+	struct sock *sk, *prev = NULL;
+
+	spin_lock(&dect_bsap_lock);
+	sk_for_each(sk, &dect_bsap_sockets) {
+		if (sk->sk_bound_dev_if &&
+		    sk->sk_bound_dev_if != cl->index)
+			continue;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 == NULL) {
+			sk->sk_err = -ENOMEM;
+			sk->sk_error_report(sk);
+			break;
+		}
+
+		if (prev != NULL) {
+			if (dect_sock_queue_rcv_skb(prev, skb2) < 0)
+				kfree_skb(skb2);
+		}
+		prev = sk;
+	}
+
+	if (prev == NULL || dect_sock_queue_rcv_skb(prev, skb) < 0)
+		kfree_skb(skb);
+
+	spin_unlock(&dect_bsap_lock);
+}
+
+static void dect_bsap_close(struct sock *sk, long timeout)
+{
+	sk_common_release(sk);
+}
+
+static int dect_bsap_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	const struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
+	int err;
+
+	if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
+		return -EINVAL;
+
+	if (addr->dect_index != 0 &&
+	    !dect_cluster_get_by_index(addr->dect_index))
+		return -ENODEV;
+
+	lock_sock(sk);
+	err = -EINVAL;
+	if (!sk_unhashed(sk))
+		goto out;
+
+	sk->sk_bound_dev_if = addr->dect_index;
+
+	spin_lock_bh(&dect_bsap_lock);
+	sk_add_node(sk, &dect_bsap_sockets);
+	spin_unlock_bh(&dect_bsap_lock);
+
+	err = 0;
+out:
+	release_sock(sk);
+	return err;
+}
+
+static void dect_bsap_unhash(struct sock *sk)
+{
+	if (sk_hashed(sk)) {
+		spin_lock_bh(&dect_bsap_lock);
+		sk_del_node_init(sk);
+		spin_unlock_bh(&dect_bsap_lock);
+	}
+}
+
+static int dect_bsap_getname(struct sock *sk, struct sockaddr *uaddr, int *len,
+			     int peer)
+{
+	struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
+
+	if (peer)
+		return -EOPNOTSUPP;
+
+	addr->dect_family = AF_DECT;
+	addr->dect_index  = sk->sk_bound_dev_if;
+	*len = sizeof(*addr);
+	return 0;
+}
+
+static int dect_bsap_recvmsg(struct kiocb *iocb, struct sock *sk,
+			     struct msghdr *msg, size_t len,
+			     int noblock, int flags, int *addrlen)
+{
+	struct sockaddr_dect *addr;
+	struct dect_bsap_auxdata aux;
+	struct sk_buff *skb;
+	size_t copied = 0;
+	int err;
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (skb == NULL)
+		goto out;
+
+	//msg->msg_flags |= DECT_LB_CB(skb)->expedited ? MSG_OOB : 0;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err < 0)
+		goto out_free;
+
+	if (msg->msg_name != NULL) {
+		addr = (struct sockaddr_dect *)msg->msg_name;
+		addr->dect_family = AF_DECT;
+		addr->dect_index = DECT_SK_CB(skb)->index;
+		msg->msg_namelen = sizeof(*addr);
+	}
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	aux.long_page = DECT_BMC_CB(skb)->long_page;
+	put_cmsg(msg, SOL_DECT, DECT_BSAP_AUXDATA, sizeof(aux), &aux);
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err ? : copied;
+}
+
+static int dect_bsap_sendmsg(struct kiocb *kiocb, struct sock *sk,
+			     struct msghdr *msg, size_t len)
+{
+	const struct sockaddr_dect *addr = msg->msg_name;
+	bool expedited = msg->msg_flags & MSG_OOB;
+	struct dect_cluster *cl;
+	struct sk_buff *skb;
+	struct cmsghdr *cmsg;
+	struct dect_bsap_auxdata *aux;
+	bool long_page = false;
+	int index;
+	int err;
+
+	if (msg->msg_namelen) {
+		if (addr->dect_family != AF_DECT)
+			return -EINVAL;
+		index = addr->dect_index;
+	} else
+		index = sk->sk_bound_dev_if;
+
+	/* Transmission is always in direction FP -> PP */
+	cl = dect_cluster_get_by_index(index);
+	if (cl == NULL)
+		return -ENODEV;
+	if (cl->mode != DECT_MODE_FP)
+		return -EOPNOTSUPP;
+
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (!CMSG_OK(msg, cmsg))
+			return -EINVAL;
+		if (cmsg->cmsg_level != SOL_DECT)
+			continue;
+
+		switch (cmsg->cmsg_type) {
+		case DECT_BSAP_AUXDATA:
+			if (cmsg->cmsg_len != CMSG_LEN(sizeof(*aux)))
+				return -EINVAL;
+			aux = (struct dect_bsap_auxdata *)CMSG_DATA(cmsg);
+			long_page = aux->long_page;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Valid frame sizes are 3 bytes (short frame), 5 bytes (long frame)
+	 * or multiples of 5 bytes up to 30 bytes (extended frame). Extended
+	 * frames can not use expedited operation. */
+	if (len == DECT_LB_SHORT_FRAME_SIZE) {
+		if (long_page)
+			return -EINVAL;
+	} else if (len % DECT_LB_LONG_FRAME_SIZE == 0) {
+		if (len == 0 || len > DECT_LB_EXTENDED_FRAME_SIZE_MAX)
+			return -EMSGSIZE;
+		if (len > DECT_LB_LONG_FRAME_SIZE && !long_page)
+			return -EINVAL;
+		if (expedited)
+			return -EOPNOTSUPP;
+	} else
+		return -EINVAL;
+
+	skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto err1;
+	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+	if (err < 0)
+		goto err2;
+	DECT_BMC_CB(skb)->long_page = long_page;
+	DECT_BMC_CB(skb)->fast_page = expedited;
+	dect_bmc_mac_page_req(cl, skb);
+	return len;
+
+err2:
+	kfree_skb(skb);
+err1:
+	return err;
+}
+
+static struct dect_proto dect_bsap_proto __read_mostly = {
+	.type		= SOCK_DGRAM,
+	.protocol	= DECT_B_SAP,
+	.capability	= CAP_NET_RAW,
+	.ops		= &dect_dgram_ops,
+	.proto.name	= "DECT_B_SAP",
+	.proto.owner	= THIS_MODULE,
+	.proto.obj_size	= sizeof(struct dect_bsap),
+	.proto.close	= dect_bsap_close,
+	.proto.bind	= dect_bsap_bind,
+	.proto.unhash	= dect_bsap_unhash,
+	.proto.recvmsg	= dect_bsap_recvmsg,
+	.proto.sendmsg	= dect_bsap_sendmsg,
+	.getname	= dect_bsap_getname,
+};
+
+int __init dect_bsap_module_init(void)
+{
+	return dect_proto_register(&dect_bsap_proto);
+}
+
+void dect_bsap_module_exit(void)
+{
+	dect_proto_unregister(&dect_bsap_proto);
+}
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT DLC B SAP sockets");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_B_SAP);
diff --git a/target/linux/generic/files/net/dect/dlc_cplane.c b/target/linux/generic/files/net/dect/dlc_cplane.c
new file mode 100644
index 0000000..9365b9d
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc_cplane.c
@@ -0,0 +1,981 @@
+/*
+ * DECT DLC C-plane
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+
+void dect_mac_page_ind(struct dect_cluster *cl, struct sk_buff *skb)
+{
+	dect_bsap_rcv(cl, skb);
+}
+
+static void dect_fa_parse_len(struct dect_fa_len *len, const struct sk_buff *skb)
+{
+	u8 l;
+
+	l = skb->data[DECT_FA_LI_OFF];
+	len->len  = (l & DECT_FA_LI_LENGTH_MASK) >> DECT_FA_LI_LENGTH_SHIFT;
+	len->more = (l & DECT_FA_LI_M_FLAG);
+}
+
+/*
+ * LAPC entity
+ */
+
+#define lapc_debug(lapc, fmt, args...) \
+	pr_debug("LAPC (MCEI: %u LLN: %u): " fmt, \
+		 (lapc)->lc->mc->mcei, (lapc)->dli.lln, ## args)
+
+static inline u8 lapc_seq_add(const struct dect_lapc *lapc, u8 s1, u8 s2)
+{
+	return (s1 + s2) & (lapc->mod - 1);
+}
+
+static inline bool dect_fa_seq_before(const struct dect_lapc *lapc, u8 s1, u8 s2)
+{
+	if (lapc->window == 1)
+		return s1 != s2;
+	else
+		return (s8)((s2 << 5) - (s1 << 5)) > 0;
+}
+
+static inline bool dect_fa_seq_after(const struct dect_lapc *lapc, u8 s1, u8 s2)
+{
+	return dect_fa_seq_before(lapc, s2, s1);
+}
+
+static void dect_lapc_transmit_skb(struct dect_lapc *lapc)
+{
+	struct sk_buff *skb = skb_peek(&lapc->retransmit_queue);
+	struct dect_fa_hdr *fh;
+
+	skb = skb_clone(skb, GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+
+	fh = (struct dect_fa_hdr *)skb->data;
+	lapc_debug(lapc, "queue I-frame: v_a: %u v_r: %u v_s: %u "
+		   "len: %u addr: %02x ctrl: %02x\n", lapc->v_a, lapc->v_r,
+		   lapc->v_s, skb->len, fh->addr, fh->ctrl);
+	skb_queue_tail(&lapc->lc->txq, skb);
+}
+
+static void dect_lapc_error_report(struct dect_lapc *lapc, int err)
+{
+	struct sock *sk = lapc->sk;
+
+	lapc_debug(lapc, "socket error: %d\n", err);
+	sk->sk_err = err;
+	sk->sk_error_report(sk);
+}
+
+static void dect_lapc_state_change(struct dect_lapc *lapc, int state)
+{
+	struct sock *sk = lapc->sk;
+
+	lapc_debug(lapc, "socket state change: %d\n", state);
+	sk->sk_state = state;
+	sk->sk_state_change(sk);
+}
+
+/**
+ * dect_lapc_timeout - retransmission timer
+ *
+ * Handle missing acknowledgements:
+ *
+ * - If not already in timer recovery condition, enter it
+ * - otherwise add one to retransmission count
+ *
+ * If the retransmission count is below the maximum, restart the timer and
+ * send an "appropriate" S-frame acknowledgement or retransmit the last
+ * I-frame, in both cases with the poll bit set.
+ */
+static void dect_lapc_timeout(unsigned long data)
+{
+	struct dect_lapc *lapc = (struct dect_lapc *)data;
+
+	lapc_debug(lapc, "retransmission timer: cnt: %u\n", lapc->retransmit_cnt);
+	if (lapc->retransmit_cnt++ < DECT_LAPC_RETRANSMIT_MAX) {
+		dect_lapc_transmit_skb(lapc);
+		mod_timer(&lapc->timer, jiffies + DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT);
+	} else
+		dect_lapc_error_report(lapc, ETIMEDOUT);
+}
+
+static bool dect_lapc_done(const struct dect_lapc *lapc)
+{
+	return skb_queue_empty(&lapc->sk->sk_write_queue) &&
+	       skb_queue_empty(&lapc->retransmit_queue);
+}
+
+void dect_lapc_destroy(struct dect_lapc *lapc)
+{
+	lapc_debug(lapc, "destroy\n");
+
+	del_timer_sync(&lapc->timer);
+	skb_queue_purge(&lapc->retransmit_queue);
+	dect_lc_unbind(lapc->lc, lapc);
+	sock_put(lapc->sk);
+	kfree(lapc);
+}
+
+static void dect_lapc_reset(struct dect_lapc *lapc)
+{
+	lapc->nlf = true;
+	lapc->v_s = 0;
+	lapc->v_a = 0;
+	lapc->v_r = 0;
+}
+
+/**
+ * dect_lapc_init - initialize a new LAPC entity
+ */
+struct dect_lapc *dect_lapc_init(struct sock *sk, const struct dect_dli *dli,
+				 enum dect_sapis sapi, struct dect_lc *lc,
+				 gfp_t gfp)
+{
+	struct dect_lapc *lapc;
+
+	lapc = kzalloc(sizeof(*lapc), gfp);
+	if (lapc == NULL)
+		return NULL;
+
+	lapc->sk = sk;
+	sock_hold(sk);
+
+	memcpy(&lapc->dli, dli, sizeof(lapc->dli));
+	lapc->sapi = sapi;
+	lapc->state = DECT_LAPC_ULI;
+	skb_queue_head_init(&lapc->retransmit_queue);
+
+	lapc->lc = lc;
+	setup_timer(&lapc->timer, dect_lapc_timeout, (unsigned long)lapc);
+	lapc->cmd = (lc->mc->cl->mode == DECT_MODE_FP) ? true : false;
+
+	switch (lapc->dli.lln) {
+	case DECT_LLN_CLASS_U:
+		break;
+	case DECT_LLN_CLASS_A:
+		lapc->window = DECT_LAPC_CLASS_A_WINDOW;
+		lapc->mod = DECT_LAPC_CLASS_A_MOD;
+		break;
+	default:
+		lapc->window = DECT_LAPC_CLASS_B_INITIAL_WINDOW;
+		lapc->mod = DECT_LAPC_CLASS_B_MOD;
+		break;
+	}
+
+	dect_lapc_reset(lapc);
+
+	lapc_debug(lapc, "init\n");
+	return lapc;
+}
+
+#define DECT_FA_FRAME_RESERVE	16
+#define DECT_FA_FRAME_SPACE	16
+
+static struct sk_buff *dect_lapc_alloc_skb(struct dect_lapc *lapc)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(DECT_FA_FRAME_SPACE + DECT_FA_FRAME_RESERVE, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+	skb_reset_mac_header(skb);
+	skb_reserve(skb, DECT_FA_FRAME_RESERVE);
+	skb_reserve(skb, DECT_FA_HDR_SIZE);
+	skb_reset_network_header(skb);
+	return skb;
+}
+
+static struct dect_fa_hdr *dect_prepare_fa_frame(const struct dect_lapc *lapc,
+						 bool command,
+						 struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh;
+	u8 ilen = skb->len;
+
+	fh = (struct dect_fa_hdr *)skb_push(skb, DECT_FA_HDR_SIZE);
+	fh->addr  = lapc->dli.lln << DECT_FA_ADDR_LLN_SHIFT;
+	fh->addr |= lapc->sapi << DECT_FA_ADDR_SAPI_SHIFT;
+	fh->addr |= DECT_FA_ADDR_RES_BIT;
+	fh->addr |= (command ? lapc->cmd : !lapc->cmd) ? DECT_FA_ADDR_CR_FLAG : 0;
+	fh->addr |= lapc->nlf ? DECT_FA_ADDR_NLF_FLAG : 0;
+	fh->ctrl  = 0;
+	fh->li    = ilen << DECT_FA_LI_LENGTH_SHIFT;
+	fh->li	 |= DECT_FA_LI_EXT_FLAG;
+	return fh;
+}
+
+static bool dect_lapc_send_iframe(struct dect_lapc *lapc, bool pf)
+{
+	struct dect_fa_hdr *fh;
+	struct sk_buff *skb;
+
+	/* Window size full? */
+	lapc_debug(lapc, "send I-frame: v_a: %u window: %u v_s: %u\n",
+		   lapc->v_a, lapc->window, lapc->v_s);
+	if (lapc_seq_add(lapc, lapc->v_a, lapc->window) == lapc->v_s)
+		return false;
+
+	/* Prepare a new I-frame */
+	skb = skb_dequeue(&lapc->sk->sk_write_queue);
+	if (skb == NULL)
+		return false;
+	fh = dect_prepare_fa_frame(lapc, true, skb);
+	fh->ctrl |= DECT_FA_CTRL_I_FMT_ID;
+	fh->ctrl |= lapc->v_r << DECT_FA_CTRL_I_NR_SHIFT;
+	fh->ctrl |= lapc->v_s << DECT_FA_CTRL_I_NS_SHIFT;
+	fh->ctrl |= pf ? DECT_FA_CTRL_I_P_FLAG : 0;
+
+	/* Append to retransmission queue and (re)start retransmission timer */
+	skb_queue_tail(&lapc->retransmit_queue, skb);
+	if (!timer_pending(&lapc->timer))
+		mod_timer(&lapc->timer, jiffies + DECT_LAPC_RETRANSMISSION_TIMEOUT);
+
+	lapc->v_s = lapc_seq_add(lapc, lapc->v_s, 1);
+
+	dect_lapc_transmit_skb(lapc);
+	return true;
+}
+
+/*
+ * Send a S-frame with the specified command. The command/response bit setting
+ * depends on the role of the LAPC, a PP uses 0 for commands and 1 for responses,
+ * a FT 1 for commands and 0 for responses.
+ */
+static bool dect_lapc_send_sframe(struct dect_lapc *lapc, u8 cr,
+				  bool command, bool pf)
+{
+	struct dect_fa_hdr *fh;
+	struct sk_buff *skb;
+
+	skb = dect_lapc_alloc_skb(lapc);
+	if (skb == NULL)
+		return false;
+
+	fh = dect_prepare_fa_frame(lapc, command, skb);
+	fh->ctrl |= DECT_FA_CTRL_S_FMT_ID;
+	fh->ctrl |= lapc->v_r << DECT_FA_CTRL_S_NR_SHIFT;
+	fh->ctrl |= cr;
+	fh->ctrl |= pf ? DECT_FA_CTRL_S_PF_FLAG : 0;
+
+	lapc_debug(lapc, "queue S-frame: v_r: %u len: %u addr: %02x ctrl: %02x\n",
+		   lapc->v_r, skb->len, fh->addr, fh->ctrl);
+	skb_queue_tail(&lapc->lc->txq, skb);
+
+	lapc->nlf = false;
+	return true;
+}
+
+/*
+ * Send an acknowledgement frame. Class B entities use RNR responses to indicate
+ * their status while busy. Otherwise an I-frame is used when data is available
+ * and a RR response frame otherwise.
+ */
+static void dect_lapc_send_ack(struct dect_lapc *lapc, bool pf)
+{
+	lapc_debug(lapc, "send ACK: I-frame present: %u\n",
+		   skb_peek(&lapc->sk->sk_write_queue) ? 1 : 0);
+	if (lapc->dli.lln != DECT_LLN_CLASS_A && lapc->busy)
+		dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RNR, false, false);
+	else if (!lapc->peer_busy && skb_peek(&lapc->sk->sk_write_queue))
+		dect_lapc_send_iframe(lapc, pf);
+	else
+		dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RR, false, pf);
+}
+
+static void dect_lapc_queue_data(struct dect_lapc *lapc, struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
+
+	skb_pull(skb, DECT_FA_HDR_SIZE);
+	if (skb->len == 0) {
+		kfree_skb(skb);
+		return;
+	}
+	lapc_debug(lapc, "reassemble message: segment len: %u more: %u\n",
+		   skb->len, (fh->li & DECT_FA_LI_M_FLAG) ? 1 : 0);
+
+	lapc->rcv_head = skb_append_frag(lapc->rcv_head, skb);
+	if (!(fh->li & DECT_FA_LI_M_FLAG)) {
+		skb = lapc->rcv_head;
+		lapc->rcv_head = NULL;
+		lapc_debug(lapc, "reassembled message: len: %u\n", skb->len);
+		sock_queue_rcv_skb(lapc->sk, skb);
+	}
+}
+
+static bool dect_lapc_update_ack(struct dect_lapc *lapc, u8 seq)
+{
+	u8 v_a = lapc->v_a;
+
+	lapc_debug(lapc, "update ACK: v_a: %u v_s: %u seq: %u\n",
+		   lapc->v_a, lapc->v_s, seq);
+#if 0
+	lapc_debug(lapc, "seq %u after v_a %u: %u\n", seq, lapc->v_a,
+		   dect_fa_seq_after(lapc, seq, lapc->v_a));
+	lapc_debug(lapc, "v_s %u !after seq %u: %u\n", lapc->v_s, seq,
+		   !dect_fa_seq_after(lapc, lapc->v_s, seq));
+#endif
+
+	/* If all outstanding I-frames have been acknowledged, stop
+	 * retransmission timer, otherwise reset it.
+	 */
+	if (dect_fa_seq_after(lapc, seq, lapc->v_a) &&
+	    !dect_fa_seq_after(lapc, lapc->v_s, seq)) {
+		lapc->v_a = seq;
+		if (lapc->v_a == lapc->v_s) {
+			del_timer_sync(&lapc->timer);
+			lapc->retransmit_cnt = 0;
+		} else
+			mod_timer(&lapc->timer, jiffies + DECT_LAPC_RETRANSMISSION_TIMEOUT);
+	} else if (seq != lapc->v_a)
+		return false;
+
+	/* Purge acknowledged frames from transmit queue */
+	while (v_a != lapc->v_a) {
+		lapc_debug(lapc, "purge retransmit queue: seq: %u\n", v_a);
+		kfree_skb(skb_dequeue(&lapc->retransmit_queue));
+		v_a = lapc_seq_add(lapc, v_a, 1);
+	}
+
+	if (lapc->sk->sk_state == DECT_SK_RELEASE_PENDING &&
+	    dect_lapc_done(lapc)) {
+		dect_lapc_state_change(lapc, DECT_SK_RELEASED);
+		dect_lapc_destroy(lapc);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * Receive a Class A or Class B I-frame. Frames with valid sequence numbers
+ * are acknowledged and queued for segment reassembly. Invalid sequence
+ * numbers cause an ACK with the expected sequence number to be sent.
+ *
+ * Class B entities need to indicate their receiver busy status when busy or
+ * when explicitly polled.
+ */
+static void dect_lapc_rcv_iframe(struct dect_lapc *lapc, struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
+	bool poll = false;
+	u8 n_s, n_r, res;
+
+	if (lapc->dli.lln == DECT_LLN_CLASS_U) {
+		kfree_skb(skb);
+		return;
+	}
+
+	if (fh->addr & DECT_FA_ADDR_NLF_FLAG)
+		dect_lapc_reset(lapc);
+
+	n_r = (fh->ctrl & DECT_FA_CTRL_I_NR_MASK) >> DECT_FA_CTRL_I_NR_SHIFT;
+	n_s = (fh->ctrl & DECT_FA_CTRL_I_NS_MASK) >> DECT_FA_CTRL_I_NS_SHIFT;
+	if (lapc->dli.lln != DECT_LLN_CLASS_A)
+		poll = fh->ctrl & DECT_FA_CTRL_I_P_FLAG;
+
+	lapc_debug(lapc, "receive I-frame: n_r: %u n_s: %u poll: %u\n",
+		   n_r, n_s, poll);
+	dect_lapc_update_ack(lapc, n_r);
+
+	/* While in receiver busy condition, all I-frames are dropped after
+	 * updating the acknowledgement number. In Class B mode receiver status
+	 * queries are still answered.
+	 */
+	if (lapc->busy) {
+		kfree_skb(skb);
+		if (poll)
+			goto poll;
+		return;
+	}
+
+	/* When the frame contains an invalid sequence number, send an
+	 * immediate ACK. */
+	if (n_s != lapc->v_r) {
+		lapc_debug(lapc, "invalid sequence number %u %u\n", n_s, lapc->v_r);
+		kfree_skb(skb);
+		goto ack;
+	}
+
+	lapc->v_r = lapc_seq_add(lapc, lapc->v_r, 1);
+	dect_lapc_queue_data(lapc, skb);
+	if (poll)
+		goto poll;
+ack:
+	return dect_lapc_send_ack(lapc, poll);
+
+poll:
+	res = lapc->busy ? DECT_FA_CTRL_S_CR_RNR : DECT_FA_CTRL_S_CR_RR;
+	dect_lapc_send_sframe(lapc, res, false, true);
+}
+
+static void dect_lapc_rcv_sframe(struct dect_lapc *lapc, struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
+	bool pf;
+	u8 n_r;
+
+	n_r = (fh->ctrl & DECT_FA_CTRL_S_NR_MASK) >> DECT_FA_CTRL_S_NR_SHIFT;
+	pf  = (fh->ctrl & DECT_FA_CTRL_S_PF_FLAG);
+	lapc_debug(lapc, "receive S-frame: n_r: %u pf: %u\n", n_r, pf);
+
+	switch (fh->ctrl & DECT_FA_CTRL_S_CR_MASK) {
+	case DECT_FA_CTRL_S_CR_RR:
+		if (!dect_lapc_update_ack(lapc, n_r))
+			goto err;
+
+		if (lapc->lc->elapc == lapc) {
+			/* Connection establishment completed */
+			lapc_debug(lapc, "established\n");
+			lapc->lc->elapc = NULL;
+			del_timer_sync(&lapc->timer);
+			dect_lapc_state_change(lapc, DECT_SK_ESTABLISHED);
+		}
+
+		dect_lapc_send_iframe(lapc, pf);
+		break;
+	case DECT_FA_CTRL_S_CR_RNR:
+		/*
+		 * Note peer receiver busy condition. If it was a RNR command
+		 * with the P bit set to 1, send a RR response with the F bit
+		 * set to 1. If it was a RNR response with the F bit set to 1,
+		 * clear timer recovery condition and update V(S).
+		 */
+		lapc->peer_busy = true;
+
+		if (fh->addr & DECT_FA_ADDR_CR_FLAG && pf)
+			dect_lapc_send_sframe(lapc, DECT_FA_CTRL_S_CR_RR, true, true);
+		else if (!(fh->addr & DECT_FA_ADDR_CR_FLAG) && pf) {
+			del_timer_sync(&lapc->timer);
+			lapc->v_s = n_r;
+		}
+
+		dect_lapc_update_ack(lapc, n_r);
+		break;
+	case DECT_FA_CTRL_S_CR_REJ:
+		lapc->peer_busy = false;
+		lapc->v_s = n_r;
+		lapc->v_a = n_r;
+		del_timer_sync(&lapc->timer);
+		break;
+	default:
+		goto err;
+	}
+
+err:
+	kfree_skb(skb);
+}
+
+static void dect_lapc_rcv_uframe(struct dect_lapc *lapc, struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
+	u8 pf, cr;
+
+	pf = (fh->ctrl & DECT_FA_CTRL_U_PF_FLAG);
+	cr = (fh->ctrl & DECT_FA_CTRL_U_U1_MASK) |
+	     (fh->ctrl & DECT_FA_CTRL_U_CR_MASK);
+
+	/* unnumbered information is only valid in class U mode */
+	if (cr == DECT_FA_CTRL_U_CR_UI) {
+		if (lapc->dli.lln != DECT_LLN_CLASS_U)
+			goto err;
+		lapc_debug(lapc, "queue UI message: len: %u\n", skb->len);
+		sock_queue_rcv_skb(lapc->sk, skb);
+		return;
+	}
+
+	/* the remaining commands/responses are only valid in class B mode */
+	if (lapc->dli.lln == DECT_LLN_CLASS_A)
+		goto err;
+
+	switch (cr) {
+	case DECT_FA_CTRL_U_CR_SABM:
+		break;
+	case DECT_FA_CTRL_U_CR_DM:
+		break;
+	case DECT_FA_CTRL_U_CR_DISC:
+		break;
+	case DECT_FA_CTRL_U_CR_UA:
+		break;
+	}
+
+err:
+	kfree_skb(skb);
+}
+
+static void dect_lapc_rcv(struct dect_lapc *lapc, struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh = (struct dect_fa_hdr *)skb->data;
+
+	if ((fh->ctrl & DECT_FA_CTRL_I_FMT_MASK) == DECT_FA_CTRL_I_FMT_ID)
+		return dect_lapc_rcv_iframe(lapc, skb);
+	else if ((fh->ctrl & DECT_FA_CTRL_S_FMT_MASK) == DECT_FA_CTRL_S_FMT_ID)
+		return dect_lapc_rcv_sframe(lapc, skb);
+	else if ((fh->ctrl & DECT_FA_CTRL_U_FMT_MASK) == DECT_FA_CTRL_U_FMT_ID)
+		return dect_lapc_rcv_uframe(lapc, skb);
+	else
+		kfree_skb(skb);
+}
+
+int dect_lapc_transmit(struct dect_lapc *lapc)
+{
+	dect_lapc_send_iframe(lapc, 0);
+	return 0;
+}
+
+int dect_lapc_establish(struct dect_lapc *lapc)
+{
+	struct sk_buff *skb;
+
+	lapc_debug(lapc, "establish\n");
+
+	/* Prepend zero-sized message to transmit queue to trigger connection
+	 * establishment.
+	 */
+	skb = dect_lapc_alloc_skb(lapc);
+	if (skb == NULL)
+		return -ENOMEM;
+	skb_queue_head(&lapc->sk->sk_write_queue, skb);
+
+	lapc->lc->elapc = lapc;
+	dect_lapc_send_iframe(lapc, lapc->dli.lln != DECT_LLN_CLASS_A);
+	lapc->nlf = false;
+
+	mod_timer(&lapc->timer, jiffies + DECT_LAPC_CLASS_A_ESTABLISH_TIMEOUT);
+	return 0;
+}
+
+/*
+ * Initiate link release.
+ */
+void dect_lapc_release(struct dect_lapc *lapc, bool normal)
+{
+	lapc_debug(lapc, "release: normal: %u\n", normal);
+	if (dect_lapc_done(lapc) || !normal) {
+		lapc->sk->sk_state = DECT_SK_RELEASED;
+		dect_lapc_destroy(lapc);
+	} else
+		dect_lapc_state_change(lapc, DECT_SK_RELEASE_PENDING);
+}
+
+/*
+ * Lc entity
+ *
+ * The Lc entity receives and transmits LAPC frames from/to the MAC layer.
+ *
+ * For transmission the frames are checksummed and fragmented into channel
+ * sized units. The channel is chosen before transmission of a new frame
+ * based on availability and demand. All fragments of one frame are
+ * transmitted in the chosen channel.
+ *
+ * Received fragments are resegmented and have their checksum validated,
+ * then routed to the LAPC entity associated with the logical link number.
+ */
+
+#define lc_debug(lc, fmt, args...) \
+	pr_debug("Lc (MCEI %u): " fmt, (lc)->mc->mcei, ## args)
+
+void dect_lc_destroy(struct dect_lc *lc)
+{
+	lc_debug(lc, "destroy\n");
+	dect_dlc_mac_conn_unbind(lc->mc);
+	kfree_skb(lc->rx_head);
+	kfree_skb(lc->tx_head);
+	__skb_queue_purge(&lc->txq);
+	kfree(lc);
+}
+
+static void dect_lc_put(struct dect_lc *lc)
+{
+	if (--lc->use > 0)
+		return;
+	dect_lc_destroy(lc);
+}
+
+static void dect_lc_hold(struct dect_lc *lc)
+{
+	lc->use++;
+}
+
+void dect_lc_unbind(struct dect_lc *lc, struct dect_lapc *lapc)
+{
+	lc_debug(lc, "unbind LLN: %u use: %u\n", lapc->dli.lln, lc->use);
+	if (WARN_ON(lc->lapcs[lapc->dli.lln] == NULL))
+		return;
+
+	lc->lapcs[lapc->dli.lln] = NULL;
+	dect_lc_put(lc);
+}
+
+void dect_lc_bind(struct dect_lc *lc, struct dect_lapc *lapc)
+{
+	lc_debug(lc, "bind LLN: %u use: %u\n", lapc->dli.lln, lc->use);
+
+	lc->lapcs[lapc->dli.lln] = lapc;
+	dect_lc_hold(lc);
+}
+
+struct dect_lc *dect_lc_init(struct dect_mac_conn *mc, gfp_t gfp)
+{
+	struct dect_lc *lc;
+
+	lc = kzalloc(sizeof(*lc), gfp);
+	if (lc == NULL)
+		return NULL;
+
+	lc->mc = mc;
+	dect_dlc_mac_conn_bind(mc);
+
+	lc_debug(lc, "init\n");
+	skb_queue_head_init(&lc->txq);
+	switch (mc->mci.pmid.type) {
+	case DECT_PMID_ASSIGNED:
+		lc->lsig = dect_build_pmid(&mc->mci.pmid);
+		break;
+	default:
+		lc->lsig = 0;
+		break;
+	}
+
+	return lc;
+}
+
+static void dect_fa_frame_csum(const struct dect_lc *lc, struct sk_buff *skb)
+{
+	u8 *data = skb->data;
+	unsigned int i;
+	u8 c0 = 0, c1 = 0;
+	u8 x, y;
+	u16 t;
+
+	data[skb->len - 2] = 0;
+	data[skb->len - 1] = 0;
+
+	for (i = 0; i < skb->len; i++) {
+		t = c0 + data[i];
+		c0 = (t & 0xffU) + ((t >> 8) & 0x1U);
+		t = c1 + c0;
+		c1 = (t & 0xffU) + ((t >> 8) & 0x1U);
+	}
+
+	t = c0 + (u8)~c1;
+	x = (t & 0xffU) + ((t >> 8) & 0x1U);
+
+	t = (u8)~c0 + (u8)~c0;
+	t = (t & 0xffU) + ((t >> 8) & 0x1U);
+	t += c1;
+	y = (t & 0xffU) + ((t >> 8) & 0x1U);
+
+	data[skb->len - 2] = x ^ (lc->lsig >> 8);
+	data[skb->len - 1] = y ^ (lc->lsig & 0xff);
+	lc_debug(lc, "checksum: lsig: %.4x x: %.2x y: %.2x\n",
+		 lc->lsig, x, y);
+}
+
+static bool dect_fa_frame_csum_verify(const struct dect_lc *lc,
+				      struct sk_buff *skb)
+{
+	u8 *data = skb->data;
+	unsigned int i;
+	u8 c0 = 0, c1 = 0;
+	u16 t;
+
+	data[skb->len - 2] ^= lc->lsig >> 8;
+	data[skb->len - 1] ^= lc->lsig & 0xff;
+
+	for (i = 0; i < skb->len; i++) {
+		t = c0 + data[i];
+		c0 = (t & 0xffU) + ((t >> 8) & 0x1U);
+		t = c1 + c0;
+		c1 = (t & 0xffU) + ((t >> 8) & 0x1U);
+	}
+
+	lc_debug(lc, "csum verify: lsig %.4x c0: %.2x c1: %.2x\n",
+		 lc->lsig, c0, c1);
+	return c0 == (u8)~0 && c1 == (u8)~0;
+}
+
+static const u8 channel_sdu_size[] = {
+	[DECT_MC_C_S]	= DECT_C_S_SDU_SIZE,
+	[DECT_MC_C_F]	= DECT_C_F_SDU_SIZE,
+};
+
+/*
+ * Prepare a DLC frame for transmission to the MAC layer. This involves
+ * checksumming the frame, selecting the logical channel for transmission
+ * and fragmenting it into units carried by the logical channel.
+ */
+static struct sk_buff *dect_lc_tx(struct dect_lc *lc)
+{
+	struct sk_buff *skb, *frag;
+	u8 *fill, fill_len;
+	u8 flen;
+
+	skb = lc->tx_head;
+	if (skb == NULL) {
+		skb = skb_dequeue(&lc->txq);
+		if (skb == NULL)
+			return NULL;
+		lc_debug(lc, "tx: begin new frame len: %u\n", skb->len);
+
+		flen = channel_sdu_size[DECT_MC_C_S];
+		fill_len = roundup(skb->len + DECT_FA_CSUM_SIZE, flen) -
+			   (skb->len + DECT_FA_CSUM_SIZE);
+		fill = skb_put(skb, fill_len);
+		memset(fill, DECT_FA_FILL_PATTERN, fill_len);
+
+		skb_put(skb, DECT_FA_CSUM_SIZE);
+		dect_fa_frame_csum(lc, skb);
+
+		lc->tx_head = skb;
+		lc->tx_len = flen;
+	}
+
+	/* Fragment into tx_len sized units */
+	if (skb->len > lc->tx_len) {
+		frag = skb_copy(skb, GFP_ATOMIC);
+		if (frag == NULL)
+			return NULL;
+		skb_trim(frag, lc->tx_len);
+		skb_pull(skb, lc->tx_len);
+	} else {
+		frag = lc->tx_head;
+		lc->tx_head = NULL;
+	}
+
+	lc_debug(lc, "tx: %sfragment len: %u\n",
+		 lc->tx_head ? "" : "last ", frag->len);
+	return frag;
+}
+
+static struct sk_buff *dect_lc_reassemble(struct dect_lc *lc,
+					  enum dect_data_channels chan,
+					  struct sk_buff *skb)
+{
+	struct dect_fa_len fl;
+	u8 flen, len;
+
+	if (lc->rx_head == NULL) {
+		dect_fa_parse_len(&fl, skb);
+		len = fl.len;
+		len += DECT_FA_HDR_SIZE + DECT_FA_CSUM_SIZE;
+
+		flen = channel_sdu_size[chan];
+		lc->rx_len = roundup(len, flen);
+		lc_debug(lc, "new SDU: len: %u flen: %u\n", len, flen);
+	}
+
+	lc->rx_head = skb_append_frag(lc->rx_head, skb);
+	skb = NULL;
+
+	if (lc->rx_head->len >= lc->rx_len) {
+		WARN_ON(lc->rx_head->len != lc->rx_len);
+		skb = lc->rx_head;
+		lc->rx_head = NULL;
+
+		if (skb_linearize(skb) < 0)
+			goto err;
+		if (!dect_fa_frame_csum_verify(lc, skb))
+			goto err;
+
+		/* Trim checksum and filling */
+		dect_fa_parse_len(&fl, skb);
+		skb_trim(skb, fl.len + DECT_FA_HDR_SIZE);
+		lc_debug(lc, "reassembled SDU: len: %u\n", skb->len);
+	}
+
+	return skb;
+
+err:
+	lc_debug(lc, "reassembly failed\n");
+	kfree_skb(skb);
+	return NULL;
+}
+
+static void dect_lc_rcv(struct dect_lc *lc, enum dect_data_channels chan,
+			struct sk_buff *skb)
+{
+	struct dect_fa_hdr *fh;
+	struct dect_lapc *lapc;
+	struct dect_dli dli;
+	enum dect_sapis sapi;
+
+	skb = dect_lc_reassemble(lc, chan, skb);
+	if (skb == NULL)
+		return;
+	fh = (struct dect_fa_hdr *)skb->data;
+
+	dli.lln = (fh->addr & DECT_FA_ADDR_LLN_MASK) >> DECT_FA_ADDR_LLN_SHIFT;
+	lc_debug(lc, "receive: LLN %u NLF %u SAPI %u\n",
+		 dli.lln, (fh->addr & DECT_FA_ADDR_NLF_FLAG) ? 1 : 0,
+		 (fh->addr & DECT_FA_ADDR_SAPI_MASK) >> DECT_FA_ADDR_SAPI_SHIFT);
+
+	if (lc->lapcs[dli.lln] != NULL)
+		return dect_lapc_rcv(lc->lapcs[dli.lln], skb);
+
+	/* Link establishment: new requests are only valid while no link
+	 * establishment is in progress.
+	 */
+	if (!(fh->addr & DECT_FA_ADDR_NLF_FLAG))
+		goto err;
+	if ((fh->ctrl & DECT_FA_CTRL_I_FMT_MASK) != DECT_FA_CTRL_I_FMT_ID)
+		goto err;
+	if (lc->elapc != NULL)
+		goto err;
+
+	sapi = (fh->addr & DECT_FA_ADDR_SAPI_MASK) >> DECT_FA_ADDR_SAPI_SHIFT;
+	if (sapi != DECT_SAPI_CO_SIGNALLING && sapi != DECT_SAPI_CL_SIGNALLING)
+		goto err;
+	memcpy(&dli.mci, &lc->mc->mci, sizeof(dli.mci));
+
+	lapc = dect_ssap_rcv_request(lc, &dli, sapi);
+	if (lapc == NULL)
+		goto err;
+	dect_lc_bind(lc, lapc);
+
+	return dect_lapc_rcv(lapc, skb);
+
+err:
+	lc_debug(lc, "packet ignored\n");
+	kfree_skb(skb);
+}
+
+void dect_cplane_rcv(struct dect_mac_conn *mc, enum dect_data_channels chan,
+		     struct sk_buff *skb)
+{
+	struct dect_lc *lc;
+
+	if (mc->lc == NULL) {
+		lc = dect_lc_init(mc, GFP_ATOMIC);
+		if (lc == NULL)
+			goto err;
+		mc->lc = lc;
+	}
+
+	lc_debug(mc->lc, "MAC_CO_DATA-ind: chan: %u len: %u\n", chan, skb->len);
+	return dect_lc_rcv(mc->lc, chan, skb);
+
+err:
+	kfree_skb(skb);
+}
+
+struct sk_buff *dect_cplane_dtr(struct dect_mac_conn *mc, enum dect_data_channels chan)
+{
+	struct dect_lc *lc;
+
+	lc = mc->lc;
+	if (lc == NULL)
+		return NULL;
+	lc_debug(lc, "MAC_CO_DTR-ind: chan: %u\n", chan);
+	return dect_lc_tx(lc);
+}
+
+void dect_cplane_notify_state_change(struct dect_mac_conn *mc)
+{
+	struct dect_lc *lc = mc->lc;
+	unsigned int i;
+
+	if (lc == NULL)
+		return;
+
+	lc_debug(lc, "mac conn state change: state: %u\n", mc->state);
+	switch (mc->state) {
+	// FIXME: this does not make sense for incoming connections
+	case DECT_MAC_CONN_OPEN_PENDING:
+		break;
+	case DECT_MAC_CONN_OPEN:
+		for (i = 0; i < ARRAY_SIZE(lc->lapcs); i++) {
+			if (lc->lapcs[i] == NULL)
+				continue;
+			dect_lapc_establish(lc->lapcs[i]);
+			break;
+		}
+		break;
+	case DECT_MAC_CONN_CLOSED:
+		break;
+	}
+}
+
+void dect_cplane_mac_dis_ind(const struct dect_mac_conn *mc,
+			     enum dect_release_reasons reason)
+{
+	struct dect_lc *lc = mc->lc;
+	unsigned int i;
+	int err;
+
+	if (lc == NULL)
+		return;
+
+	switch (reason) {
+	case DECT_REASON_BEARER_RELEASE:
+		err = 0;
+		break;
+	case DECT_REASON_BEARER_SETUP_OR_HANDOVER_FAILED:
+		err = EHOSTUNREACH;
+		break;
+	case DECT_REASON_TIMEOUT_LOST_HANDSHAKE:
+		err = ETIMEDOUT;
+		break;
+	default:
+		err = EIO;
+		break;
+	}
+
+	dect_lc_hold(lc);
+	for (i = 0; i < ARRAY_SIZE(lc->lapcs); i++) {
+		if (lc->lapcs[i] == NULL)
+			continue;
+		lc->lapcs[i]->sk->sk_state = DECT_SK_RELEASED;
+		dect_lapc_error_report(lc->lapcs[i], err);
+		dect_lapc_destroy(lc->lapcs[i]);
+	}
+	dect_lc_put(lc);
+}
+
+void dect_cplane_mac_enc_eks_ind(const struct dect_mac_conn *mc,
+				 enum dect_cipher_states status)
+{
+	struct dect_lc *lc = mc->lc;
+	struct dect_dl_encrypt enc;
+	struct sk_buff *skb, *nskb;
+	unsigned int i;
+
+	if (lc == NULL || lc->use == 0)
+		return;
+
+	enc.status = status;
+	skb = dect_alloc_notification(DECT_DL_ENCRYPT, &enc, sizeof(enc));
+
+	for (i = 0; i < ARRAY_SIZE(lc->lapcs); i++) {
+		if (lc->lapcs[i] == NULL)
+			continue;
+
+		nskb = skb ? skb_clone(skb, GFP_ATOMIC) : NULL;
+		if (nskb != NULL)
+			sock_queue_err_skb(lc->lapcs[i]->sk, nskb);
+		else
+			dect_lapc_error_report(lc->lapcs[i], ENOMEM);
+	}
+
+	kfree_skb(skb);
+}
diff --git a/target/linux/generic/files/net/dect/dlc_lu1_sap.c b/target/linux/generic/files/net/dect/dlc_lu1_sap.c
new file mode 100644
index 0000000..ef75758
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc_lu1_sap.c
@@ -0,0 +1,474 @@
+/*
+ * DECT DLC LU1 SAP sockets
+ *
+ * Copyright (c) 2009-2010 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+#include <net/dect/transceiver.h>
+
+#define DECT_LU1_FRAME_NONE	255
+#define DECT_LU1_PREQUEUE_LEN	5
+
+#define lu1_debug(lu1, fmt, args...) \
+	pr_debug("LU1: rx_bytes: %u tx_bytes: %u " fmt, \
+		 (lu1)->qstats.rx_bytes, (lu1)->qstats.tx_bytes, \
+		 ## args)
+
+struct dect_lu1_sap {
+	struct sock			sk;
+	int				index;
+	struct dect_ulei		ulei;
+	struct dect_mac_conn		*mc;
+	u8				frame;
+	u8				slot;
+	struct sk_buff			*last;
+	struct dect_lux			lux;
+	struct dect_lu1_queue_stats	qstats;
+};
+
+/* Seamless handover slot offsets as per ETS 300 175-3 Annex F */
+static const u8 slot_offset_tbl[][DECT_HALF_FRAME_SIZE] = {
+	[DECT_FULL_SLOT] = {
+		[0]		= 0,
+		[1]		= 1,
+		[2]		= 3,
+		[3]		= 5,
+		[4]		= 6,
+		[5]		= 8,
+		[6]		= 10,
+		[7]		= 11,
+		[8]		= 13,
+		[9]		= 15,
+		[10]		= 16,
+		[11]		= 18,
+	},
+	[DECT_DOUBLE_SLOT] = {
+		[0]		= 0,
+		[2]		= 8,
+		[4]		= 16,
+		[6]		= 24,
+		[8]		= 32,
+		[10]		= 40,
+	},
+	[DECT_LONG_SLOT_640] = {
+		[0]		= 0,
+		[1]		= 3,
+		[2]		= 6,
+		[3]		= 10,
+		[4]		= 13,
+		[5]		= 16,
+		[6]		= 20,
+		[7]		= 23,
+		[8]		= 26,
+		[9]		= 30,
+		[10]		= 33,
+	},
+};
+
+static struct sk_buff *dect_lu1_dequeue(struct dect_lux *lux)
+{
+	struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
+	struct dect_cluster *cl = lu1->mc->cl;
+	struct sock *sk = &lu1->sk;
+	struct sk_buff *skb, *clone, *head = NULL;
+	u8 frame, slot, off, last_off, need = 40;
+
+	/* Fill queue up to prequeue len before delivering the first frame */
+	if (lu1->frame == DECT_LU1_FRAME_NONE &&
+	    sk->sk_write_queue.qlen < DECT_LU1_PREQUEUE_LEN)
+		return NULL;
+
+	/* Calculate seamless handover data offset */
+	frame = __dect_framenum(&cl->timer_base[DECT_TIMER_TX]);
+	slot  = __dect_slotnum(&cl->timer_base[DECT_TIMER_TX]);
+	if (slot >= DECT_HALF_FRAME_SIZE)
+		slot -= DECT_HALF_FRAME_SIZE;
+
+	last_off = slot_offset_tbl[DECT_FULL_SLOT][lu1->slot];
+	off      = slot_offset_tbl[DECT_FULL_SLOT][slot];
+
+	if (off > last_off)
+		off -= last_off;
+	else
+		off += need - last_off;
+
+	/* Advance queue */
+	lu1_debug(lu1, "dequeue: slot: %u off: %u need: %u\n", slot, off, need);
+	if (lu1->frame != DECT_LU1_FRAME_NONE && lu1->frame != frame)
+		lu1->qstats.tx_bytes -= skb_queue_pull(&sk->sk_write_queue, off);
+
+	lu1->frame = frame;
+	lu1->slot  = slot;
+
+	/* Duplicate data from last frame on underflow */
+	if (lu1->qstats.tx_bytes < need && lu1->last) {
+		lu1->qstats.tx_underflow++;
+		skb = skb_clone(lu1->last, GFP_ATOMIC);
+		if (skb == NULL)
+			goto err;
+		skb_pull(skb, skb->len - (need - lu1->qstats.tx_bytes));
+
+		skb_queue_head(&sk->sk_write_queue, skb);
+		lu1->qstats.tx_bytes += skb->len;
+		lu1_debug(lu1, "fill: len: %u need: %u\n", skb->len, need);
+
+	}
+
+	skb = NULL;
+	while (need > 0) {
+		if (skb == NULL) {
+			skb = skb_peek(&sk->sk_write_queue);
+			if (skb == NULL)
+				goto underflow;
+			/* The head needs to be copied to avoid sharing the
+			 * frag list. */
+			clone = skb_copy(skb, GFP_ATOMIC);
+		} else {
+			if (skb_queue_is_last(&sk->sk_write_queue, skb))
+				goto underflow;
+			skb = skb->next;
+			clone = skb_clone(skb, GFP_ATOMIC);
+		}
+
+		if (clone == NULL)
+			goto err;
+
+		if (clone->len > need)
+			skb_trim(clone, need);
+		need -= clone->len;
+
+		head = skb_append_frag(head, clone);
+		lu1_debug(lu1, "dequeue: head: %u need: %u\n", head->len, need);
+	}
+
+	if (skb_linearize(head) < 0)
+		goto err;
+
+	kfree_skb(lu1->last);
+	lu1->last = skb_get(head);
+
+	lu1_debug(lu1, "dequeued: len: %u\n", head->len);
+	return head;
+
+underflow:
+	lu1->qstats.tx_underflow++;
+err:
+	kfree_skb(head);
+	lu1_debug(lu1, "dequeue: no frame available\n");
+	return NULL;
+}
+
+static void dect_lu1_enqueue(struct dect_lux *lux, struct sk_buff *skb)
+{
+	struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
+	unsigned int len = skb->len;
+
+	if (sock_queue_rcv_skb(&lu1->sk, skb) < 0)
+		kfree_skb(skb);
+	else
+		lu1->qstats.rx_bytes += len;
+}
+
+static void dect_lu1_disconnect(struct dect_lux *lux)
+{
+	struct dect_lu1_sap *lu1 = container_of(lux, struct dect_lu1_sap, lux);
+	struct sock *sk = &lu1->sk;
+
+	sk->sk_state = DECT_SK_RELEASED;
+	sk->sk_err = ENETDOWN;
+	if (!sock_flag(sk, SOCK_DEAD))
+		sk->sk_error_report(sk);
+	lu1->mc->fbx = NULL;
+	dect_dlc_mac_conn_unbind(lu1->mc);
+	lu1->mc = NULL;
+}
+
+static const struct dect_lux_ops dect_lu1_ops = {
+	.dequeue	= dect_lu1_dequeue,
+	.enqueue	= dect_lu1_enqueue,
+	.disconnect	= dect_lu1_disconnect,
+};
+
+static inline struct dect_lu1_sap *dect_lu1_sap(struct sock *sk)
+{
+	return (struct dect_lu1_sap *)sk;
+}
+
+static int dect_parse_ulei(struct dect_ulei *ulei,
+			   const struct sockaddr_dect_lu *addr)
+{
+	if (dect_parse_ari(&ulei->mci.ari, (u64)addr->dect_ari << 24) == 0)
+		return -EINVAL;
+	dect_parse_pmid(&ulei->mci.pmid, addr->dect_pmid);
+	ulei->mci.lcn = addr->dect_lcn;
+	return 0;
+}
+
+static void dect_build_ulei(struct sockaddr_dect_lu *addr,
+			    const struct dect_ulei *ulei)
+{
+	addr->dect_family = AF_DECT;
+	addr->dect_pmid   = dect_build_pmid(&ulei->mci.pmid);
+	addr->dect_lcn    = ulei->mci.lcn;
+}
+
+static int dect_lu1_init(struct sock *sk)
+{
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+
+	sk->sk_state = DECT_SK_RELEASED;
+	lu1->frame   = DECT_LU1_FRAME_NONE;
+	return 0;
+}
+
+static void dect_lu1_close(struct sock *sk, long timeout)
+{
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+
+	if (sk->sk_state == DECT_SK_ESTABLISHED) {
+		lu1->mc->fbx = NULL;
+		dect_dlc_mac_conn_unbind(lu1->mc);
+		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+	}
+
+	__skb_queue_purge(&sk->sk_receive_queue);
+	__skb_queue_purge(&sk->sk_write_queue);
+	kfree_skb(lu1->last);
+
+	sock_orphan(sk);
+	sock_put(sk);
+}
+
+static int dect_lu1_getname(struct sock *sk, struct sockaddr *uaddr,
+			     int *len, int peer)
+{
+	struct sockaddr_dect_lu *addr = (struct sockaddr_dect_lu *)uaddr;
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+
+	if (peer)
+		return -EOPNOTSUPP;
+
+	addr->dect_index = lu1->index;
+	dect_build_ulei(addr, &lu1->ulei);
+	*len = sizeof(*addr);
+	return 0;
+}
+
+static int dect_lu1_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_dect_lu *addr = (struct sockaddr_dect_lu *)uaddr;
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+	struct dect_cluster *cl;
+	struct dect_ulei ulei;
+	struct dect_mac_conn *mc;
+	int err;
+
+	err = dect_parse_ulei(&ulei, addr);
+	if (err < 0)
+		goto err1;
+
+	err = -ENODEV;
+	cl = dect_cluster_get_by_index(addr->dect_index);
+	if (cl == NULL)
+		goto err1;
+
+	err = -ENETDOWN;
+	mc = dect_mac_conn_get_by_mci(cl, &ulei.mci);
+	if (mc == NULL)
+		goto err1;
+	WARN_ON(mc->state == DECT_MAC_CONN_CLOSED);
+
+	err = -EBUSY;
+	if (mc->fbx != NULL)
+		goto err1;
+
+	memcpy(&lu1->ulei, &ulei, sizeof(lu1->ulei));
+
+	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+	sk->sk_state = DECT_SK_ESTABLISHED;
+
+	lu1->lux.fbx.ops = &dect_fbn_ops;
+	lu1->lux.ops = &dect_lu1_ops;
+	lu1->mc = mc;
+	mc->fbx = &lu1->lux.fbx;
+	dect_dlc_mac_conn_bind(lu1->mc);
+	pr_debug("LU1: bound to MCEI %u\n", mc->mcei);
+	return 0;
+
+err1:
+	return err;
+}
+
+static int dect_lu1_getsockopt(struct sock *sk, int level, int optname,
+			       char __user *optval, int __user *optlen)
+{
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+	int len;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+	if (len < 0)
+		return -EINVAL;
+
+	switch (optname) {
+	case DECT_LU1_QUEUE_STATS:
+		if (len > sizeof(lu1->qstats))
+			len = sizeof(lu1->qstats);
+		if (put_user(len, optlen) ||
+		    copy_to_user(optval, &lu1->qstats, len))
+			return -EFAULT;
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+}
+
+static int dect_lu1_recvmsg(struct kiocb *iocb, struct sock *sk,
+			    struct msghdr *msg, size_t len,
+			    int noblock, int flags, int *addr_len)
+{
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+	struct sk_buff *skb;
+	size_t copied = 0, copy;
+	long timeo;
+	int err = 0;
+
+	if (flags & (MSG_OOB | MSG_TRUNC))
+		return -EOPNOTSUPP;
+
+	lock_sock(sk);
+
+	if (sk->sk_state != DECT_SK_ESTABLISHED) {
+		err = -ENOTCONN;
+		goto out;
+	}
+
+	timeo = sock_rcvtimeo(sk, noblock);
+
+	while (copied < len) {
+		skb = skb_peek(&sk->sk_receive_queue);
+		if (skb != NULL)
+			goto copy;
+
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+
+		sk_wait_data(sk, &timeo);
+		continue;
+
+copy:
+		copy = len - copied;
+		if (copy > skb->len)
+			copy = skb->len;
+
+		err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copy);
+		if (err < 0)
+			break;
+		copied += copy;
+
+		if (copy < skb->len) {
+			__skb_pull(skb, copy);
+			break;
+		} else
+			sk_eat_skb(sk, skb, 0);
+	}
+
+out:
+	lu1->qstats.rx_bytes -= copied;
+	if (copied < len)
+		lu1->qstats.rx_underflow++;
+
+	release_sock(sk);
+	lu1_debug(lu1, "recvmsg: dequeued: %zu len: %zu\n", copied, len);
+	return copied ? : err;
+}
+
+static int dect_lu1_sendmsg(struct kiocb *kiocb, struct sock *sk,
+			    struct msghdr *msg, size_t len)
+{
+	struct dect_lu1_sap *lu1 = dect_lu1_sap(sk);
+	struct sk_buff *skb;
+	int err;
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (sk->sk_state != DECT_SK_ESTABLISHED)
+		return -ENOTCONN;
+
+	skb = sock_alloc_send_skb(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto err1;
+	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+	if (err < 0)
+		goto err2;
+
+	skb_queue_tail(&sk->sk_write_queue, skb);
+	lu1->qstats.tx_bytes += len;
+	lu1_debug(lu1, "sendmsg: queued: %zu\n", len);
+	return len;
+
+err2:
+	kfree_skb(skb);
+err1:
+	return err;
+}
+
+static struct dect_proto dect_lu1_proto = {
+	.type			= SOCK_STREAM,
+	.protocol		= DECT_LU1_SAP,
+	.capability		= -1,
+	.ops			= &dect_stream_ops,
+	.proto.name		= "DECT_LU1_SAP",
+	.proto.owner		= THIS_MODULE,
+	.proto.obj_size		= sizeof(struct dect_lu1_sap),
+	.proto.init		= dect_lu1_init,
+	.proto.close		= dect_lu1_close,
+	.proto.connect		= dect_lu1_connect,
+	.proto.getsockopt	= dect_lu1_getsockopt,
+	.proto.recvmsg		= dect_lu1_recvmsg,
+	.proto.sendmsg		= dect_lu1_sendmsg,
+	.getname		= dect_lu1_getname,
+};
+
+static int __init dect_lu1_sap_module_init(void)
+{
+	BUILD_BUG_ON(sizeof(struct sockaddr_dect_lu) >
+		     sizeof(struct sockaddr));
+	return dect_proto_register(&dect_lu1_proto);
+}
+
+static void dect_lu1_sap_module_exit(void)
+{
+	dect_proto_unregister(&dect_lu1_proto);
+}
+
+module_init(dect_lu1_sap_module_init);
+module_exit(dect_lu1_sap_module_exit);
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT DLC LU1 SAP sockets");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_LU1_SAP);
diff --git a/target/linux/generic/files/net/dect/dlc_s_sap.c b/target/linux/generic/files/net/dect/dlc_s_sap.c
new file mode 100644
index 0000000..37baa5c
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc_s_sap.c
@@ -0,0 +1,659 @@
+/*
+ * DECT DLC S SAP sockets - DLC C-plane data link service access
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <asm/uaccess.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+
+static DEFINE_SPINLOCK(dect_ssap_lock);
+static HLIST_HEAD(dect_ssap_sockets);
+static HLIST_HEAD(dect_ssap_listeners);
+
+struct dect_ssap {
+	struct dect_csk		csk;
+	int			index;
+	struct dect_dlei	dlei;
+	struct dect_lapc	*lapc;
+};
+
+static inline struct dect_ssap *dect_ssap(struct sock *sk)
+{
+	return (struct dect_ssap *)sk;
+}
+
+static int dect_parse_dlei(struct dect_dlei *dlei,
+			   const struct sockaddr_dect_ssap *addr)
+{
+	if (dect_parse_ari(&dlei->mci.ari, (u64)addr->dect_ari << 24) == 0)
+		return -EINVAL;
+	dect_parse_pmid(&dlei->mci.pmid, addr->dect_pmid);
+	dlei->mci.lcn = addr->dect_lcn;
+
+	dlei->lln = addr->dect_lln;
+	if (dlei->lln > DECT_LLN_UNASSIGNED &&
+	    dlei->lln != DECT_LLN_ANY)
+		return -EINVAL;
+
+	dlei->sapi = addr->dect_sapi;
+	switch (dlei->sapi) {
+	case DECT_SAPI_CO_SIGNALLING:
+	case DECT_SAPI_CL_SIGNALLING:
+	case DECT_SAPI_ANY:
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void dect_build_dlei(struct sockaddr_dect_ssap *addr,
+			    const struct dect_dlei *dlei)
+{
+	addr->dect_family = AF_DECT;
+	addr->dect_pmid = dect_build_pmid(&dlei->mci.pmid);
+	addr->dect_ari  = dect_build_ari(&dlei->mci.ari) >> 24;
+	addr->dect_lcn  = dlei->mci.lcn;
+	addr->dect_lln  = dlei->lln;
+	addr->dect_sapi = dlei->sapi;
+}
+
+static void dect_ssap_insert(struct sock *sk)
+{
+	sk_add_node(sk, &dect_ssap_sockets);
+	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+}
+
+static void dect_ssap_unlink(struct sock *sk)
+{
+	if (sk_del_node_init(sk))
+		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+}
+
+static int dect_ssap_init(struct sock *sk)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+
+	INIT_HLIST_HEAD(&ssap->csk.accept_queue);
+	return 0;
+}
+
+static struct sock *dect_ssap_acceptq_dequeue(struct dect_ssap *ssap)
+{
+	struct sock *sk;
+
+	if (hlist_empty(&ssap->csk.accept_queue))
+		return NULL;
+	sk = hlist_entry(ssap->csk.accept_queue.first, struct sock, sk_bind_node);
+	__sk_del_bind_node(sk);
+	sk_node_init(&sk->sk_bind_node);
+	sk_acceptq_removed(&ssap->csk.sk);
+	return sk;
+}
+
+static void dect_ssap_close(struct sock *sk, long timeout)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct sock *req;
+
+	pr_debug("close sock %p refcnt %u rmem %u wmem %u\n",
+		 sk, atomic_read(&sk->sk_refcnt),
+		 atomic_read(&sk->sk_rmem_alloc),
+		 atomic_read(&sk->sk_wmem_alloc));
+
+	spin_lock_bh(&dect_ssap_lock);
+	dect_ssap_unlink(sk);
+	spin_unlock_bh(&dect_ssap_lock);
+
+	if (sk->sk_state != DECT_SK_RELEASED && ssap->lapc != NULL)
+		dect_lapc_release(ssap->lapc, false);
+
+	if (!hlist_unhashed(&sk->sk_bind_node))
+		__sk_del_bind_node(sk);
+
+	while ((req = dect_ssap_acceptq_dequeue(ssap)) != NULL) {
+		spin_lock_bh(&dect_ssap_lock);
+		dect_ssap_unlink(req);
+		spin_unlock_bh(&dect_ssap_lock);
+
+		dect_lapc_release(dect_ssap(req)->lapc, false);
+	}
+
+	sk_common_release(sk);
+}
+
+static int dect_ssap_bind_conflict(int index, const struct dect_dlei *dlei)
+{
+	struct dect_ssap *ssap;
+	struct sock *sk;
+
+	// FIXME: wildcards
+	sk_for_each(sk, &dect_ssap_sockets) {
+		ssap = dect_ssap(sk);
+		if (ssap->index == index &&
+		    !dect_ari_cmp(&ssap->dlei.mci.ari, &dlei->mci.ari) &&
+		    !dect_pmid_cmp(&ssap->dlei.mci.pmid, &dlei->mci.pmid) &&
+		    ssap->dlei.lln == dlei->lln &&
+		    ssap->dlei.sapi == dlei->sapi)
+			return -EADDRINUSE;
+	}
+	return 0;
+}
+
+static int dect_ssap_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct dect_dlei dlei;
+	int err;
+
+	if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
+		return -EINVAL;
+
+	err = dect_parse_dlei(&dlei, addr);
+	if (err < 0)
+		return err;
+
+	lock_sock(sk);
+	spin_lock_bh(&dect_ssap_lock);
+
+	err = dect_ssap_bind_conflict(addr->dect_index, &dlei);
+	if (err < 0)
+		goto out;
+
+	ssap->index = addr->dect_index;
+	memcpy(&ssap->dlei, &dlei, sizeof(ssap->dlei));
+	dect_ssap_insert(sk);
+out:
+	spin_unlock_bh(&dect_ssap_lock);
+	release_sock(sk);
+	return err;
+}
+
+static struct dect_ssap *dect_ssap_lookup_listener(const struct dect_cluster *cl,
+						   const struct dect_dli *dli,
+						   enum dect_sapis sapi)
+{
+	struct dect_ssap *ssap;
+	struct sock *sk;
+
+	pr_debug("lookup listener: lln %u sapi %u\n", dli->lln, sapi);
+	sk_for_each_bound(sk, &dect_ssap_listeners) {
+		ssap = dect_ssap(sk);
+		if (cl->index != ssap->index)
+			continue;
+#if 0
+		if (!dect_ari_cmp(&ssap->dlei.mci.ari, &dli->mci.ari))
+			continue;
+		if (!dect_pmid_cmp(&ssap->dlei.mci.pmid, &dli->mci.pmid))
+			continue;
+#endif
+		pr_debug("ssap: lln %u sapi %u\n", ssap->dlei.lln, ssap->dlei.sapi);
+		if (ssap->dlei.lln != DECT_LLN_ANY &&
+		    ssap->dlei.lln != dli->lln)
+			continue;
+		if (ssap->dlei.sapi != DECT_SAPI_ANY &&
+		    ssap->dlei.sapi != sapi)
+			continue;
+		return ssap;
+	}
+	return NULL;
+}
+
+struct dect_lapc *dect_ssap_rcv_request(struct dect_lc *lc,
+					const struct dect_dli *dli,
+					enum dect_sapis sapi)
+{
+	struct dect_ssap *ssap, *newssap;
+	struct sock *sk, *newsk;
+	struct dect_lapc *lapc = NULL;
+
+	spin_lock(&dect_ssap_lock);
+	ssap = dect_ssap_lookup_listener(lc->mc->cl, dli, sapi);
+	if (ssap == NULL)
+		goto out;
+
+	sk = &ssap->csk.sk;
+	if (sk_acceptq_is_full(sk))
+		goto out;
+
+	newsk = sk_alloc(&init_net, PF_DECT, GFP_ATOMIC, sk->sk_prot);
+	if (newsk == NULL)
+		goto out;
+
+	sock_init_data(NULL, newsk);
+	newsk->sk_type     = sk->sk_type;
+	newsk->sk_protocol = sk->sk_protocol;
+	newsk->sk_destruct = sk->sk_destruct;
+
+	lapc = dect_lapc_init(newsk, dli, sapi, lc, GFP_ATOMIC);
+	if (lapc == NULL)
+		goto err1;
+
+	newssap = dect_ssap(newsk);
+	newssap->index     = lc->mc->cl->index;
+	memcpy(&newssap->dlei.mci, &dli->mci, sizeof(newssap->dlei.mci));
+	newssap->dlei.lln  = dli->lln;
+	newssap->dlei.sapi = sapi;
+	newssap->lapc      = lapc;
+
+	newsk->sk_state = DECT_SK_ESTABLISHED;
+	dect_ssap_insert(newsk);
+	sk_add_bind_node(newsk, &ssap->csk.accept_queue);
+	sk_acceptq_added(sk);
+
+	sk->sk_state_change(sk);
+	sk->sk_data_ready(sk, 0);
+	goto out;
+
+err1:
+	sk_free(newsk);
+	goto out;
+
+out:
+	spin_unlock(&dect_ssap_lock);
+	return lapc;
+}
+
+static void dect_ssap_hash(struct sock *sk)
+{
+	sk->sk_state = DECT_SK_LISTEN;
+
+	spin_lock_bh(&dect_ssap_lock);
+	sk_add_bind_node(sk, &dect_ssap_listeners);
+	spin_unlock_bh(&dect_ssap_lock);
+}
+
+static void dect_ssap_unhash(struct sock *sk)
+{
+	if (sk_hashed(sk)) {
+		spin_lock_bh(&dect_ssap_lock);
+		__sk_del_bind_node(sk);
+		spin_unlock_bh(&dect_ssap_lock);
+	}
+}
+
+static int dect_ssap_wait_req(struct sock *sk, int noblock)
+{
+	struct task_struct *tsk = current;
+	struct dect_ssap *ssap = dect_ssap(sk);
+	long timeo = sock_rcvtimeo(sk, noblock);
+
+	for (;;) {
+		DEFINE_WAIT(wait);
+
+		if (sk->sk_state != DECT_SK_LISTEN)
+			return -EINVAL;
+		if (!hlist_empty(&ssap->csk.accept_queue))
+			break;
+		if (!timeo)
+			return -EWOULDBLOCK;
+		if (signal_pending(tsk))
+			return sock_intr_errno(timeo);
+
+		prepare_to_wait_exclusive(sk_sleep(sk), &wait,
+					  TASK_INTERRUPTIBLE);
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+		finish_wait(sk_sleep(sk), &wait);
+	}
+	return 0;
+}
+
+static struct sock *dect_ssap_accept(struct sock *sk, int flags, int *errp)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct sock *newsk;
+	int err;
+
+	lock_sock(sk);
+	err = dect_ssap_wait_req(sk, flags & O_NONBLOCK);
+	if (err < 0)
+		goto err;
+
+	newsk = dect_ssap_acceptq_dequeue(ssap);
+	release_sock(sk);
+
+	*errp = 0;
+	return newsk;
+
+err:
+	release_sock(sk);
+	*errp = err;
+	return NULL;
+}
+
+static int dect_ssap_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct dect_cluster *cl;
+	struct dect_dlei dlei;
+	struct dect_dli dli;
+	struct dect_lapc *lapc;
+	struct dect_lc *lc;
+	struct dect_mac_conn *mc;
+	bool new_mc = false, new_lc = false;
+	int err;
+
+	if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
+		return -EINVAL;
+
+	err = dect_parse_dlei(&dlei, addr);
+	if (err < 0)
+		goto err1;
+
+	err = -ENODEV;
+	cl = dect_cluster_get_by_index(addr->dect_index);
+	if (cl == NULL)
+		goto err1;
+
+	/* The assignable class B LLNs may only be used for connections
+	 * originating from a PT. The unassigned LLN may be used by an FT
+	 * to request class B operation. Class A and U may be used by both.
+	 */
+	err = -EINVAL;
+	switch (dlei.lln) {
+	case DECT_LLN_ASSIGNABLE_MIN ... DECT_LLN_ASSIGNABLE_MAX:
+		if (cl->mode != DECT_MODE_PP)
+			goto err1;
+		break;
+	case DECT_LLN_UNASSIGNED:
+		if (cl->mode != DECT_MODE_FP)
+			goto err1;
+		break;
+	default:
+		break;
+	}
+
+	/* Lookup MAC connection and initiate new one if necessary */
+	err = -ENOMEM;
+	mc = dect_mac_conn_get_by_mci(cl, &dlei.mci);
+	if (mc == NULL) {
+		mc = dect_mac_conn_init(cl, &dlei.mci, NULL);
+		if (mc == NULL)
+			goto err1;
+		new_mc = true;
+		lc = NULL;
+	} else {
+		WARN_ON(mc->state == DECT_MAC_CONN_CLOSED);
+		lc = mc->lc;
+	}
+
+	/* Get Lc entity and verify LLN is available */
+	if (lc == NULL) {
+		lc = dect_lc_init(mc, GFP_KERNEL);
+		if (lc == NULL)
+			goto err2;
+		mc->lc = lc;
+		new_lc = true;
+	} else {
+		err = -EADDRINUSE;
+		if (lc->lapcs[dlei.lln] != NULL)
+			goto err2;
+	}
+
+	memcpy(&dli.mci, &dlei.mci, sizeof(dli.mci));
+	dli.lln = dlei.lln;
+
+	lapc = dect_lapc_init(sk, &dli, dlei.sapi, lc, GFP_KERNEL);
+	if (lapc == NULL)
+		goto err3;
+	ssap->lapc = lapc;
+
+	dect_lc_bind(lc, lapc);
+
+	if (new_mc)
+		err = dect_dlc_mac_conn_establish(mc);
+	else
+		err = dect_lapc_establish(lapc);
+
+	if (err < 0)
+		goto err4;
+
+	sk->sk_state = DECT_SK_ESTABLISH_PENDING;
+	return 0;
+
+err4:
+	dect_lapc_destroy(lapc);
+	/* Both will be release by dect_lapc_destroy() */
+	new_lc = false;
+	new_mc = false;
+err3:
+	if (new_lc)
+		dect_lc_destroy(lc);
+err2:
+	if (new_mc)
+		dect_dlc_mac_conn_destroy(mc);
+err1:
+	return err;
+}
+
+static int dect_ssap_getname(struct sock *sk, struct sockaddr *uaddr, int *len,
+			     int peer)
+{
+	struct sockaddr_dect_ssap *addr = (struct sockaddr_dect_ssap *)uaddr;
+	struct dect_ssap *ssap = dect_ssap(sk);
+
+#if 0
+	if (peer)
+		return -EOPNOTSUPP;
+#endif
+	addr->dect_index = ssap->index;
+	dect_build_dlei(addr, &ssap->dlei);
+	*len = sizeof(*addr);
+	return 0;
+}
+
+static void dect_ssap_shutdown(struct sock *sk, int how)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+
+	if (!(how & SEND_SHUTDOWN))
+		return;
+
+	if (sk->sk_state == DECT_SK_ESTABLISHED)
+		dect_lapc_release(ssap->lapc, true);
+}
+
+static int dect_ssap_setsockopt(struct sock *sk, int level, int optname,
+				char __user *optval, unsigned int optlen)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct dect_dl_encrypt dle;
+	int err;
+	u64 ck;
+
+	switch (optname) {
+	case DECT_DL_ENC_KEY:
+		if (optlen != sizeof(ck))
+			return -EINVAL;
+		if (sk->sk_state != DECT_SK_ESTABLISH_PENDING &&
+		    sk->sk_state != DECT_SK_ESTABLISHED)
+			return -ENOTCONN;
+		if (copy_from_user(&ck, optval, sizeof(ck)))
+			return -EFAULT;
+		err = dect_dlc_mac_conn_enc_key_req(ssap->lapc->lc->mc, ck);
+		break;
+	case DECT_DL_ENCRYPT:
+		if (optlen != sizeof(dle))
+			return -EINVAL;
+		if (sk->sk_state != DECT_SK_ESTABLISHED)
+			return -ENOTCONN;
+		if (ssap->lapc->lc->mc->cl->mode != DECT_MODE_PP)
+			return -EOPNOTSUPP;
+		if (copy_from_user(&dle, optval, sizeof(dle)))
+			return -EFAULT;
+		err = dect_dlc_mac_conn_enc_eks_req(ssap->lapc->lc->mc,
+						    dle.status);
+		break;
+	default:
+		err = -ENOPROTOOPT;
+	}
+	return err;
+}
+
+static int dect_ssap_recvmsg(struct kiocb *iocb, struct sock *sk,
+			     struct msghdr *msg, size_t len,
+			     int noblock, int flags, int *addr_len)
+{
+	struct sockaddr_dect *addr;
+	struct sk_buff *skb, *eskb;
+	size_t copied = 0;
+	int err;
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	eskb = skb_dequeue(&sk->sk_error_queue);
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (skb == NULL) {
+		if (eskb != NULL && err == -EAGAIN) {
+			err = 0;
+			goto out;
+		}
+		if (sk->sk_type == SOCK_SEQPACKET) {
+			lock_sock(sk);
+			if (sk->sk_state != DECT_SK_ESTABLISHED &&
+			    err == -EAGAIN)
+				err = -ENOTCONN;
+			release_sock(sk);
+		}
+		goto out;
+	}
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err < 0)
+		goto out_free;
+
+	if (msg->msg_name != NULL) {
+		addr = (struct sockaddr_dect *)msg->msg_name;
+		addr->dect_family = AF_DECT;
+		addr->dect_index = DECT_SK_CB(skb)->index;
+		msg->msg_namelen = sizeof(*addr);
+	}
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	if (eskb != NULL)
+		put_cmsg(msg, SOL_DECT, DECT_NOTIFY_CB(eskb)->type,
+			 eskb->len, eskb->data);
+	kfree_skb(eskb);
+
+	return err ? : copied;
+}
+
+static int dect_ssap_sendmsg(struct kiocb *kiocb, struct sock *sk,
+			     struct msghdr *msg, size_t len)
+{
+	struct dect_ssap *ssap = dect_ssap(sk);
+	struct sk_buff *skb;
+	long timeo;
+	int err;
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (len > DECT_FA_I_MAX)
+		return -EMSGSIZE;
+
+	lock_sock(sk);
+	if (sk->sk_type == SOCK_SEQPACKET) {
+		if (sk->sk_state != DECT_SK_ESTABLISHED) {
+			timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+			err = sk_stream_wait_connect(sk, &timeo);
+			if (err < 0)
+				goto err1;
+		}
+	}
+
+	err = -EPIPE;
+	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+		goto err1;
+
+	skb = sock_alloc_send_skb(sk, len + 32, msg->msg_flags & MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto err1;
+	skb_reset_mac_header(skb);
+	skb_reserve(skb, 16);
+	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+	if (err < 0)
+		goto err2;
+
+	skb_queue_tail(&sk->sk_write_queue, skb);
+	release_sock(sk);
+
+	dect_lapc_transmit(ssap->lapc);
+	return len;
+
+err2:
+	kfree_skb(skb);
+err1:
+	err = sk_stream_error(sk, msg->msg_flags, err);
+	release_sock(sk);
+	return err;
+}
+
+static struct dect_proto dect_ssap_proto __read_mostly = {
+	.type			= SOCK_SEQPACKET,
+	.protocol		= DECT_S_SAP,
+	.capability		= CAP_NET_RAW,
+	.ops			= &dect_stream_ops,
+	.proto.name		= "DECT_S_SAP",
+	.proto.owner		= THIS_MODULE,
+	.proto.obj_size		= sizeof(struct dect_ssap),
+	.proto.init		= dect_ssap_init,
+	.proto.close		= dect_ssap_close,
+	.proto.bind		= dect_ssap_bind,
+	.proto.hash		= dect_ssap_hash,
+	.proto.unhash		= dect_ssap_unhash,
+	.proto.accept		= dect_ssap_accept,
+	.proto.connect		= dect_ssap_connect,
+	.proto.shutdown		= dect_ssap_shutdown,
+	.proto.setsockopt	= dect_ssap_setsockopt,
+	.proto.recvmsg		= dect_ssap_recvmsg,
+	.proto.sendmsg		= dect_ssap_sendmsg,
+	.getname		= dect_ssap_getname,
+};
+
+int __init dect_ssap_module_init(void)
+{
+	BUILD_BUG_ON(sizeof(struct sockaddr_dect_ssap) >
+		     sizeof(struct sockaddr));
+	return dect_proto_register(&dect_ssap_proto);
+}
+
+void dect_ssap_module_exit(void)
+{
+	dect_proto_unregister(&dect_ssap_proto);
+}
+
+MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_S_SAP);
diff --git a/target/linux/generic/files/net/dect/dlc_uplane.c b/target/linux/generic/files/net/dect/dlc_uplane.c
new file mode 100644
index 0000000..6d8d318
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dlc_uplane.c
@@ -0,0 +1,86 @@
+/*
+ * DECT DLC U-plane
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+
+static struct sk_buff *dect_fbn_dequeue(struct dect_fbx *fbx)
+{
+	struct dect_lux *lux = container_of(fbx, struct dect_lux, fbx);
+
+	return lux->ops->dequeue(lux);
+}
+
+static void dect_fbn_enqueue(struct dect_fbx *fbx, struct sk_buff *skb)
+{
+	struct dect_lux *lux = container_of(fbx, struct dect_lux, fbx);
+
+	lux->ops->enqueue(lux, skb);
+}
+
+const struct dect_fbx_ops dect_fbn_ops = {
+	.dequeue	= dect_fbn_dequeue,
+	.enqueue	= dect_fbn_enqueue,
+};
+EXPORT_SYMBOL_GPL(dect_fbn_ops);
+
+struct sk_buff *dect_uplane_dtr(struct dect_mac_conn *mc, enum dect_data_channels chan)
+{
+	struct dect_fbx *fbx;
+
+	fbx = mc->fbx;
+	if (fbx == NULL)
+		return NULL;
+	return fbx->ops->dequeue(fbx);
+}
+
+void dect_uplane_rcv(struct dect_mac_conn *mc, enum dect_data_channels chan,
+		     struct sk_buff *skb)
+{
+	struct dect_fbx *fbx;
+
+	fbx = mc->fbx;
+	if (fbx == NULL)
+		goto err;
+	return fbx->ops->enqueue(fbx, skb);
+
+err:
+	kfree_skb(skb);
+}
+
+void dect_uplane_notify_state_change(struct dect_mac_conn *mc)
+{
+	struct dect_lux *lux;
+	struct dect_fbx *fbx;
+
+	fbx = mc->fbx;
+	if (fbx == NULL)
+		return;
+	lux = container_of(fbx, struct dect_lux, fbx);
+
+	switch (mc->state) {
+	case DECT_MAC_CONN_OPEN_PENDING:
+		break;
+	case DECT_MAC_CONN_OPEN:
+		break;
+	case DECT_MAC_CONN_CLOSED:
+		return lux->ops->disconnect(lux);
+	}
+}
diff --git a/target/linux/generic/files/net/dect/dsc.c b/target/linux/generic/files/net/dect/dsc.c
new file mode 100644
index 0000000..d3f597a
--- /dev/null
+++ b/target/linux/generic/files/net/dect/dsc.c
@@ -0,0 +1,141 @@
+/*
+ * DECT Standard Cipher
+ *
+ * Copyright (c) 2010 Erik Tews <e_tews at cdc.informatik.tu-darmstadt.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <net/dect/dsc.h>
+
+#define R1_LEN			17
+#define R2_LEN			19
+#define R3_LEN			21
+#define R4_LEN			23
+
+#define MASK_R1			(65536 | 32)
+#define MASK_R2			(262144 | 4096 | 8 | 4)
+#define MASK_R3			(1048576 | 2)
+#define MASK_R4			(256 | 4194304)
+
+#define R1_CLOCKMASK		(1 << 8)
+#define R2_CLOCKMASK		(1 << 9)
+#define R3_CLOCKMASK		(1 << 10)
+
+#define R1_R4_CLOCKMASK		(1 << 0)
+#define R2_R4_CLOCKMASK		(1 << 1)
+#define R3_R4_CLOCKMASK		(1 << 2)
+
+static uint32_t clock(uint32_t lfsr, int length, uint32_t mask)
+{
+	return (lfsr >> 1) ^ (-(lfsr & 1) & mask);
+}
+
+static uint32_t combine(uint32_t comb, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+	uint32_t c, x10, x11, x20, x21, x30, x31;
+
+	c = comb;
+	x10 = r1 & 1;
+	x11 = (r1 >> 1) & 1;
+	x20 = r2 & 1;
+	x21 = (r2 >> 1) & 1;
+	x30 = r3 & 1;
+	x31 = (r3 >> 1) & 1;
+
+	return (x11 & x10 & c) ^
+	       (x20 & x11 & x10) ^
+	       (x21 & x10 & c) ^
+	       (x21 & x20 & x10) ^
+	       (x30 & x10 & c) ^
+	       (x30 & x20 & x10) ^
+	       (x11 & c) ^
+	       (x11 & x10) ^
+	       (x20 & x11) ^
+	       (x30 & c) ^
+	       (x31 & c) ^
+	       (x31 & x10) ^
+	       (x21) ^
+	       (x31);
+}
+
+void dect_dsc_keystream(uint64_t iv, const uint8_t *key,
+			uint8_t *output, unsigned int len)
+{
+	uint8_t input[16];
+	uint32_t R1, R2, R3, R4, N1, N2, N3, COMB;
+	unsigned int i, keybit;
+
+	memset(output, 0, len);
+	input[0] = iv & 0xff;
+	input[1] = (iv >> 8) & 0xff;
+	input[2] = (iv >> 16) & 0xff;
+	input[3] = (iv >> 24) & 0xff;
+	input[4] = (iv >> 32) & 0xff;
+	for (i = 5; i < 8; i++)
+		input[i] = 0;
+	for (i = 0; i < 8; i++)
+		input[i + 8] = key[i];
+
+	R1 = R2 = R3 = R4 = COMB = 0;
+
+	/* load IV and KEY */
+	for (i = 0; i < 128; i++) {
+		keybit = (input[i / 8] >> ((i) & 7)) & 1;
+		R1 = clock(R1, R1_LEN, MASK_R1) ^ (keybit << (R1_LEN - 1));
+		R2 = clock(R2, R2_LEN, MASK_R2) ^ (keybit << (R2_LEN - 1));
+		R3 = clock(R3, R3_LEN, MASK_R3) ^ (keybit << (R3_LEN - 1));
+		R4 = clock(R4, R4_LEN, MASK_R4) ^ (keybit << (R4_LEN - 1));
+	}
+
+	for (i = 0; i < 40 + (len * 8); i++) {
+		N1 = R1;
+		N2 = R2;
+		N3 = R3;
+		COMB = combine(COMB, R1, R2, R3);
+		if (((R2 & R2_CLOCKMASK) != 0) ^
+		    ((R3 & R3_CLOCKMASK) != 0) ^
+		    ((R4 & R1_R4_CLOCKMASK) != 0))
+			N1 = clock(R1, R1_LEN, MASK_R1);
+		if (((R1 & R1_CLOCKMASK) != 0) ^
+		    ((R3 & R3_CLOCKMASK) != 0) ^
+		    ((R4 & R2_R4_CLOCKMASK) != 0))
+			N2 = clock(R2, R2_LEN, MASK_R2);
+		if (((R1 & R1_CLOCKMASK) != 0) ^
+		    ((R2 & R2_CLOCKMASK) != 0) ^
+		    ((R4 & R3_R4_CLOCKMASK) != 0))
+			N3 = clock(R3, R3_LEN, MASK_R3);
+
+		/* Check whether any registers are zero after 11 pre-ciphering
+		 * steps. If a register is all-zero after 11 steps, set input
+		 * bit to one (see U.S. patent 5608802)
+		 */
+		if (i == 11) {
+			if (!R1)
+				N1 ^= (1 << (R1_LEN - 1));
+			if (!R2)
+				N2 ^= (1 << (R2_LEN - 1));
+			if (!R3)
+				N3 ^= (1 << (R3_LEN - 1));
+			if (!R4)
+				R4 ^= (1 << (R4_LEN - 1));
+		}
+
+		N1 = clock(N1, R1_LEN, MASK_R1);
+		R1 = clock(N1, R1_LEN, MASK_R1);
+		N2 = clock(N2, R2_LEN, MASK_R2);
+		R2 = clock(N2, R2_LEN, MASK_R2);
+		N3 = clock(N3, R3_LEN, MASK_R3);
+		R3 = clock(N3, R3_LEN, MASK_R3);
+		R4 = clock(R4, R4_LEN, MASK_R4);
+		R4 = clock(R4, R4_LEN, MASK_R4);
+		R4 = clock(R4, R4_LEN, MASK_R4);
+
+		if (i >= 40)
+			output[(i - 40) / 8] |= ((COMB) << (7 - ((i - 40) & 7)));
+	}
+}
diff --git a/target/linux/generic/files/net/dect/identities.c b/target/linux/generic/files/net/dect/identities.c
new file mode 100644
index 0000000..64438ef
--- /dev/null
+++ b/target/linux/generic/files/net/dect/identities.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+
+bool dect_ari_masked_cmp(const struct dect_ari *a1, const struct dect_ari *a2,
+			 const struct dect_ari *m)
+{
+	/* An empty class mask implies a wildcard for everything */
+	if (!m->arc)
+		return false;
+	if (a1->arc != a2->arc)
+		return true;
+
+	if ((a1->fpn ^ a2->fpn) & m->fpn)
+		return true;
+
+	switch (a1->arc) {
+	case DECT_ARC_A:
+		return  ((a1->emc ^ a2->emc) & m->emc);
+	case DECT_ARC_B:
+		return (((a1->eic ^ a2->eic) & m->eic) |
+		        ((a1->fps ^ a2->fps) & m->fps));
+	case DECT_ARC_C:
+		return (((a1->poc ^ a2->poc) & m->poc) |
+		        ((a1->fps ^ a2->fps) & m->fps));
+	case DECT_ARC_D:
+		return  ((a1->gop ^ a2->gop) & m->gop);
+	case DECT_ARC_E:
+		return  ((a1->fil ^ a2->fil) & m->fil);
+	default:
+		return true;
+	}
+}
+EXPORT_SYMBOL_GPL(dect_ari_masked_cmp);;
+
+bool dect_ari_cmp(const struct dect_ari *a1, const struct dect_ari *a2)
+{
+	static const struct dect_ari mask = {
+		.arc = ~0,
+		.fpn = ~0,
+		.fps = ~0,
+		{ ~0 }
+	};
+	return dect_ari_masked_cmp(a1, a2, &mask);
+}
+EXPORT_SYMBOL_GPL(dect_ari_cmp);
+
+u8 dect_parse_ari(struct dect_ari *ari, u64 a)
+{
+	ari->arc = (a & DECT_ARI_ARC_MASK) >> DECT_ARI_ARC_SHIFT;
+	switch (ari->arc) {
+	case DECT_ARC_A:
+		ari->emc = (a & DECT_ARI_A_EMC_MASK) >> DECT_ARI_A_EMC_SHIFT;
+		ari->fpn = (a & DECT_ARI_A_FPN_MASK) >> DECT_ARI_A_FPN_SHIFT;
+		return DECT_ARC_A_LEN;
+	case DECT_ARC_B:
+		ari->eic = (a & DECT_ARI_B_EIC_MASK) >> DECT_ARI_B_EIC_SHIFT;
+		ari->fpn = (a & DECT_ARI_B_FPN_MASK) >> DECT_ARI_B_FPN_SHIFT;
+		ari->fps = (a & DECT_ARI_B_FPS_MASK) >> DECT_ARI_B_FPS_SHIFT;
+		return DECT_ARC_B_LEN;
+	case DECT_ARC_C:
+		ari->poc = (a & DECT_ARI_C_POC_MASK) >> DECT_ARI_C_POC_SHIFT;
+		ari->fpn = (a & DECT_ARI_C_FPN_MASK) >> DECT_ARI_C_FPN_SHIFT;
+		ari->fps = (a & DECT_ARI_C_FPS_MASK) >> DECT_ARI_C_FPS_SHIFT;
+		return DECT_ARC_C_LEN;
+	case DECT_ARC_D:
+		ari->gop = (a & DECT_ARI_D_GOP_MASK) >> DECT_ARI_D_GOP_SHIFT;
+		ari->fpn = (a & DECT_ARI_D_FPN_MASK) >> DECT_ARI_D_FPN_SHIFT;
+		return DECT_ARC_D_LEN;
+	case DECT_ARC_E:
+		ari->fil = (a & DECT_ARI_E_FIL_MASK) >> DECT_ARI_E_FIL_SHIFT;
+		ari->fpn = (a & DECT_ARI_E_FPN_MASK) >> DECT_ARI_E_FPN_SHIFT;
+		return DECT_ARC_E_LEN;
+	default:
+		return 0;
+	}
+}
+EXPORT_SYMBOL_GPL(dect_parse_ari);
+
+u64 dect_build_ari(const struct dect_ari *ari)
+{
+	u64 a = 0;
+
+	a |= (u64)ari->arc << DECT_ARI_ARC_SHIFT;
+	switch (ari->arc) {
+	case DECT_ARC_A:
+		a |= (u64)ari->emc << DECT_ARI_A_EMC_SHIFT;
+		a |= (u64)ari->fpn << DECT_ARI_A_FPN_SHIFT;
+		break;
+	case DECT_ARC_B:
+		a |= (u64)ari->eic << DECT_ARI_B_EIC_SHIFT;
+		a |= (u64)ari->fpn << DECT_ARI_B_FPN_SHIFT;
+		a |= (u64)ari->fps << DECT_ARI_B_FPS_SHIFT;
+		break;
+	case DECT_ARC_C:
+		a |= (u64)ari->poc << DECT_ARI_C_POC_SHIFT;
+		a |= (u64)ari->fpn << DECT_ARI_C_FPN_SHIFT;
+		a |= (u64)ari->fps << DECT_ARI_C_FPS_SHIFT;
+		break;
+	case DECT_ARC_D:
+		a |= (u64)ari->gop << DECT_ARI_D_GOP_SHIFT;
+		a |= (u64)ari->fpn << DECT_ARI_D_FPN_SHIFT;
+		break;
+	case DECT_ARC_E:
+		a |= (u64)ari->fil << DECT_ARI_E_FIL_SHIFT;
+		a |= (u64)ari->fpn << DECT_ARI_E_FPN_SHIFT;
+		break;
+	}
+	return a;
+}
+EXPORT_SYMBOL_GPL(dect_build_ari);
+
+u64 dect_build_rfpi(const struct dect_idi *idi)
+{
+	u64 t = 0;
+
+	t |= idi->e ? DECT_RFPI_E_FLAG : 0;
+	t |= dect_build_ari(&idi->pari) >> DECT_RFPI_ARI_SHIFT;
+	t |= (u64)idi->rpn << DECT_RFPI_RPN_SHIFT;
+	return t;
+}
+EXPORT_SYMBOL_GPL(dect_build_rfpi);
+
+bool dect_rfpi_cmp(const struct dect_idi *i1, const struct dect_idi *i2)
+{
+	return dect_ari_cmp(&i1->pari, &i2->pari) ||
+	       i1->rpn != i2->rpn ||
+	       i1->e   != i2->e;
+}
+EXPORT_SYMBOL_GPL(dect_rfpi_cmp);
+
+u16 dect_build_fmid(const struct dect_idi *idi)
+{
+	u64 rfpi;
+
+	rfpi = dect_build_rfpi(idi);
+	rfpi >>= (sizeof(rfpi) - DECT_NT_ID_RFPI_LEN - 1) * BITS_PER_BYTE;
+	return rfpi & DECT_FMID_MASK;
+}
+EXPORT_SYMBOL_GPL(dect_build_fmid);
+
+/*
+ * PMID (Portable MAC Identity)
+ */
+
+void dect_parse_pmid(struct dect_pmid *pmid, u32 p)
+{
+	if ((p & DECT_PMID_DEFAULT_ID_MASK) == DECT_PMID_DEFAULT_ID) {
+		pmid->type = DECT_PMID_DEFAULT;
+		pmid->num  = p & DECT_PMID_DEFAULT_NUM_MASK;
+	} else if ((p & DECT_PMID_EMERGENCY_ID_MASK) == DECT_PMID_EMERGENCY_ID) {
+		pmid->type = DECT_PMID_EMERGENCY;
+		pmid->tpui = p & DECT_PMID_EMERGENCY_TPUI_MASK;
+	} else {
+		pmid->type = DECT_PMID_ASSIGNED;
+		pmid->tpui = p & DECT_PMID_ASSIGNED_TPUI_MASK;
+	}
+}
+EXPORT_SYMBOL_GPL(dect_parse_pmid);
+
+u32 dect_build_pmid(const struct dect_pmid *pmid)
+{
+	u32 p = 0;
+
+	switch (pmid->type) {
+	case DECT_PMID_DEFAULT:
+		p |= DECT_PMID_DEFAULT_ID;
+		p |= pmid->tpui;
+		break;
+	case DECT_PMID_EMERGENCY:
+		p |= DECT_PMID_EMERGENCY_ID;
+		p |= pmid->tpui;
+		break;
+	case DECT_PMID_ASSIGNED:
+		p |= pmid->tpui;
+		break;
+	}
+	return p;
+}
+EXPORT_SYMBOL_GPL(dect_build_pmid);
+
+bool dect_pmid_cmp(const struct dect_pmid *p1, const struct dect_pmid *p2)
+{
+	return memcmp(p1, p2, sizeof(*p1));
+}
+EXPORT_SYMBOL(dect_pmid_cmp);
+
+/**
+ * dect_parse_mci - Extract the MCI elements from a packed MCI in a
+ * 		    struct sockaddr_dect_lu
+ *
+ * The packed MCI is build from ARI + PMID + LCN
+ */
+int dect_parse_mci(struct dect_mci *mci, u64 m)
+{
+	u32 p;
+	u8 len;
+
+	len = dect_parse_ari(&mci->ari, m);
+
+	len += DECT_PMID_SIZE;
+	p = (m >> (sizeof(m) * BITS_PER_BYTE - len)) & DECT_PMID_MASK;
+	dect_parse_pmid(&mci->pmid, p);
+
+	len += DECT_ECN_SIZE;
+	mci->lcn = (m >> (sizeof(m) * BITS_PER_BYTE - len)) & DECT_LCN_MASK;
+	return 0;
+}
+
+u64 dect_build_mci(const struct dect_mci *mci)
+{
+	return 0;
+}
diff --git a/target/linux/generic/files/net/dect/mac_ccf.c b/target/linux/generic/files/net/dect/mac_ccf.c
new file mode 100644
index 0000000..6445829
--- /dev/null
+++ b/target/linux/generic/files/net/dect/mac_ccf.c
@@ -0,0 +1,2070 @@
+/*
+ * DECT MAC Cluster Control Functions
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_ccf.h>
+#include <net/dect/mac_csf.h>
+#include <net/dect/ccp.h>
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT MAC Layer");
+MODULE_LICENSE("GPL");
+
+static void dect_llme_scan_result_notify(const struct dect_cluster *cl,
+					 const struct dect_scan_result *res);
+static void dect_llme_mac_info_ind(const struct dect_cluster *cl,
+				   const struct dect_idi *idi,
+				   const struct dect_si *si);
+
+static struct dect_cluster *dect_cluster_get_by_name(const struct nlattr *nla)
+{
+	struct dect_cluster *cl;
+
+	list_for_each_entry(cl, &dect_cluster_list, list) {
+		if (!nla_strcmp(nla, cl->name))
+			return cl;
+	}
+	return NULL;
+}
+
+static struct dect_cluster *dect_cluster(const struct dect_cluster_handle *clh)
+{
+	return container_of(clh, struct dect_cluster, handle);
+}
+
+static struct dect_cell_handle *
+dect_cluster_get_cell_by_rpn(struct dect_cluster *cl, u8 rpn)
+{
+	struct dect_cell_handle *ch;
+
+	list_for_each_entry(ch, &cl->cells, list) {
+		if (ch->rpn == rpn)
+			return ch;
+	}
+	return NULL;
+}
+
+/*
+ * MAC CCF layer timers
+ */
+
+static u8 dect_framenum(const struct dect_cluster *cl, enum dect_timer_bases b)
+{
+	return __dect_framenum(&cl->timer_base[b]);
+}
+
+static void dect_run_timers(struct dect_cluster *cl, enum dect_timer_bases b)
+{
+	__dect_run_timers(cl->name, &cl->timer_base[b]);
+}
+
+static void dect_timer_base_update(struct dect_cluster *cl,
+				   enum dect_timer_bases base,
+				   u32 mfn, u8 framenum, u8 slot)
+{
+	cl->timer_base[base].mfn      = mfn;
+	cl->timer_base[base].framenum = framenum;
+	cl->timer_base[base].slot     = slot;
+}
+
+static void dect_timer_add(struct dect_cluster *cl, struct dect_timer *timer,
+			   enum dect_timer_bases b, u32 frame, u8 slot)
+{
+	timer->cluster = cl;
+	__dect_timer_add(cl->name, &cl->timer_base[b], timer, frame, slot);
+}
+
+static void dect_timer_setup(struct dect_timer *timer,
+			     void (*func)(struct dect_cluster *, void *),
+			     void *data)
+{
+	dect_timer_init(timer);
+	timer->cb.cluster = func;
+	timer->data       = data;
+}
+
+static void dect_ccf_time_ind(struct dect_cluster_handle *clh,
+			      enum dect_timer_bases base,
+			      u32 mfn, u8 framenum, u8 slot)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+
+	if (base == DECT_TIMER_TX) {
+		dect_timer_base_update(cl, base, mfn, framenum, slot);
+		dect_run_timers(cl, base);
+	} else {
+		dect_run_timers(cl, base);
+		dect_timer_base_update(cl, base, mfn, framenum, slot);
+	}
+}
+
+static void dect_scan_report(const struct dect_cluster_handle *clh,
+			     const struct dect_scan_result *res)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+
+	dect_llme_scan_result_notify(cl, res);
+}
+
+static void dect_mac_info_ind(const struct dect_cluster_handle *clh,
+			      const struct dect_idi *idi,
+			      const struct dect_si *si)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+
+	pr_debug("cl %p: MAC_INFO-ind: rpn: %u\n", cl, idi->rpn);
+	cl->si	= *si;
+	cl->rpn	= idi->rpn;
+
+	dect_llme_mac_info_ind(cl, idi, &cl->si);
+}
+
+/*
+ * Broadcast message control
+ */
+
+/**
+ * dect_bmc_mac_page_req - queue one segment of B_S channel data
+ *
+ * @cl:		DECT cluster
+ * @skb:	SDU
+ */
+void dect_bmc_mac_page_req(struct dect_cluster *cl, struct sk_buff *skb)
+{
+	const struct dect_cell_handle *ch, *prev = NULL;
+	struct sk_buff *clone;
+
+	BUG_ON(cl->mode != DECT_MODE_FP);
+
+	list_for_each_entry(ch, &cl->cells, list) {
+		if (prev != NULL) {
+			clone = skb_clone(skb, GFP_ATOMIC);
+			if (clone != NULL)
+				prev->ops->page_req(prev, clone);
+		}
+		prev = ch;
+	}
+	if (prev != NULL)
+		prev->ops->page_req(prev, skb);
+}
+
+static void dect_bmc_page_ind(const struct dect_cluster_handle *clh,
+			      struct sk_buff *skb)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+
+	return dect_mac_page_ind(cl, skb);
+}
+
+/*
+ * Multi-Bearer Control
+ */
+
+#define mbc_debug(mbc, fmt, args...) \
+	pr_debug("MBC (MCEI %u/%s): " fmt, \
+		 (mbc)->id.mcei, dect_mbc_states[(mbc)->state], ## args);
+
+static const char * const dect_mbc_states[] = {
+	[DECT_MBC_NONE]		= "NONE",
+	[DECT_MBC_INITIATED]	= "INITIATED",
+	[DECT_MBC_ESTABLISHED]	= "ESTABLISHED",
+	[DECT_MBC_RELEASED]	= "RELEASED",
+};
+
+static void dect_mbc_hold(struct dect_mbc *mbc)
+{
+	mbc->refcnt++;
+}
+
+static void dect_mbc_put(struct dect_mbc *mbc)
+{
+	if (--mbc->refcnt > 0)
+		return;
+	kfree(mbc);
+}
+
+static struct dect_tb *dect_mbc_tb_get_by_tbei(const struct dect_mbc *mbc,
+					       const struct dect_tbc_id *id)
+{
+	struct dect_tb *tb;
+
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		if (tb->id.tbei == id->tbei)
+			return tb;
+	}
+	return NULL;
+}
+
+static struct dect_mbc *dect_mbc_get_by_tbc_id(const struct dect_cluster *cl,
+					       const struct dect_tbc_id *id)
+{
+	struct dect_mbc *mbc;
+
+	list_for_each_entry(mbc, &cl->mbcs, list) {
+		if (!memcmp(&mbc->id.ari, &id->ari, sizeof(id->ari)) &&
+		    !memcmp(&mbc->id.pmid, &id->pmid, sizeof(id->pmid)) &&
+		    mbc->id.ecn == id->ecn)
+			return mbc;
+	}
+	return NULL;
+}
+
+static struct dect_mbc *dect_mbc_get_by_mcei(const struct dect_cluster *cl, u32 mcei)
+{
+	struct dect_mbc *mbc;
+
+	list_for_each_entry(mbc, &cl->mbcs, list) {
+		if (mbc->id.mcei == mcei)
+			return mbc;
+	}
+	return NULL;
+}
+
+u32 dect_mbc_alloc_mcei(struct dect_cluster *cl)
+{
+	u32 mcei;
+
+	while (1) {
+		mcei = ++cl->mcei_rover;
+		if (mcei == 0)
+			continue;
+		if (dect_mbc_get_by_mcei(cl, mcei))
+			continue;
+		return mcei;
+	}
+}
+
+static bool dect_ct_tail_allowed(const struct dect_cluster *cl, u8 framenum)
+{
+	if (cl->mode == DECT_MODE_FP)
+		return (framenum & 0x1) == 0x1;
+	else
+		return (framenum & 0x1) == 0x0;
+}
+
+/*
+ * MBC normal receive half frame timer:
+ *
+ * Deliver received data segments to the DLC at half frame boundaries.
+ * Data is delivered for the following channels:
+ *
+ * - C_S after an ARQ window
+ * - I_N normal delay
+ *
+ * Additionally in half frames that end an ARQ window, acknowledgment
+ * of C_S segment reception of the preceeding transmit half frame is
+ * verified.
+ */
+static void dect_mbc_normal_rx_timer(struct dect_cluster *cl, void *data)
+{
+	struct dect_mbc *mbc = data;
+	struct dect_tb *tb;
+	struct sk_buff *skb;
+
+	mbc_debug(mbc, "Normal RX timer\n");
+	dect_mbc_hold(mbc);
+
+	if (mbc->cs_rx_skb != NULL) {
+		skb = mbc->cs_rx_skb;
+		mbc->cs_rx_skb = NULL;
+		mbc->stats.cs_rx_bytes += skb->len;
+		dect_mac_co_data_ind(cl, mbc->id.mcei, DECT_MC_C_S, skb);
+
+		/* C-channel reception might trigger release of the MBC in case
+		 * it acknowledges the last outstanding LAPC I-frame. */
+		if (mbc->state == DECT_MBC_RELEASED)
+			goto out;
+	}
+
+	if (mbc->cs_tx_ok && mbc->cs_rx_ok) {
+		mbc->stats.cs_tx_bytes += mbc->cs_tx_skb->len;
+		kfree_skb(mbc->cs_tx_skb);
+		mbc->cs_tx_skb = NULL;
+		mbc->cs_tx_ok  = false;
+	}
+	mbc->cs_rx_ok = false;
+
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		if (tb->b_rx_skb == NULL)
+			continue;
+		skb = tb->b_rx_skb;
+		tb->b_rx_skb = NULL;
+		mbc->stats.i_rx_bytes += skb->len;
+		dect_mac_co_data_ind(cl, mbc->id.mcei, DECT_MC_I_N, skb);
+	}
+
+	dect_timer_add(cl, &mbc->normal_rx_timer, DECT_TIMER_RX,
+		       1, dect_normal_receive_end(cl->mode));
+out:
+	dect_mbc_put(mbc);
+}
+
+/*
+ * MBC slot based receive timer:
+ *
+ * Deliver received I_N minimal delay B-field segments to the DLC.
+ */
+static void dect_mbc_slot_rx_timer(struct dect_cluster *cl, void *data)
+{
+	struct dect_tb *tb = data;
+	struct dect_mbc *mbc = tb->mbc;
+	struct sk_buff *skb;
+
+	mbc_debug(mbc, "Slot RX timer: TBEI: %u LBN: %u slot: %u\n",
+		  tb->id.tbei, tb->id.lbn, tb->rx_slot);
+
+	if (tb->b_rx_skb != NULL) {
+		skb = tb->b_rx_skb;
+		tb->b_rx_skb = NULL;
+		mbc->stats.i_rx_bytes += skb->len;
+		dect_mac_co_data_ind(cl, mbc->id.mcei, DECT_MC_I_N, skb);
+	}
+
+	dect_timer_add(cl, &tb->slot_rx_timer, DECT_TIMER_RX, 1, tb->rx_slot);
+}
+
+/*
+ * MBC normal transmit half frame timer:
+ *
+ * Request data from the DLC for the next frame. Data is requested for the
+ * following channels:
+ *
+ * - C_S before an ARQ window starts
+ * - I_N normal delay
+ */
+static void dect_mbc_normal_tx_timer(struct dect_cluster *cl, void *data)
+{
+	const struct dect_cell_handle *ch;
+	struct dect_mbc *mbc = data;
+	struct dect_tb *tb;
+	struct sk_buff *skb;
+
+	mbc_debug(mbc, "Normal TX timer\n");
+
+	if (dect_ct_tail_allowed(cl, dect_framenum(cl, DECT_TIMER_TX))) {
+		if (mbc->cs_tx_skb == NULL) {
+			skb = dect_mac_co_dtr_ind(cl, mbc->id.mcei, DECT_MC_C_S);
+			if (skb != NULL) {
+				DECT_CS_CB(skb)->seq = mbc->cs_tx_seq;
+				mbc->cs_tx_seq = !mbc->cs_tx_seq;
+				mbc->cs_tx_skb = skb;
+			}
+		}
+
+		if (mbc->cs_tx_skb != NULL) {
+			list_for_each_entry(tb, &mbc->tbs, list) {
+				skb = skb_clone(mbc->cs_tx_skb, GFP_ATOMIC);
+				if (skb == NULL)
+					continue;
+				ch = tb->ch;
+				ch->ops->tbc_data_req(ch, &tb->id, DECT_MC_C_S, skb);
+				mbc->cs_tx_ok = true;
+			}
+		}
+	}
+
+	if (mbc->service != DECT_SERVICE_IN_MIN_DELAY) {
+		list_for_each_entry(tb, &mbc->tbs, list) {
+			ch = tb->ch;
+			skb = dect_mac_co_dtr_ind(cl, mbc->id.mcei, DECT_MC_I_N);
+			if (skb != NULL) {
+				mbc->stats.i_tx_bytes += skb->len;
+				ch->ops->tbc_data_req(ch, &tb->id, DECT_MC_I_N, skb);
+			}
+		}
+	}
+
+	dect_timer_add(cl, &mbc->normal_tx_timer, DECT_TIMER_TX,
+		       1, dect_normal_transmit_base(cl->mode));
+}
+
+/*
+ * MBC slot based transmit timer:
+ *
+ * Request data from the DLC for the I_N minimal delay channel.
+ */
+static void dect_mbc_slot_tx_timer(struct dect_cluster *cl, void *data)
+{
+	struct dect_tb *tb = data;
+	struct dect_mbc *mbc = tb->mbc;
+	const struct dect_cell_handle *ch = tb->ch;
+	struct sk_buff *skb;
+
+	mbc_debug(mbc, "Slot TX timer: TBEI: %u LBN: %u slot: %u\n",
+		  tb->id.tbei, tb->id.lbn, tb->tx_slot);
+
+	skb = dect_mac_co_dtr_ind(cl, mbc->id.mcei, DECT_MC_I_N);
+	if (skb != NULL) {
+		mbc->stats.i_tx_bytes += skb->len;
+		ch->ops->tbc_data_req(ch, &tb->id, DECT_MC_I_N, skb);
+	}
+	dect_timer_add(cl, &tb->slot_tx_timer, DECT_TIMER_TX, 1, tb->tx_slot);
+}
+
+static int dect_mbc_complete_setup(struct dect_cluster *cl, struct dect_mbc *mbc)
+{
+	if (!del_timer(&mbc->timer))
+		return 0;
+
+	dect_timer_add(cl, &mbc->normal_rx_timer, DECT_TIMER_RX,
+		       0, dect_normal_receive_end(cl->mode));
+	dect_timer_add(cl, &mbc->normal_tx_timer, DECT_TIMER_TX,
+		       0, dect_normal_transmit_base(cl->mode));
+	mbc->state = DECT_MBC_ESTABLISHED;
+
+	return 1;
+}
+
+static void dect_mbc_tb_release(struct dect_tb *tb);
+
+static void dect_mbc_tb_handover_timer(struct dect_cluster *cl, void *data)
+{
+	struct dect_tb *tb = data, *tb1, *i;
+	struct dect_mbc *mbc = tb->mbc;
+
+	mbc_debug(mbc, "Handover timer: TBEI: %u LBN: %u\n",
+		  tb->id.tbei, tb->id.lbn);
+
+	tb1 = NULL;
+	list_for_each_entry(i, &mbc->tbs, list) {
+		if (i->id.lbn == tb->id.lbn) {
+			tb1 = i;
+			break;
+		}
+	}
+	if (tb1 == NULL)
+		return;
+
+	tb1->ch->ops->tbc_dis_req(tb1->ch, &tb1->id,
+				  DECT_REASON_BEARER_HANDOVER_COMPLETED);
+	list_del(&tb1->list);
+	dect_mbc_tb_release(tb1);
+	mbc->stats.handovers++;
+}
+
+static void dect_mbc_tb_complete_setup(struct dect_cluster *cl, struct dect_tb *tb)
+{
+	if (cl->mode == DECT_MODE_FP && tb->handover)
+		dect_timer_add(cl, &tb->handover_timer, DECT_TIMER_RX,
+			       DECT_MBC_TB_HANDOVER_TIMEOUT, tb->rx_slot);
+
+	if (tb->mbc->service == DECT_SERVICE_IN_MIN_DELAY) {
+		dect_timer_add(cl, &tb->slot_rx_timer, DECT_TIMER_RX,
+			       0, tb->rx_slot);
+		dect_timer_add(cl, &tb->slot_tx_timer, DECT_TIMER_TX,
+			       0, tb->tx_slot);
+	}
+}
+
+static void dect_mbc_tb_release(struct dect_tb *tb)
+{
+	dect_timer_del(&tb->handover_timer);
+	dect_timer_del(&tb->slot_rx_timer);
+	dect_timer_del(&tb->slot_tx_timer);
+	kfree(tb);
+}
+
+static struct dect_tb *dect_mbc_tb_init(struct dect_mbc *mbc,
+					const struct dect_cell_handle *ch, u8 lbn)
+{
+	struct dect_tb *tb;
+
+	tb = kzalloc(sizeof(*tb), GFP_ATOMIC);
+	if (tb == NULL)
+		return NULL;
+
+	tb->mbc      = mbc;
+	tb->ch       = ch;
+	tb->id.ari   = mbc->id.ari;
+	tb->id.pmid  = mbc->id.pmid;
+	tb->id.ecn   = 0;
+	tb->id.lbn   = lbn;
+	tb->id.tbei  = 0;
+	tb->handover = false;
+	tb->rx_slot  = 0;
+	tb->tx_slot  = 0;
+
+	dect_timer_setup(&tb->handover_timer, dect_mbc_tb_handover_timer, tb);
+	dect_timer_setup(&tb->slot_rx_timer, dect_mbc_slot_rx_timer, tb);
+	dect_timer_setup(&tb->slot_tx_timer, dect_mbc_slot_tx_timer, tb);
+
+	return tb;
+}
+
+static void dect_mbc_release(struct dect_mbc *mbc)
+{
+	struct dect_tb *tb, *next;
+
+	mbc_debug(mbc, "release\n");
+	mbc->state = DECT_MBC_RELEASED;
+	del_timer(&mbc->timer);
+	list_del(&mbc->list);
+
+	dect_timer_del(&mbc->normal_rx_timer);
+	dect_timer_del(&mbc->normal_tx_timer);
+
+	list_for_each_entry_safe(tb, next, &mbc->tbs, list)
+		dect_mbc_tb_release(tb);
+
+	kfree_skb(mbc->cs_rx_skb);
+	kfree_skb(mbc->cs_tx_skb);
+	dect_mbc_put(mbc);
+}
+
+static void dect_mbc_timeout(unsigned long data)
+{
+	struct dect_mbc *mbc = (struct dect_mbc *)data;
+	struct dect_tb *tb;
+	enum dect_release_reasons reason;
+
+	mbc_debug(mbc, "timeout\n");
+	reason = DECT_REASON_BEARER_SETUP_OR_HANDOVER_FAILED;
+
+	list_for_each_entry(tb, &mbc->tbs, list)
+		tb->ch->ops->tbc_dis_req(tb->ch, &tb->id, reason);
+
+	if (mbc->state != DECT_MBC_NONE)
+		dect_mac_dis_ind(mbc->cl, mbc->id.mcei, reason);
+
+	dect_mbc_release(mbc);
+}
+
+static struct dect_mbc *dect_mbc_init(struct dect_cluster *cl,
+				      const struct dect_mbc_id *id)
+{
+	struct dect_mbc *mbc;
+
+	mbc = kzalloc(sizeof(*mbc), GFP_ATOMIC);
+	if (mbc == NULL)
+		return NULL;
+	mbc->refcnt   = 1;
+	mbc->cl       = cl;
+	mbc->id       = *id;
+	mbc->state    = DECT_MBC_NONE;
+	mbc->ho_stamp = jiffies - DECT_MBC_HANDOVER_TIMER;
+
+	INIT_LIST_HEAD(&mbc->tbs);
+	dect_timer_setup(&mbc->normal_rx_timer, dect_mbc_normal_rx_timer, mbc);
+	dect_timer_setup(&mbc->normal_tx_timer, dect_mbc_normal_tx_timer, mbc);
+
+	mbc->cs_tx_seq = 1;
+	mbc->cs_rx_seq = 1;
+
+	setup_timer(&mbc->timer, dect_mbc_timeout, (unsigned long)mbc);
+	list_add_tail(&mbc->list, &cl->mbcs);
+	return mbc;
+}
+
+static int dect_mbc_tb_setup(struct dect_mbc *mbc, struct dect_tb *tb)
+{
+	const struct dect_cell_handle *ch = tb->ch;
+	struct dect_channel_desc chd;
+	int err;
+
+	memset(&chd, 0, sizeof(chd));
+	chd.pkt   = DECT_PACKET_P32;
+	chd.b_fmt = DECT_B_UNPROTECTED;
+
+	err = ch->ops->tbc_establish_req(ch, &tb->id, &chd,
+					 DECT_SERVICE_IN_MIN_DELAY,
+					 tb->handover);
+	if (err < 0)
+		return err;
+
+	mbc->setup_cnt++;
+	return 0;
+}
+
+/**
+ * dect_mac_con_req - request a new MAC connection
+ *
+ * @cl:		DECT cluster
+ * @id:		MBC identifier
+ */
+int dect_mac_con_req(struct dect_cluster *cl, const struct dect_mbc_id *id)
+{
+	struct dect_cell_handle *ch;
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+	int err;
+
+	err = -EHOSTUNREACH;
+	ch = dect_cluster_get_cell_by_rpn(cl, 0);
+	if (ch == NULL)
+		goto err1;
+
+	err = -ENOMEM;
+	mbc = dect_mbc_init(cl, id);
+	if (mbc == NULL)
+		goto err1;
+	mbc->state = DECT_MBC_INITIATED;
+	mbc_debug(mbc, "MAC_CON-req\n");
+
+	tb = dect_mbc_tb_init(mbc, ch, 0xf);
+	if (tb == NULL)
+		goto err2;
+
+	err = dect_mbc_tb_setup(mbc, tb);
+	if (err < 0)
+		goto err3;
+
+	list_add_tail(&tb->list, &mbc->tbs);
+	mod_timer(&mbc->timer, jiffies + DECT_MBC_SETUP_TIMEOUT);
+	return 0;
+
+err3:
+	dect_mbc_tb_release(tb);
+err2:
+	dect_mbc_release(mbc);
+err1:
+	return err;
+}
+
+void dect_mac_dis_req(struct dect_cluster *cl, u32 mcei)
+{
+	const struct dect_cell_handle *ch;
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+
+	mbc = dect_mbc_get_by_mcei(cl, mcei);
+	if (mbc == NULL)
+		return;
+	mbc_debug(mbc, "MAC_DIS-req\n");
+
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		ch = tb->ch;
+		ch->ops->tbc_dis_req(ch, &tb->id, DECT_REASON_CONNECTION_RELEASE);
+	}
+
+	dect_mbc_release(mbc);
+}
+
+/* TBC establishment indication from CSF */
+static int dect_tbc_establish_ind(const struct dect_cluster_handle *clh,
+				  const struct dect_cell_handle *ch,
+				  const struct dect_tbc_id *id,
+				  enum dect_mac_service_types service,
+				  bool handover)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	struct dect_mbc_id mid;
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+	unsigned int cnt;
+	int err;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL) {
+		if (handover)
+			return -ENOENT;
+
+		mid.mcei = dect_mbc_alloc_mcei(cl);
+		mid.type = 0;
+		mid.ari  = id->ari;
+		mid.pmid = id->pmid;
+		mid.ecn  = id->ecn;
+
+		err = -ENOMEM;
+		mbc = dect_mbc_init(cl, &mid);
+		if (mbc == NULL)
+			goto err1;
+		mbc->service = service;
+	} else {
+		if (!handover)
+			return -EEXIST;
+
+		cnt = 0;
+		list_for_each_entry(tb, &mbc->tbs, list) {
+			if (tb->id.lbn == id->lbn)
+				cnt++;
+		}
+		if (cnt > 1)
+			return -EEXIST;
+
+		if (mbc->cipher_state == DECT_CIPHER_ENABLED) {
+			err = ch->ops->tbc_enc_req(ch, id, mbc->ck);
+			if (err < 0)
+				return err;
+		}
+	}
+
+	mbc_debug(mbc, "TBC_ESTABLISH-ind: TBEI: %u LBN: %u H/O: %u\n",
+		  id->tbei, id->lbn, handover);
+
+	err = -ENOMEM;
+	tb = dect_mbc_tb_init(mbc, ch, id->lbn);
+	if (tb == NULL)
+		goto err2;
+	tb->handover = handover;
+
+	err = ch->ops->tbc_establish_res(ch, id);
+	if (err < 0)
+		goto err3;
+
+	list_add_tail(&tb->list, &mbc->tbs);
+	if (!handover)
+		mod_timer(&mbc->timer, jiffies + DECT_MBC_SETUP_TIMEOUT);
+	return 0;
+
+err3:
+	dect_mbc_tb_release(tb);
+err2:
+	dect_mbc_release(mbc);
+err1:
+	return err;
+}
+
+static int dect_tbc_establish_cfm(const struct dect_cluster_handle *clh,
+				  const struct dect_tbc_id *id, bool success,
+				  u8 rx_slot)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	const struct dect_cell_handle *ch;
+	struct dect_mbc *mbc;
+	struct dect_tb *tb, *i;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL)
+		return -ENOENT;
+
+	mbc_debug(mbc, "TBC_ESTABLISH-cfm: TBEI: %u LBN: %u success: %d\n",
+		  id->tbei, id->lbn, success);
+
+	tb = NULL;
+	list_for_each_entry(i, &mbc->tbs, list) {
+		if (i->id.lbn  == id->lbn &&
+		    i->id.tbei == 0) {
+			tb = i;
+			break;
+		}
+	}
+	if (tb == NULL)
+		return -ENOENT;
+
+	if (success) {
+		tb->id.tbei = id->tbei;
+		tb->rx_slot = rx_slot;
+		tb->tx_slot = dect_tdd_slot(rx_slot);
+
+		switch (mbc->state) {
+		case DECT_MBC_NONE:
+			if (!dect_mbc_complete_setup(cl, mbc))
+				return 0;
+			dect_mbc_tb_complete_setup(cl, tb);
+
+			return dect_mac_con_ind(cl, &mbc->id, mbc->service);
+		case DECT_MBC_INITIATED:
+			if (!dect_mbc_complete_setup(cl, mbc))
+				return 0;
+			dect_mbc_tb_complete_setup(cl, tb);
+
+			return dect_mac_con_cfm(cl, mbc->id.mcei, mbc->service);
+		case DECT_MBC_ESTABLISHED:
+			ch = tb->ch;
+			if (mbc->cipher_state == DECT_CIPHER_ENABLED &&
+			    ch->ops->tbc_enc_req(ch, id, mbc->ck) < 0) {
+				ch->ops->tbc_dis_req(ch, id, DECT_REASON_UNKNOWN);
+				return -1;
+			}
+			dect_mbc_tb_complete_setup(cl, tb);
+			return 0;
+		default:
+			return WARN_ON(-1);
+		}
+	} else {
+		switch (mbc->state) {
+		case DECT_MBC_NONE:
+			dect_mbc_release(mbc);
+			return 0;
+		case DECT_MBC_INITIATED:
+			if (mbc->setup_cnt > DECT_MBC_SETUP_MAX_ATTEMPTS ||
+			    dect_mbc_tb_setup(mbc, tb) < 0) {
+				dect_mac_dis_ind(cl, mbc->id.mcei,
+					DECT_REASON_BEARER_SETUP_OR_HANDOVER_FAILED);
+				dect_mbc_release(mbc);
+			}
+			return 0;
+		case DECT_MBC_ESTABLISHED:
+			list_del(&tb->list);
+			dect_mbc_tb_release(tb);
+			return 0;
+		default:
+			return WARN_ON(-1);
+		}
+	}
+}
+
+static int dect_tbc_event_ind(const struct dect_cluster_handle *clh,
+			      const struct dect_tbc_id *id,
+			      enum dect_tbc_event event)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL)
+		return -ENOENT;
+	mbc_debug(mbc, "TBC_EVENT-ind: TBEI: %u LBN: %u event: %u\n",
+		  id->tbei, id->lbn, event);
+
+	tb = dect_mbc_tb_get_by_tbei(mbc, id);
+	if (tb == NULL)
+		return -ENOENT;
+
+	switch (event) {
+	case DECT_TBC_ACK_RECEIVED:
+		mbc->cs_rx_ok = true;
+		return 0;
+	case DECT_TBC_CIPHER_ENABLED:
+		mbc->cipher_state = DECT_TBC_CIPHER_ENABLED;
+		dect_mac_enc_eks_ind(cl, mbc->id.mcei, DECT_CIPHER_ENABLED);
+		return 0;
+	case DECT_TBC_CIPHER_DISABLED:
+		mbc->cipher_state = DECT_TBC_CIPHER_DISABLED;
+		dect_mac_enc_eks_ind(cl, mbc->id.mcei, DECT_CIPHER_DISABLED);
+		return 0;
+	default:
+		return WARN_ON(-1);
+	}
+}
+
+static int dect_tbc_handover_req(const struct dect_cluster_handle *clh,
+				 const struct dect_tbc_id *id)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	struct dect_cell_handle *ch;
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+	unsigned int cnt;
+	int err;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL)
+		return -ENOENT;
+	mbc_debug(mbc, "TBC_HANDOVER-req: TBEI: %u LBN: %u\n",
+		  id->tbei, id->lbn);
+
+	/* Handover already in progress or two bearers active?? */
+	cnt = 0;
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		if (tb->id.lbn  != id->lbn)
+			continue;
+		 if (tb->id.tbei == 0)
+			return 0;
+		 cnt++;
+	}
+	if (cnt > 1)
+		return 0;
+
+	/* Handover rate-limiting */
+	if (mbc->ho_tokens == 0) {
+		if (time_after_eq(jiffies, mbc->ho_stamp + DECT_MBC_HANDOVER_TIMER)) {
+			mbc->ho_tokens = DECT_MBC_HANDOVER_LIMIT;
+			mbc->ho_stamp  = jiffies;
+		}
+		mbc_debug(mbc, "handover: tokens: %u\n", mbc->ho_tokens);
+		if (mbc->ho_tokens == 0)
+			return 0;
+	}
+
+	ch = dect_cluster_get_cell_by_rpn(cl, 0);
+	if (ch == NULL)
+		return -EHOSTUNREACH;
+
+	tb = dect_mbc_tb_init(mbc, ch, id->lbn);
+	if (tb == NULL)
+		return -ENOMEM;
+	tb->handover = true;
+
+	err = dect_mbc_tb_setup(mbc, tb);
+	if (err < 0)
+		goto err1;
+
+	list_add_tail(&tb->list, &mbc->tbs);
+	mbc->ho_tokens--;
+	return 0;
+
+err1:
+	dect_mbc_tb_release(tb);
+	return err;
+}
+
+/* TBC release indication from CSF */
+static void dect_tbc_dis_ind(const struct dect_cluster_handle *clh,
+			     const struct dect_tbc_id *id,
+			     enum dect_release_reasons reason)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL)
+		return;
+	mbc_debug(mbc, "TBC_DIS-ind: TBEI: %u LBN: %u reason: %u\n",
+		  id->tbei, id->lbn, reason);
+
+	tb = dect_mbc_tb_get_by_tbei(mbc, id);
+	if (tb == NULL)
+		return;
+
+	list_del(&tb->list);
+	dect_mbc_tb_release(tb);
+	if (!list_empty(&mbc->tbs))
+		return;
+
+	dect_mac_dis_ind(cl, mbc->id.mcei, reason);
+	dect_mbc_release(mbc);
+}
+
+/* Set Encryption key request from DLC */
+int dect_mac_enc_key_req(const struct dect_cluster *cl, u32 mcei, u64 ck)
+{
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+	int err;
+
+	mbc = dect_mbc_get_by_mcei(cl, mcei);
+	if (mbc == NULL)
+		return -ENOENT;
+	mbc_debug(mbc, "MAC_ENC_KEY-req: key: %016llx\n", (unsigned long long)ck);
+
+	mbc->ck = ck;
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		err = tb->ch->ops->tbc_enc_key_req(tb->ch, &tb->id, ck);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Change encryption status requst from DLC */
+int dect_mac_enc_eks_req(const struct dect_cluster *cl, u32 mcei,
+			 enum dect_cipher_states status)
+{
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+	int err;
+
+	mbc = dect_mbc_get_by_mcei(cl, mcei);
+	if (mbc == NULL)
+		return -ENOENT;
+	mbc_debug(mbc, "MAC_ENC_EKS-req: status: %d\n", status);
+
+	if (mbc->cipher_state == status)
+		return 0;
+
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		err = tb->ch->ops->tbc_enc_eks_req(tb->ch, &tb->id, status);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static void dect_tbc_data_ind(const struct dect_cluster_handle *clh,
+			      const struct dect_tbc_id *id,
+			      enum dect_data_channels chan,
+			      struct sk_buff *skb)
+{
+	const struct dect_cluster *cl = dect_cluster(clh);
+	struct dect_mbc *mbc;
+	struct dect_tb *tb;
+
+	mbc = dect_mbc_get_by_tbc_id(cl, id);
+	if (mbc == NULL)
+		goto err;
+	mbc_debug(mbc, "TBC_DATA-ind: TBEI: %u LBN: %u chan: %u len: %u\n",
+		  id->tbei, id->lbn, chan, skb->len);
+
+	switch (chan) {
+	case DECT_MC_C_S:
+		/* Drop duplicate segments */
+		if (DECT_CS_CB(skb)->seq != mbc->cs_rx_seq)
+			goto err;
+		if (mbc->cs_rx_skb != NULL)
+			goto err;
+		mbc->cs_rx_seq = !mbc->cs_rx_seq;
+		mbc->cs_rx_skb = skb;
+		return;
+	case DECT_MC_I_N:
+		tb = dect_mbc_tb_get_by_tbei(mbc, id);
+		if (tb == NULL)
+			goto err;
+		tb->b_rx_skb = skb;
+		return;
+	default:
+		break;
+	}
+err:
+	kfree_skb(skb);
+}
+
+static void dect_cluster_unbind_cell(struct dect_cluster_handle *clh,
+				     struct dect_cell_handle *ch)
+{
+	list_del(&ch->list);
+}
+
+static int dect_cluster_enable_cell(struct dect_cluster *cl,
+				    struct dect_cell_handle *ch)
+{
+	int err;
+
+	err = ch->ops->preload(ch, &cl->pari, ch->rpn, &cl->si);
+	if (err < 0)
+		return err;
+
+	err = ch->ops->enable(ch);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int dect_cluster_bind_cell(struct dect_cluster_handle *clh,
+				  struct dect_cell_handle *ch)
+{
+	struct dect_cluster *cl = dect_cluster(clh);
+	u8 rpn, max;
+	int err;
+
+	/* Allocate RPN for the cell */
+	max = 8;
+	for (rpn = 0; rpn < max; rpn++) {
+		if (!dect_cluster_get_cell_by_rpn(cl, rpn))
+			break;
+	}
+	if (rpn == max)
+		return -EMFILE;
+
+	ch->clh = clh;
+	ch->rpn = rpn;
+
+	err = ch->ops->set_mode(ch, cl->mode);
+	if (err < 0)
+		return err;
+
+	err = dect_cluster_enable_cell(cl, ch);
+	if (err < 0)
+		return err;
+
+	list_add_tail(&ch->list, &cl->cells);
+	return 0;
+}
+
+static const struct dect_ccf_ops dect_ccf_ops = {
+	.bind			= dect_cluster_bind_cell,
+	.unbind			= dect_cluster_unbind_cell,
+	.time_ind		= dect_ccf_time_ind,
+	.scan_report		= dect_scan_report,
+	.mac_info_ind		= dect_mac_info_ind,
+	.tbc_establish_ind	= dect_tbc_establish_ind,
+	.tbc_establish_cfm	= dect_tbc_establish_cfm,
+	.tbc_event_ind		= dect_tbc_event_ind,
+	.tbc_handover_req	= dect_tbc_handover_req,
+	.tbc_dis_ind		= dect_tbc_dis_ind,
+	.tbc_data_ind		= dect_tbc_data_ind,
+	.bmc_page_ind		= dect_bmc_page_ind,
+};
+
+static int dect_cluster_preload(struct dect_cluster *cl,
+				const struct dect_ari *pari,
+				const struct dect_si *si)
+{
+	const struct dect_cell_handle *ch;
+	int err = 0;
+
+	list_for_each_entry(ch, &cl->cells, list) {
+		err = ch->ops->preload(ch, pari, ch->rpn, si);
+		if (err < 0)
+			return err;
+	}
+
+	cl->pari = *pari;
+	cl->si   = *si;
+	return 0;
+}
+
+static int dect_cluster_scan(struct dect_cluster *cl,
+			     const struct dect_llme_req *lreq,
+			     const struct dect_ari *pari,
+			     const struct dect_ari *pari_mask)
+{
+	struct dect_cell_handle *ch;
+
+	ch = dect_cluster_get_cell_by_rpn(cl, 0);
+	if (ch == NULL)
+		return -ENOENT;
+	return ch->ops->scan(ch, lreq, pari, pari_mask);
+}
+
+static void dect_fp_init_si(struct dect_cluster *cl)
+{
+	struct dect_si *si = &cl->si;
+
+	/* Make phone not go into "call technician" mode :) */
+	si->fpc.fpc = DECT_FPC_FULL_SLOT |
+		      DECT_FPC_CO_SETUP_ON_DUMMY |
+		      DECT_FPC_CL_UPLINK |
+		      DECT_FPC_CL_DOWNLINK |
+		      DECT_FPC_BASIC_A_FIELD_SETUP |
+		      DECT_FPC_ADV_A_FIELD_SETUP |
+		      DECT_FPC_CF_MESSAGES |
+		      DECT_FPC_IN_MIN_DELAY |
+		      DECT_FPC_IN_NORM_DELAY |
+		      DECT_FPC_IP_ERROR_DETECTION |
+		      DECT_FPC_IP_ERROR_CORRECTION;
+	si->fpc.hlc = DECT_HLC_ADPCM_G721_VOICE |
+		      DECT_HLC_GAP_PAP_BASIC_SPEECH |
+		      DECT_HLC_CISS_SERVICE |
+		      DECT_HLC_CLMS_SERVICE |
+		      DECT_HLC_COMS_SERVICE |
+		      DECT_HLC_LOCATION_REGISTRATION |
+		      DECT_HLC_ACCESS_RIGHTS_REQUESTS |
+		      DECT_HLC_STANDARD_AUTHENTICATION |
+		      DECT_HLC_STANDARD_CIPHERING;
+}
+
+static int dect_cluster_init(struct dect_cluster *cl)
+{
+	spin_lock_init(&cl->lock);
+	INIT_LIST_HEAD(&cl->bmc.bcs);
+	INIT_LIST_HEAD(&cl->mbcs);
+	INIT_LIST_HEAD(&cl->cells);
+	INIT_LIST_HEAD(&cl->mac_connections);
+	dect_timer_base_init(cl->timer_base, DECT_TIMER_TX);
+	dect_timer_base_init(cl->timer_base, DECT_TIMER_RX);
+
+	if (cl->mode == DECT_MODE_FP)
+		dect_fp_init_si(cl);
+
+	cl->handle.ops = &dect_ccf_ops;
+	cl->handle.index = cl->index;
+
+	return dect_ccp_cluster_init(cl);
+}
+
+static void dect_cluster_shutdown(struct dect_cluster *cl)
+{
+	struct dect_cell_handle *ch, *ch_next;
+	struct dect_mbc *mbc, *mbc_next;
+
+	list_for_each_entry_safe(mbc, mbc_next, &cl->mbcs, list) {
+		dect_mac_dis_ind(cl, mbc->id.mcei, DECT_REASON_UNKNOWN);
+		dect_mbc_release(mbc);
+	}
+
+	list_for_each_entry_safe(ch, ch_next, &cl->cells, list)
+		dect_cluster_unbind_cell(&cl->handle, ch);
+
+	dect_ccp_cluster_shutdown(cl);
+}
+
+/*
+ * LLME netlink interface
+ */
+
+static struct sk_buff *dect_llme_fill(const struct dect_cluster *cl,
+				      const struct dect_llme_req *lreq,
+				      u8 op, u8 type,
+				      int (*fill)(const struct dect_cluster *,
+						  struct sk_buff *, const void *),
+				      const void *data);
+
+static void dect_llme_req_init(struct dect_llme_req *lreq,
+			       const struct nlmsghdr *nlh,
+			       const struct sk_buff *skb)
+{
+	memcpy(&lreq->nlh, nlh, sizeof(lreq->nlh));
+	lreq->nlpid = NETLINK_CB(skb).portid;
+}
+
+static int dect_fill_ari(struct sk_buff *skb, const struct dect_ari *ari, int attr)
+{
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, attr);
+	if (nla == NULL)
+		goto nla_put_failure;
+
+	nla_put_u8(skb, DECTA_ARI_CLASS, ari->arc);
+	nla_put_u32(skb, DECTA_ARI_FPN, ari->fpn);
+
+	switch (ari->arc) {
+	case DECT_ARC_A:
+		nla_put_u16(skb, DECTA_ARI_EMC, ari->emc);
+		break;
+	case DECT_ARC_B:
+		nla_put_u16(skb, DECTA_ARI_EIC, ari->eic);
+		nla_put_u32(skb, DECTA_ARI_FPS, ari->fps);
+		break;
+	case DECT_ARC_C:
+		nla_put_u16(skb, DECTA_ARI_POC, ari->poc);
+		nla_put_u32(skb, DECTA_ARI_FPS, ari->fps);
+		break;
+	case DECT_ARC_D:
+		nla_put_u32(skb, DECTA_ARI_GOP, ari->gop);
+		break;
+	case DECT_ARC_E:
+		nla_put_u16(skb, DECTA_ARI_FIL, ari->fil);
+		break;
+	}
+	nla_nest_end(skb, nla);
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+static const struct nla_policy dect_ari_policy[DECTA_ARI_MAX + 1] = {
+	[DECTA_ARI_CLASS]	= { .type = NLA_U8 },
+	[DECTA_ARI_FPN]		= { .type = NLA_U32 },
+	[DECTA_ARI_FPS]		= { .type = NLA_U32 },
+	[DECTA_ARI_EMC]		= { .type = NLA_U16 },
+	[DECTA_ARI_EIC]		= { .type = NLA_U16 },
+	[DECTA_ARI_POC]		= { .type = NLA_U16 },
+	[DECTA_ARI_GOP]		= { .type = NLA_U32 },
+	[DECTA_ARI_FIL]		= { .type = NLA_U32 },
+};
+
+static const u32 dect_ari_valid_attrs[] = {
+	[DECT_ARC_A]		= (1 << DECTA_ARI_EMC),
+	[DECT_ARC_B]		= (1 << DECTA_ARI_EIC) | (1 << DECTA_ARI_FPS),
+	[DECT_ARC_C]		= (1 << DECTA_ARI_POC) | (1 << DECTA_ARI_FPS),
+	[DECT_ARC_D]		= (1 << DECTA_ARI_GOP),
+	[DECT_ARC_E]		= (1 << DECTA_ARI_FIL),
+};
+
+static int dect_nla_parse_ari(struct dect_ari *ari, const struct nlattr *nla)
+{
+	struct nlattr *tb[DECTA_ARI_MAX + 1];
+	unsigned int attr;
+	int err;
+
+	err = nla_parse_nested(tb, DECTA_ARI_MAX, nla, dect_ari_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[DECTA_ARI_CLASS] == NULL)
+		return -EINVAL;
+
+	memset(ari, 0, sizeof(*ari));
+	ari->arc = nla_get_u8(tb[DECTA_ARI_CLASS]);
+	if (ari->arc > DECT_ARC_E)
+		return -EINVAL;
+
+	for (attr = DECTA_ARI_UNSPEC + 1; attr <= DECTA_ARI_MAX; attr++) {
+		if (tb[attr] == NULL)
+			continue;
+
+		switch (attr) {
+		case DECTA_ARI_CLASS:
+		case DECTA_ARI_FPN:
+			/* always valid */
+			break;
+		default:
+			if (!(dect_ari_valid_attrs[ari->arc] & (1 << attr)))
+				return -EINVAL;
+			break;
+		}
+	}
+
+	if (tb[DECTA_ARI_FPN] != NULL)
+		ari->fpn = nla_get_u32(tb[DECTA_ARI_FPN]);
+	if (tb[DECTA_ARI_FPS] != NULL)
+		ari->fps = nla_get_u32(tb[DECTA_ARI_FPS]);
+
+	switch (ari->arc) {
+	case DECT_ARC_A:
+		if (tb[DECTA_ARI_EMC] != NULL)
+			ari->emc = nla_get_u16(tb[DECTA_ARI_EMC]);
+		break;
+	case DECT_ARC_B:
+		if (tb[DECTA_ARI_EIC] != NULL)
+			ari->eic = nla_get_u16(tb[DECTA_ARI_EIC]);
+		break;
+	case DECT_ARC_C:
+		if (tb[DECTA_ARI_POC] != NULL)
+			ari->poc = nla_get_u16(tb[DECTA_ARI_POC]);
+		break;
+	case DECT_ARC_D:
+		if (tb[DECTA_ARI_GOP] != NULL)
+			ari->gop = nla_get_u32(tb[DECTA_ARI_GOP]);
+		break;
+	case DECT_ARC_E:
+		if (tb[DECTA_ARI_FIL] != NULL)
+			ari->fil = nla_get_u16(tb[DECTA_ARI_FIL]);
+		break;
+	}
+	return 0;
+}
+
+static int dect_fill_sari(struct sk_buff *skb, const struct dect_sari *sari,
+			  int attr)
+{
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, attr);
+	if (nla == NULL)
+		goto nla_put_failure;
+	if (dect_fill_ari(skb, &sari->ari, DECTA_SARI_ARI) < 0)
+		goto nla_put_failure;
+	if (sari->black)
+		nla_put_flag(skb, DECTA_SARI_BLACK);
+	if (sari->tari)
+		nla_put_flag(skb, DECTA_SARI_TARI);
+	nla_nest_end(skb, nla);
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+static int dect_llme_fill_mac_info(const struct dect_cluster *cl,
+				   struct sk_buff *skb, const void *data)
+{
+	const struct dect_si *si = data;
+	struct nlattr *nla;
+	unsigned int i;
+
+	if (si->mask & (1 << DECT_TM_TYPE_SARI) && si->num_saris > 0) {
+		nla = nla_nest_start(skb, DECTA_MAC_INFO_SARI_LIST);
+		if (nla == NULL)
+			goto nla_put_failure;
+		for (i = 0; i < si->num_saris; i++) {
+			if (dect_fill_sari(skb, &si->sari[i],
+					   DECTA_LIST_ELEM) < 0)
+				goto nla_put_failure;
+		}
+		nla_nest_end(skb, nla);
+	}
+
+	nla_put_u8(skb, DECTA_MAC_INFO_RPN, cl->rpn);
+
+	if (si->mask & (1 << DECT_TM_TYPE_FPC)) {
+		nla_put_u32(skb, DECTA_MAC_INFO_FPC, si->fpc.fpc);
+		nla_put_u16(skb, DECTA_MAC_INFO_HLC, si->fpc.hlc);
+	}
+
+	if (si->mask & (1 << DECT_TM_TYPE_EFPC)) {
+		nla_put_u16(skb, DECTA_MAC_INFO_EFPC, si->efpc.fpc);
+		nla_put_u32(skb, DECTA_MAC_INFO_EHLC, si->efpc.hlc);
+	}
+
+	if (si->mask & (1 << DECT_TM_TYPE_EFPC2)) {
+		nla_put_u16(skb, DECTA_MAC_INFO_EFPC2, si->efpc2.fpc);
+		nla_put_u32(skb, DECTA_MAC_INFO_EHLC2, si->efpc2.hlc);
+	}
+
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int dect_llme_fill_scan_result(const struct dect_cluster *cl,
+				      struct sk_buff *skb, const void *data)
+{
+	const struct dect_scan_result *res = data;
+	const struct dect_idi *idi = &res->idi;
+	const struct dect_si *si = &res->si;
+
+	nla_put_u8(skb, DECTA_MAC_INFO_RSSI, res->rssi >> DECT_RSSI_AVG_SCALE);
+
+	if (dect_fill_ari(skb, &idi->pari, DECTA_MAC_INFO_PARI) < 0)
+		goto nla_put_failure;
+	nla_put_u8(skb, DECTA_MAC_INFO_RPN, idi->rpn);
+
+	dect_llme_fill_mac_info(cl, skb, si);
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static void dect_llme_scan_result_notify(const struct dect_cluster *cl,
+					 const struct dect_scan_result *res)
+{
+	struct sk_buff *skb;
+	u32 pid = res->lreq.nlpid;
+	int err = 0;
+
+	skb = dect_llme_fill(cl, &res->lreq,
+			     DECT_LLME_INDICATE, DECT_LLME_MAC_INFO,
+			     dect_llme_fill_scan_result, res);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto err;
+	}
+	nlmsg_notify(dect_nlsk, skb, pid, DECTNLGRP_LLME, 1, GFP_ATOMIC);
+err:
+	if (err < 0)
+		netlink_set_err(dect_nlsk, pid, DECTNLGRP_LLME, err);
+}
+
+static void dect_llme_mac_info_ind(const struct dect_cluster *cl,
+				   const struct dect_idi *idi,
+				   const struct dect_si *si)
+{
+	struct sk_buff *skb;
+	int err = 0;
+
+	skb = dect_llme_fill(cl, NULL,
+			     DECT_LLME_INDICATE, DECT_LLME_MAC_INFO,
+			     dect_llme_fill_mac_info, si);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto err;
+	}
+	nlmsg_notify(dect_nlsk, skb, 0, DECTNLGRP_LLME, 0, GFP_ATOMIC);
+err:
+	if (err < 0)
+		netlink_set_err(dect_nlsk, 0, DECTNLGRP_LLME, err);
+}
+
+static int dect_llme_mac_info_req(struct dect_cluster *cl,
+				  const struct sk_buff *skb_in,
+				  const struct nlmsghdr *nlh,
+				  const struct nlattr *tb[DECTA_MAC_INFO_MAX + 1])
+{
+	struct dect_llme_req lreq;
+	struct sk_buff *skb;
+
+	dect_llme_req_init(&lreq, nlh, skb_in);
+	skb = dect_llme_fill(cl, &lreq,
+			     DECT_LLME_INDICATE, DECT_LLME_MAC_INFO,
+			     dect_llme_fill_mac_info, &cl->si);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	return nlmsg_unicast(dect_nlsk, skb, lreq.nlpid);
+}
+
+static int dect_llme_mac_info_res(struct dect_cluster *cl,
+				  const struct sk_buff *skb_in,
+				  const struct nlmsghdr *nlh,
+				  const struct nlattr *tb[DECTA_MAC_INFO_MAX + 1])
+{
+	struct dect_cell_handle *ch;
+	struct dect_ari pari;
+	int err;
+
+	if (cl->mode != DECT_MODE_PP)
+		return -EOPNOTSUPP;
+
+	if (tb[DECTA_MAC_INFO_PARI] != NULL) {
+		err = dect_nla_parse_ari(&pari, tb[DECTA_MAC_INFO_PARI]);
+		if (err < 0)
+			return err;
+	} else
+		return -EINVAL;
+
+	ch = dect_cluster_get_cell_by_rpn(cl, 0);
+	if (ch == NULL)
+		return -EHOSTUNREACH;
+
+	cl->pari = pari;
+	memset(&cl->si, 0, sizeof(cl->si));
+
+	return dect_cluster_enable_cell(cl, ch);
+}
+
+static const struct nla_policy dect_llme_mac_info_policy[DECTA_MAC_INFO_MAX + 1] =  {
+	[DECTA_MAC_INFO_PARI]		= { .type = NLA_NESTED },
+	[DECTA_MAC_INFO_RPN]		= { .type = NLA_U8 },
+	[DECTA_MAC_INFO_RSSI]		= { .type = NLA_U8 },
+	[DECTA_MAC_INFO_SARI_LIST]	= { .type = NLA_NESTED },
+	[DECTA_MAC_INFO_FPC]		= { .type = NLA_U32 },
+	[DECTA_MAC_INFO_HLC]		= { .type = NLA_U16 },
+	[DECTA_MAC_INFO_EFPC]		= { .type = NLA_U16 },
+	[DECTA_MAC_INFO_EHLC]		= { .type = NLA_U32 },
+	[DECTA_MAC_INFO_EFPC2]		= { .type = NLA_U16 },
+	[DECTA_MAC_INFO_EHLC2]		= { .type = NLA_U32 },
+};
+
+static int dect_llme_scan_request(struct dect_cluster *cl,
+				  const struct sk_buff *skb,
+				  const struct nlmsghdr *nlh,
+				  const struct nlattr *tb[DECTA_MAC_INFO_MAX + 1])
+{
+	struct dect_llme_req lreq;
+	struct dect_ari pari, pari_mask;
+	int err;
+
+	if (tb[DECTA_MAC_INFO_PARI] != NULL) {
+		err = dect_nla_parse_ari(&pari, tb[DECTA_MAC_INFO_PARI]);
+		if (err < 0)
+			return err;
+	} else
+		memset(&pari, 0, sizeof(pari));
+	memset(&pari_mask, 0, sizeof(pari_mask));
+
+	dect_llme_req_init(&lreq, nlh, skb);
+	return dect_cluster_scan(cl, &lreq, &pari, &pari_mask);
+}
+
+static int dect_llme_mac_rfp_preload(struct dect_cluster *cl,
+				     const struct sk_buff *skb,
+				     const struct nlmsghdr *nlh,
+				     const struct nlattr *tb[DECTA_MAC_INFO_MAX + 1])
+{
+	struct dect_ari pari;
+	struct dect_si si;
+	int err = 0;
+	u32 num;
+
+	if (cl->mode != DECT_MODE_FP)
+		return -EINVAL;
+
+	if (tb[DECTA_MAC_INFO_PARI] != NULL) {
+		err = dect_nla_parse_ari(&pari, tb[DECTA_MAC_INFO_PARI]);
+		if (err < 0)
+			return err;
+	} else
+		pari = cl->pari;
+
+	si = cl->si;
+	if (tb[DECTA_MAC_INFO_HLC])
+		si.fpc.hlc = nla_get_u16(tb[DECTA_MAC_INFO_HLC]);
+	if (tb[DECTA_MAC_INFO_EHLC])
+		si.efpc.hlc = nla_get_u32(tb[DECTA_MAC_INFO_EHLC]);
+	if (tb[DECTA_MAC_INFO_EHLC2])
+		si.efpc2.hlc = nla_get_u32(tb[DECTA_MAC_INFO_EHLC2]);
+
+	if (si.efpc2.fpc || si.efpc2.hlc)
+		si.efpc.fpc |= DECT_EFPC_EXTENDED_FP_INFO2;
+	else
+		si.efpc.fpc &= ~DECT_EFPC_EXTENDED_FP_INFO2;
+
+	if (si.efpc.fpc || si.efpc.hlc)
+		si.fpc.fpc |= DECT_FPC_EXTENDED_FP_INFO;
+	else
+		si.fpc.fpc &= ~DECT_FPC_EXTENDED_FP_INFO;
+
+        if (tb[DECTA_MAC_INFO_MFN]) {
+                num = nla_get_u32(tb[DECTA_MAC_INFO_MFN]);
+                if (num >= (1 << 24))
+                        return -EINVAL;
+                si.mfn.num = num;
+        }
+
+	return dect_cluster_preload(cl, &pari, &si);
+}
+
+static struct sk_buff *dect_llme_fill(const struct dect_cluster *cl,
+				      const struct dect_llme_req *lreq,
+				      u8 op, u8 type,
+				      int (*fill)(const struct dect_cluster *,
+						  struct sk_buff *, const void *),
+				      const void *data)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	struct dectmsg *dm;
+	struct nlattr *nest;
+	u32 seq = lreq ? lreq->nlh.nlmsg_seq : 0;
+	int err = -ENOBUFS;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+	if (skb == NULL)
+		goto err1;
+
+	nlh = nlmsg_put(skb, 0, seq, DECT_LLME_MSG, sizeof(*dm), NLMSG_DONE);
+	if (nlh == NULL) {
+		err = -EMSGSIZE;
+		goto err2;
+	}
+
+	dm = nlmsg_data(nlh);
+	dm->dm_index = cl->index;
+
+	nla_put_u8(skb, DECTA_LLME_OP, op);
+	nla_put_u8(skb, DECTA_LLME_TYPE, type);
+	nest = nla_nest_start(skb, DECTA_LLME_DATA);
+	if (nest == NULL)
+		goto nla_put_failure;
+	if (fill(cl, skb, data) < 0)
+		goto nla_put_failure;
+	nla_nest_end(skb, nest);
+
+	nlmsg_end(skb, nlh);
+	return skb;
+
+nla_put_failure:
+err2:
+	kfree_skb(skb);
+err1:
+	return ERR_PTR(err);
+}
+
+static const struct dect_llme_link {
+	struct {
+		int (*doit)(struct dect_cluster *cl, const struct sk_buff *,
+			    const struct nlmsghdr *, const struct nlattr *[]);
+	} ops[DECT_LLME_MAX + 1];
+	const struct nla_policy *policy;
+	unsigned int maxtype;
+} dect_llme_dispatch[DECT_LLME_MAX + 1] = {
+	[DECT_LLME_SCAN]	= {
+		.policy		= dect_llme_mac_info_policy,
+		.maxtype	= DECTA_MAC_INFO_MAX,
+		.ops		= {
+			[DECT_LLME_REQUEST].doit = dect_llme_scan_request,
+		},
+	},
+	[DECT_LLME_MAC_INFO] = {
+		.policy		= dect_llme_mac_info_policy,
+		.maxtype	= DECTA_MAC_INFO_MAX,
+		.ops		= {
+			[DECT_LLME_REQUEST].doit = dect_llme_mac_info_req,
+			[DECT_LLME_RESPONSE].doit = dect_llme_mac_info_res,
+		},
+	},
+	[DECT_LLME_MAC_RFP_PRELOAD] = {
+		.policy		= dect_llme_mac_info_policy,
+		.maxtype	= DECTA_MAC_INFO_MAX,
+		.ops		= {
+			[DECT_LLME_REQUEST].doit = dect_llme_mac_rfp_preload,
+		},
+	},
+};
+
+static const struct nla_policy dect_llme_policy[DECTA_LLME_MAX + 1] = {
+	[DECTA_LLME_OP]		= { .type = NLA_U8 },
+	[DECTA_LLME_TYPE]	= { .type = NLA_U8 },
+	[DECTA_LLME_DATA]	= { .type = NLA_NESTED },
+};
+
+static int dect_llme_msg(const struct sk_buff *skb,
+			 const struct nlmsghdr *nlh,
+			 const struct nlattr *tb[DECTA_LLME_MAX + 1])
+{
+	const struct dect_llme_link *link;
+	struct dect_cluster *cl;
+	struct dectmsg *dm;
+	enum dect_llme_msg_types type;
+	enum dect_llme_ops op;
+	int err;
+
+	if (tb[DECTA_LLME_OP] == NULL ||
+	    tb[DECTA_LLME_TYPE] == NULL ||
+	    tb[DECTA_LLME_DATA] == NULL)
+		return -EINVAL;
+
+	dm = nlmsg_data(nlh);
+	cl = dect_cluster_get_by_index(dm->dm_index);
+	if (cl == NULL)
+		return -ENODEV;
+
+	type = nla_get_u8(tb[DECTA_LLME_TYPE]);
+	if (type > DECT_LLME_MAX)
+		return -EINVAL;
+	link = &dect_llme_dispatch[type];
+
+	op = nla_get_u8(tb[DECTA_LLME_OP]);
+	switch (op) {
+	case DECT_LLME_REQUEST:
+	case DECT_LLME_INDICATE:
+	case DECT_LLME_RESPONSE:
+	case DECT_LLME_CONFIRM:
+		if (link->ops[op].doit == NULL)
+			return -EOPNOTSUPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (1) {
+		struct nlattr *nla[link->maxtype + 1];
+
+		err = nla_parse_nested(nla, link->maxtype, tb[DECTA_LLME_DATA],
+				       link->policy);
+		if (err < 0)
+			return err;
+		return link->ops[op].doit(cl, skb, nlh,
+					  (const struct nlattr **)nla);
+	}
+}
+
+/*
+ * Cluster netlink interface
+ */
+
+static int dect_cluster_alloc_index(void)
+{
+	static int index;
+
+	for (;;) {
+		if (++index <= 0)
+			index = 1;
+		if (!dect_cluster_get_by_index(index))
+			return index;
+	}
+}
+
+static int dect_fill_tb(struct sk_buff *skb, const struct dect_tb *tb)
+{
+	struct nlattr *nest;
+
+	nest = nla_nest_start(skb, DECTA_LIST_ELEM);
+	if (nest == NULL)
+		goto nla_put_failure;
+	nla_put_u8(skb, DECTA_MBC_TB_LBN, tb->id.lbn);
+	nla_put_u8(skb, DECTA_MBC_TB_ECN, tb->id.ecn);
+	nla_put_u8(skb, DECTA_MBC_TB_CELL, tb->ch->rpn);
+	nla_put_u8(skb, DECTA_MBC_TB_RX_SLOT, tb->rx_slot);
+	nla_put_u8(skb, DECTA_MBC_TB_TX_SLOT, tb->tx_slot);
+	nla_nest_end(skb, nest);
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int dect_fill_mbc(struct sk_buff *skb, const struct dect_mbc *mbc)
+{
+	struct nlattr *nest, *stats, *tbs;
+	struct dect_tb *tb;
+	int err;
+
+	nest = nla_nest_start(skb, DECTA_LIST_ELEM);
+	if (nest == NULL)
+		goto nla_put_failure;
+	nla_put_u32(skb, DECTA_MBC_MCEI, mbc->id.mcei);
+	nla_put_u8(skb, DECTA_MBC_SERVICE, mbc->service);
+	nla_put_u8(skb, DECTA_MBC_STATE, mbc->state);
+	nla_put_u8(skb, DECTA_MBC_CIPHER_STATE, mbc->cipher_state);
+
+	stats = nla_nest_start(skb, DECTA_MBC_STATS);
+	if (stats == NULL)
+		goto nla_put_failure;
+	nla_put_u32(skb, DECTA_MBC_STATS_CS_RX_BYTES, mbc->stats.cs_rx_bytes);
+	nla_put_u32(skb, DECTA_MBC_STATS_CS_TX_BYTES, mbc->stats.cs_tx_bytes);
+	nla_put_u32(skb, DECTA_MBC_STATS_I_RX_BYTES, mbc->stats.i_rx_bytes);
+	nla_put_u32(skb, DECTA_MBC_STATS_I_TX_BYTES, mbc->stats.i_tx_bytes);
+	nla_put_u32(skb, DECTA_MBC_STATS_HANDOVERS, mbc->stats.handovers);
+	nla_nest_end(skb, stats);
+
+	tbs = nla_nest_start(skb, DECTA_MBC_TBS);
+	if (tbs == NULL)
+		goto nla_put_failure;
+	list_for_each_entry(tb, &mbc->tbs, list) {
+		err = dect_fill_tb(skb, tb);
+		if (err < 0)
+			goto nla_put_failure;
+	}
+	nla_nest_end(skb, tbs);
+	nla_nest_end(skb, nest);
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
+static int dect_fill_cluster(struct sk_buff *skb,
+			     const struct dect_cluster *cl,
+			     u16 type, u32 pid, u32 seq, u16 flags)
+{
+	struct nlmsghdr *nlh;
+	struct dectmsg *dm;
+	struct nlattr *nest;
+	struct dect_cell_handle *ch;
+	struct dect_mbc *mbc;
+
+	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*dm), flags);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	dm = nlmsg_data(nlh);
+	dm->dm_index = cl->index;
+	nla_put_string(skb, DECTA_CLUSTER_NAME, cl->name);
+	nla_put_u8(skb, DECTA_CLUSTER_MODE, cl->mode);
+	if (dect_fill_ari(skb, &cl->pari, DECTA_CLUSTER_PARI) < 0)
+		goto nla_put_failure;
+
+	if (!list_empty(&cl->cells)) {
+		nest = nla_nest_start(skb, DECTA_CLUSTER_CELLS);
+		if (nest == NULL)
+			goto nla_put_failure;
+		list_for_each_entry(ch, &cl->cells, list)
+			nla_put_u8(skb, DECTA_LIST_ELEM, ch->rpn);
+		nla_nest_end(skb, nest);
+	}
+
+	if (!list_empty(&cl->mbcs)) {
+		nest = nla_nest_start(skb, DECTA_CLUSTER_MBCS);
+		if (nest == NULL)
+			goto nla_put_failure;
+
+		list_for_each_entry(mbc, &cl->mbcs, list)
+			if (dect_fill_mbc(skb, mbc) < 0)
+				goto nla_put_failure;
+
+		nla_nest_end(skb, nest);
+	}
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static int dect_dump_cluster(struct sk_buff *skb,
+			     struct netlink_callback *cb)
+{
+	const struct dect_cluster *cl;
+	unsigned int idx, s_idx;
+
+	s_idx = cb->args[0];
+	idx = 0;
+	list_for_each_entry(cl, &dect_cluster_list, list) {
+		if (idx < s_idx)
+			goto cont;
+		if (dect_fill_cluster(skb, cl, DECT_NEW_CLUSTER,
+				      NETLINK_CB(cb->skb).portid,
+				      cb->nlh->nlmsg_seq, NLM_F_MULTI) <= 0)
+			break;
+cont:
+		idx++;
+	}
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
+static void dect_notify_cluster(u16 event, const struct dect_cluster *cl,
+				const struct nlmsghdr *nlh, u32 pid)
+{
+	struct sk_buff *skb;
+	bool report = nlh ? nlmsg_report(nlh) : 0;
+	u32 seq = nlh ? nlh->nlmsg_seq : 0;
+	int err = -ENOBUFS;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		goto err;
+
+	err = dect_fill_cluster(skb, cl, event, pid, seq, NLMSG_DONE);
+	if (err < 0) {
+		WARN_ON(err == -EMSGSIZE);
+		kfree_skb(skb);
+		goto err;
+	}
+	nlmsg_notify(dect_nlsk, skb, pid, DECTNLGRP_CLUSTER, report, GFP_KERNEL);
+err:
+	if (err < 0)
+		netlink_set_err(dect_nlsk, pid, DECTNLGRP_CLUSTER, err);
+}
+
+static const struct nla_policy dect_cluster_policy[DECTA_CLUSTER_MAX + 1] = {
+	[DECTA_CLUSTER_NAME]		= { .type = NLA_STRING, .len = DECTNAMSIZ },
+	[DECTA_CLUSTER_MODE]		= { .type = NLA_U8 },
+	[DECTA_CLUSTER_PARI]		= { .len  = NLA_NESTED },
+};
+
+static int dect_new_cluster(const struct sk_buff *skb,
+			    const struct nlmsghdr *nlh,
+			    const struct nlattr *tb[DECTA_CLUSTER_MAX + 1])
+{
+	struct dect_cluster *cl;
+	struct dect_ari pari;
+	enum dect_cluster_modes uninitialized_var(mode);
+	int err;
+
+	if (tb[DECTA_CLUSTER_NAME] == NULL)
+		return -EINVAL;
+
+	if (tb[DECTA_CLUSTER_MODE] != NULL) {
+		mode = nla_get_u8(tb[DECTA_CLUSTER_MODE]);
+		switch (mode) {
+		case DECT_MODE_FP:
+		case DECT_MODE_PP:
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (tb[DECTA_CLUSTER_PARI] != NULL) {
+		err = dect_nla_parse_ari(&pari, tb[DECTA_CLUSTER_PARI]);
+		if (err < 0)
+			return err;
+	}
+
+	cl = dect_cluster_get_by_name(tb[DECTA_CLUSTER_NAME]);
+	if (cl != NULL) {
+		if (nlh->nlmsg_flags & NLM_F_EXCL)
+			return -EEXIST;
+
+		return 0;
+	}
+
+	if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+		return -ENOENT;
+
+	if (tb[DECTA_CLUSTER_MODE] == NULL)
+		return -EINVAL;
+
+	cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+	if (cl == NULL)
+		return -ENOMEM;
+	nla_strlcpy(cl->name, tb[DECTA_CLUSTER_NAME], sizeof(cl->name));
+
+	memcpy(&cl->pari, &pari, sizeof(cl->pari));
+	cl->index = dect_cluster_alloc_index();
+	cl->mode  = mode;
+
+	err = dect_cluster_init(cl);
+	if (err < 0)
+		goto err1;
+
+	list_add_tail(&cl->list, &dect_cluster_list);
+	dect_notify_cluster(DECT_NEW_CLUSTER, cl, nlh, NETLINK_CB(skb).portid);
+	return 0;
+
+err1:
+	kfree(cl);
+	return err;
+}
+
+static int dect_del_cluster(const struct sk_buff *skb,
+			    const struct nlmsghdr *nlh,
+			    const struct nlattr *tb[DECTA_CLUSTER_MAX + 1])
+{
+	struct dect_cluster *cl;
+	struct dectmsg *dm;
+
+	dm = nlmsg_data(nlh);
+	if (dm->dm_index != 0)
+		cl = dect_cluster_get_by_index(dm->dm_index);
+	else if (tb[DECTA_CLUSTER_NAME] != NULL)
+		cl = dect_cluster_get_by_name(tb[DECTA_CLUSTER_NAME]);
+	else
+		return -EINVAL;
+	if (cl == NULL)
+		return -ENODEV;
+
+	dect_cluster_shutdown(cl);
+	list_del(&cl->list);
+
+	dect_notify_cluster(DECT_DEL_CLUSTER, cl, nlh, NETLINK_CB(skb).portid);
+	kfree(cl);
+	return 0;
+}
+
+static int dect_get_cluster(const struct sk_buff *in_skb,
+			    const struct nlmsghdr *nlh,
+			    const struct nlattr *tb[DECTA_CLUSTER_MAX + 1])
+{
+	u32 pid = NETLINK_CB(in_skb).portid;
+	const struct dect_cluster *cl;
+	struct dectmsg *dm;
+	struct sk_buff *skb;
+	int err;
+
+	dm = nlmsg_data(nlh);
+	if (dm->dm_index != 0)
+		cl = dect_cluster_get_by_index(dm->dm_index);
+	else if (tb[DECTA_CLUSTER_NAME] != NULL)
+		cl = dect_cluster_get_by_name(tb[DECTA_CLUSTER_NAME]);
+	else
+		return -EINVAL;
+	if (cl == NULL)
+		return -ENODEV;
+
+	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOMEM;
+	err = dect_fill_cluster(skb, cl, DECT_NEW_CLUSTER, pid,
+			        nlh->nlmsg_seq, NLMSG_DONE);
+	if (err < 0)
+		goto err1;
+	return nlmsg_unicast(dect_nlsk, skb, pid);
+
+err1:
+	kfree_skb(skb);
+	return err;
+}
+
+static const struct dect_netlink_handler dect_cluster_handlers[] = {
+	{
+		/* DECT_NEW_CLUSTER */
+		.policy		= dect_cluster_policy,
+		.maxtype	= DECTA_CLUSTER_MAX,
+		.doit		= dect_new_cluster,
+	},
+	{
+		/* DECT_DEL_CLUSTER */
+		.policy		= dect_cluster_policy,
+		.maxtype	= DECTA_CLUSTER_MAX,
+		.doit		= dect_del_cluster,
+	},
+	{
+		/* DECT_GET_CLUSTER */
+		.policy		= dect_cluster_policy,
+		.maxtype	= DECTA_CLUSTER_MAX,
+		.doit		= dect_get_cluster,
+		.dump		= dect_dump_cluster,
+	},
+	{
+		/* DECT_LLME_MSG */
+		.policy		= dect_llme_policy,
+		.maxtype	= DECTA_LLME_MAX,
+		.doit		= dect_llme_msg,
+	},
+};
+
+static int __init dect_ccf_module_init(void)
+{
+	int err;
+
+	err = dect_bsap_module_init();
+	if (err < 0)
+		goto err1;
+
+	err = dect_ssap_module_init();
+	if (err < 0)
+		goto err2;
+
+	dect_netlink_register_handlers(dect_cluster_handlers, DECT_NEW_CLUSTER,
+				       ARRAY_SIZE(dect_cluster_handlers));
+
+	return 0;
+
+err2:
+	dect_bsap_module_exit();
+err1:
+	return err;
+}
+
+static void __exit dect_ccf_module_exit(void)
+{
+	dect_netlink_unregister_handlers(DECT_NEW_CLUSTER,
+				         ARRAY_SIZE(dect_cluster_handlers));
+	dect_bsap_module_exit();
+	dect_ssap_module_exit();
+}
+
+module_init(dect_ccf_module_init);
+module_exit(dect_ccf_module_exit);
diff --git a/target/linux/generic/files/net/dect/mac_csf.c b/target/linux/generic/files/net/dect/mac_csf.c
new file mode 100644
index 0000000..22be8a0
--- /dev/null
+++ b/target/linux/generic/files/net/dect/mac_csf.c
@@ -0,0 +1,5151 @@
+/*
+ * DECT MAC Cell Site Functions
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifdef CONFIG_DECT_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+#include <net/dect/ccp.h>
+
+/* avoid <KERN_DEBUG> for continuation lines */
+#undef KERN_DEBUG
+#define KERN_DEBUG
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT MAC Cell Site Functions");
+MODULE_LICENSE("GPL");
+
+static void dect_notify_cell(u16 event, const struct dect_cell *cell,
+			     const struct nlmsghdr *nlh, u32 pid);
+static void dect_cell_schedule_page(struct dect_cell *cell, u32 mask);
+
+static const u8 dect_fp_preamble[]	= { 0x55, 0x55, 0xe9, 0x8a};
+static const u8 dect_pp_preamble[]	= { 0xaa, 0xaa, 0x16, 0x75};
+
+static const u8 dect_b_field_sizes[] = {
+	[DECT_PACKET_P00]	= 0,
+	[DECT_PACKET_P32]	= 40,
+	[DECT_PACKET_P640j]	= 80,
+	[DECT_PACKET_P80]	= 100,
+};
+
+static u8 dect_b_field_size(const struct dect_channel_desc *chd)
+{
+	return dect_b_field_sizes[chd->pkt];
+}
+
+#define mac_debug(cell, base, fmt, args...) \
+	pr_debug("%s %u.%.2u.%.2u: " fmt, \
+		 (base) == DECT_TIMER_TX ? "TX" : "RX", \
+		 cell->timer_base[(base)].mfn, cell->timer_base[(base)].framenum, \
+		 cell->timer_base[(base)].slot, ## args)
+
+#define rx_debug(cell, fmt, args...) \
+	mac_debug(cell, DECT_TIMER_RX, fmt, ## args)
+#define tx_debug(cell, fmt, args...) \
+	mac_debug(cell, DECT_TIMER_TX, fmt, ## args)
+
+static LIST_HEAD(dect_cell_list);
+
+struct dect_cell *dect_cell_get_by_index(u32 index)
+{
+	struct dect_cell *cell;
+
+	list_for_each_entry(cell, &dect_cell_list, list) {
+		if (cell->index == index)
+			return cell;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(dect_cell_get_by_index);
+
+static struct dect_cell *dect_cell_get_by_name(const struct nlattr *nla)
+{
+	struct dect_cell *cell;
+
+	list_for_each_entry(cell, &dect_cell_list, list) {
+		if (!nla_strcmp(nla, cell->name))
+			return cell;
+	}
+	return NULL;
+}
+
+static struct dect_cell *dect_cell(const struct dect_cell_handle *ch)
+{
+	return container_of(ch, struct dect_cell, handle);
+}
+
+/*
+ * MAC CSF layer timers
+ */
+
+#if 0
+#define timer_debug(cell, base, fmt, args...) \
+	mac_debug(cell, base, fmt, ## args)
+#else
+#define timer_debug(cell, base, fmt, args...)
+#endif
+
+static u8 dect_slotnum(const struct dect_cell *cell, enum dect_timer_bases b)
+{
+	return __dect_slotnum(&cell->timer_base[b]);
+}
+
+static u8 dect_framenum(const struct dect_cell *cell, enum dect_timer_bases b)
+{
+	return __dect_framenum(&cell->timer_base[b]);
+}
+
+static u32 dect_mfn(const struct dect_cell *cell, enum dect_timer_bases b)
+{
+	return __dect_mfn(&cell->timer_base[b]);
+}
+
+/* Return whether the TX time is in the next frame relative to the RX time */
+static bool dect_tx_time_wrapped(const struct dect_cell *cell)
+{
+	return dect_slotnum(cell, DECT_TIMER_TX) <
+	       dect_slotnum(cell, DECT_TIMER_RX);
+}
+
+/**
+ * dect_timer_synchronize_framenum
+ *
+ * Synchronize the current frame number based on Q-channel reception.
+ *
+ * Q-channel information is transmitted only in frame 8 and serves as an
+ * indirect indication. The TX frame number update needs to take the clock
+ * difference into account.
+ */
+static void dect_timer_synchronize_framenum(struct dect_cell *cell, u8 framenum)
+{
+	cell->timer_base[DECT_TIMER_RX].framenum = framenum;
+	if (dect_tx_time_wrapped(cell))
+		framenum++;
+	cell->timer_base[DECT_TIMER_TX].framenum = framenum;
+}
+
+static void dect_timer_synchronize_mfn(struct dect_cell *cell, u32 mfn)
+{
+	cell->timer_base[DECT_TIMER_RX].mfn = mfn;
+	cell->timer_base[DECT_TIMER_TX].mfn = mfn;
+	cell->timer_sync_stamp = mfn;
+}
+
+static void dect_run_timers(struct dect_cell *cell, enum dect_timer_bases b)
+{
+	__dect_run_timers(cell->name, &cell->timer_base[b]);
+}
+
+static void dect_timer_base_update(struct dect_cell *cell,
+				   enum dect_timer_bases b, u8 slot)
+{
+	struct dect_timer_base *base = &cell->timer_base[b];
+
+	base->slot = slot;
+	if (base->slot == 0) {
+		base->framenum = dect_next_framenum(base->framenum);
+		if (base->framenum == 0)
+			base->mfn = dect_next_mfn(base->mfn);
+	}
+	timer_debug(cell, base, "update time base\n");
+}
+
+/**
+ * dect_timer_add - (re)schedule a timer
+ *
+ * Frame numbers are relative to the current time, slot positions are absolute.
+ * A timer scheduled for (1, 2) will expire in slot 2 in the next frame.
+ *
+ * A frame number of zero will expire at the next occurence of the slot, which
+ * can be within the same frame in case the slot is not already in the past, or
+ * in the next frame in case it is.
+ */
+static void dect_timer_add(struct dect_cell *cell, struct dect_timer *timer,
+			   enum dect_timer_bases base, u32 frame, u8 slot)
+{
+	timer->cell = cell;
+	__dect_timer_add(cell->name, &cell->timer_base[base], timer, frame, slot);
+}
+
+static void dect_timer_setup(struct dect_timer *timer,
+			     void (*func)(struct dect_cell *, void *),
+			     void *data)
+{
+	dect_timer_init(timer);
+	timer->cb.cell = func;
+	timer->data    = data;
+}
+
+/*
+ * Basic Channel lists
+ *
+ * A channel list contains channel descriptions of all physical channels
+ * able to carry the packet type, sorted into multiple bins based on the
+ * maximum RSSI value of the TDD slot pair.
+ *
+ * At any time, only a single incomplete channel list exists that is updated
+ * based on the RSSI measurements gathered by the individual IRC instances.
+ * Once a list is complete, it is added to the list of active channel lists,
+ * replacing the previous one for the same packet type, if any.
+ */
+
+#if 1
+#define chl_debug(cell, chl, fmt, args...) \
+	rx_debug(cell, "channel-list %s (%u): " fmt, \
+		 (chl)->pkt == DECT_PACKET_P00 ? "P00" : \
+		 (chl)->pkt == DECT_PACKET_P08 ? "P08" : \
+		 (chl)->pkt == DECT_PACKET_P32 ? "P32" : "?", \
+		 (chl)->available, ## args)
+#else
+#define chl_debug(cell, chl, fmt, args...)
+#endif
+
+static int dect_chl_schedule_update(struct dect_cell *cell,
+				    enum dect_packet_types pkt);
+
+static struct dect_channel_list *dect_chl_lookup(const struct dect_cell *cell,
+						 enum dect_packet_types pkt)
+{
+	struct dect_channel_list *chl;
+
+	list_for_each_entry(chl, &cell->chanlists, list) {
+		if (chl->pkt == pkt)
+			return chl;
+	}
+	return NULL;
+}
+
+static void dect_chl_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_channel_list *chl = data;
+
+	if (dect_chl_schedule_update(cell, chl->pkt) < 0)
+		dect_timer_add(cell, &chl->timer, DECT_TIMER_RX, 1, 0);
+}
+
+static void dect_chl_release(struct dect_channel_list *chl)
+{
+	dect_timer_del(&chl->timer);
+	kfree(chl);
+}
+
+static struct dect_channel_list *dect_chl_init(struct dect_cell *cell,
+					       enum dect_packet_types pkt)
+{
+	struct dect_channel_list *chl;
+	unsigned int entries, i;
+
+	entries = DECT_CARRIER_NUM * DECT_HALF_FRAME_SIZE;
+	chl = kzalloc(sizeof(*chl) + entries * sizeof(chl->entries[0]), GFP_ATOMIC);
+	if (chl == NULL)
+		return NULL;
+	chl->pkt = pkt;
+	dect_timer_setup(&chl->timer, dect_chl_timer, chl);
+	for (i = 0; i < ARRAY_SIZE(chl->bins); i++)
+		INIT_LIST_HEAD(&chl->bins[i]);
+	for (i = 0; i < entries; i++)
+		INIT_LIST_HEAD(&chl->entries[i].list);
+	return chl;
+}
+
+static int dect_chl_schedule_update(struct dect_cell *cell,
+				    enum dect_packet_types pkt)
+{
+	struct dect_channel_list *chl;
+
+	list_for_each_entry(chl, &cell->chl_pending, list) {
+		if (chl->pkt == pkt)
+			return 0;
+	}
+
+	chl = dect_chl_init(cell, pkt);
+	if (chl == NULL)
+		return -ENOMEM;
+	chl_debug(cell, chl, "schedule update\n");
+	list_add_tail(&chl->list, &cell->chl_pending);
+	return 0;
+}
+
+static struct dect_channel_list *dect_chl_get_pending(struct dect_cell *cell)
+{
+	struct dect_channel_list *chl;
+
+	if (list_empty(&cell->chl_pending))
+		return NULL;
+	chl = list_first_entry(&cell->chl_pending,
+			       struct dect_channel_list,
+			       list);
+	list_del(&chl->list);
+	return chl;
+}
+
+static void dect_chl_update(struct dect_cell *cell,
+			    struct dect_channel_list *chl,
+			    const struct dect_channel_desc *chd, u8 rssi)
+{
+	struct dect_channel_list_entry *e;
+	u8 slot, bin;
+
+	if (rssi > dect_dbm_to_rssi(DECT_CHANNEL_LIST_MAX_DBM)) {
+		chl_debug(cell, chl, "carrier %u slot %u: too much noise: RSSI %u\n",
+			  chd->carrier, chd->slot, rssi);
+		return;
+	}
+
+	slot = chd->slot < 12 ? chd->slot : chd->slot - 12;
+	chl_debug(cell, chl, "update carrier %u slot %u pos %u RSSI %u\n",
+		  chd->carrier, chd->slot, slot, rssi);
+
+	e = &chl->entries[chd->carrier * DECT_HALF_FRAME_SIZE + slot];
+	if (!list_empty(&e->list))
+		return;
+
+	if (chd->slot < DECT_HALF_FRAME_SIZE) {
+		e->slot    = slot;
+		e->carrier = chd->carrier;
+		e->rssi    = rssi;
+	} else if (e->rssi != 0) {
+		e->rssi = max(e->rssi, rssi);
+		bin = rssi * ARRAY_SIZE(chl->bins) / (DECT_RSSI_RANGE + 1);
+
+		list_add_tail(&e->list, &chl->bins[bin]);
+		chl->available++;
+	}
+}
+
+static void dect_chl_update_carrier(struct dect_cell *cell, u8 carrier)
+{
+	struct dect_channel_list *chl, *old;
+
+	chl = cell->chl;
+	chl_debug(cell, chl, "update status %llx rfcars %x carrier %u\n",
+		  (unsigned long long)chl->status, cell->si.ssi.rfcars, carrier);
+
+	chl->status |= 1ULL << carrier;
+	if (chl->status != cell->si.ssi.rfcars)
+		return;
+	cell->chl = NULL;
+
+	chl_debug(cell, chl, "complete %u entries\n", chl->available);
+	old = dect_chl_lookup(cell, chl->pkt);
+	if (old != NULL) {
+		list_del(&old->list);
+		dect_chl_release(old);
+	}
+
+	dect_timer_add(cell, &chl->timer, DECT_TIMER_RX,
+		       DECT_CHANNEL_LIST_MAX_AGE * 2 / 3 *
+		       DECT_FRAMES_PER_SECOND, 0);
+	list_add_tail(&chl->list, &cell->chanlists);
+}
+
+/**
+ * dect_channel_delay - calculate delay in frames until a channel is accessible
+ *
+ * Calculate the delay in frames until one of the remote sides' scans is on the
+ * specified carrier.
+ *
+ * A FP maintains one to three scans, which lag behind each other by three
+ * carriers, a PP maintains zero or one (fast-setup) scan. The PP fast-
+ * setup scan leads the FP primary scan by one carrier.
+ *
+ * Setup needs at least one full frame, therefore a scan reaching a carrier
+ * earlier than that must be treated as reachable one cycle later.
+ */
+static u8 dect_channel_delay(const struct dect_cell *cell,
+			     const struct dect_channel_desc *chd)
+{
+	u64 rfcars = cell->si.ssi.rfcars;
+	u8 i, txs, scn, frames;
+	s8 d;
+
+	if (cell->mode == DECT_MODE_FP) {
+		/* PP fast-setup scan */
+		scn = dect_next_carrier(rfcars, cell->si.ssi.pscn);
+		txs = 1;
+	} else {
+		/* FP primary scan */
+		scn = dect_prev_carrier(rfcars, cell->si.ssi.pscn);
+		txs = min(cell->si.ssi.txs + 1, 3);
+	}
+
+	frames = ~0;
+	for (i = 0; i < txs; i++) {
+		d = dect_carrier_distance(rfcars, scn, chd->carrier);
+#if 0
+		if (dect_slotnum(cell, DECT_TIMER_TX) >= chd->slot)
+			d--;
+#endif
+		/* More than two frames in the future? */
+		if (d <= DECT_CHANNEL_MIN_DELAY)
+			d += hweight64(rfcars);
+
+		frames = min_t(u8, frames, d);
+		pr_debug("rfcars %llx distance %u->%u slot %u: %u frames %u\n",
+			 (unsigned long long)rfcars, scn, chd->carrier,
+			 chd->slot, d, frames);
+
+		scn = dect_carrier_sub(rfcars, scn, 3);
+	}
+
+	return frames;
+}
+
+static void dect_update_blind_full_slots(struct dect_cell *cell)
+{
+	u16 bfs;
+
+	bfs = ~(cell->trg.blind_full_slots | (cell->trg.blind_full_slots >> 12));
+	cell->trg_blind_full_slots = bfs & ((1 << DECT_HALF_FRAME_SIZE) - 1);
+
+	dect_cell_schedule_page(cell, 1 << DECT_TM_TYPE_BFS);
+}
+
+/**
+ * dect_channel_reserve - reserve a channel on a transceiver
+ *
+ * Reserve the specified transceiver and schedule a blind-full-slots
+ * page message if the visibility state changed.
+ */
+static void dect_channel_reserve(struct dect_cell *cell,
+				 struct dect_transceiver *trx,
+				 const struct dect_channel_desc *chd)
+{
+	if (!dect_transceiver_reserve(&cell->trg, trx, chd))
+		return;
+
+	dect_update_blind_full_slots(cell);
+}
+
+/**
+ * dect_channel_release - release a channel on a transceiver
+ *
+ * Release the specified transceiver and schedule a blind-full-slots
+ * page message if the visibility state changed.
+ */
+static void dect_channel_release(struct dect_cell *cell,
+				 struct dect_transceiver *trx,
+				 const struct dect_channel_desc *chd)
+{
+	if (!dect_transceiver_release(&cell->trg, trx, chd))
+		return;
+
+	dect_update_blind_full_slots(cell);
+}
+
+/**
+ * dect_select_transceiver - select a transceiver for placing a bearer
+ *
+ * Select the lowest order transceiver that is able to operate on a physical
+ * channel.
+ */
+static struct dect_transceiver *
+dect_select_transceiver(const struct dect_cell *cell,
+			const struct dect_channel_desc *chd)
+{
+	struct dect_transceiver *trx;
+
+	dect_foreach_transceiver_reverse(trx, &cell->trg) {
+		if (trx->state != DECT_TRANSCEIVER_LOCKED)
+			continue;
+		if (!dect_transceiver_channel_available(trx, chd))
+			continue;
+		return trx;
+	}
+	return NULL;
+}
+
+/**
+ * dect_select_channel - select a physical channel for bearer setup
+ *
+ * @cell:	DECT cell
+ * @trx:	selected transceiver
+ * @chd:	channel description
+ * @rssi:	last measure RSSI value of selected channel
+ * @quick:	prefer quickly accessible channel
+ *
+ * This performs the common steps of channel selection based on channel lists.
+ * In "quick" mode, the selected channel is the first channel accessible within
+ * three TDMA frames from the lowest three available bands. When not in quick
+ * mode or when no channel is accessible within three frames, the first
+ * available channel from the lowest available band is selected.
+ *
+ * "quick" mode is used for setting up pilot bearers and for bearer handover.
+ *
+ * The returned channel description is within the normal transmit half
+ * of the cell's mode.
+ */
+static int dect_select_channel(struct dect_cell *cell,
+			       struct dect_transceiver **trxp,
+			       struct dect_channel_desc *chd, u8 *rssi,
+			       bool quick)
+{
+	struct dect_channel_list_entry *e, *sel;
+	struct dect_channel_list *chl;
+	struct dect_transceiver *trx, *uninitialized_var(tsel);
+	u8 bin, first, last;
+
+	chl = dect_chl_lookup(cell, chd->pkt);
+	if (chl == NULL)
+		return -ENOENT;
+
+	/* Find first non-empty bin */
+	for (first = 0; first < ARRAY_SIZE(chl->bins); first++) {
+		if (!list_empty(&chl->bins[first]))
+			break;
+	}
+	if (first == ARRAY_SIZE(chl->bins))
+		return -ENOSPC;
+
+	sel = NULL;
+retry:
+	last = max_t(u8, first + quick ? 3 : 1, ARRAY_SIZE(chl->bins));
+	for (bin = first; sel == NULL && bin < last; bin++) {
+		list_for_each_entry(e, &chl->bins[bin], list) {
+			u8 n = DECT_HALF_FRAME_SIZE - 1 - e->slot;
+
+			if (!(cell->trg_blind_full_slots & (1 << n)))
+				continue;
+
+			if (cell->mode == DECT_MODE_PP &&
+			    !(cell->blind_full_slots & (1 << n)))
+				continue;
+
+			chd->carrier = e->carrier;
+			chd->slot = dect_normal_transmit_base(cell->mode) + e->slot;
+			if (quick && dect_channel_delay(cell, chd) > 3)
+				continue;
+
+			trx = dect_select_transceiver(cell, chd);
+			if (trx == NULL)
+				continue;
+			if (sel != NULL) {
+				if (trx->index < tsel->index)
+					continue;
+				if (sel->rssi < e->rssi)
+					continue;
+			}
+
+			sel  = e;
+			tsel = trx;
+
+			/* Stop searching if this is the best possible choice */
+			if (tsel->index == hweight16(cell->trg.trxmask))
+				break;
+		}
+	}
+
+	if (sel == NULL) {
+		/* Check the first band again without considering delay when
+		 * no quickly accessible channel is available within the first
+		 * three bands. */
+		if (quick) {
+			quick = false;
+			goto retry;
+		}
+		return -ENOSPC;
+	}
+
+	list_del_init(&sel->list);
+	chl->available--;
+	if (chl->available < DECT_CHANNEL_LIST_LOW_WATERMARK)
+		dect_chl_schedule_update(cell, chl->pkt);
+
+	chd->carrier = sel->carrier;
+	chd->slot = dect_normal_transmit_base(cell->mode) + sel->slot;
+	chl_debug(cell, chl, "select channel: carrier %u slot %u RSSI %u\n",
+		  chd->carrier,	chd->slot, sel->rssi);
+
+	*rssi = sel->rssi;
+	*trxp = tsel;
+	return 0;
+}
+
+static struct dect_dbc *dect_dbc_get(const struct dect_cell *cell)
+{
+	if (list_empty(&cell->dbcs))
+		return NULL;
+	return list_first_entry(&cell->dbcs, struct dect_dbc, list);
+}
+
+
+/*
+ * Tail message parsing/construction
+ */
+
+static enum dect_tail_identifications dect_parse_tail(const struct sk_buff *skb)
+{
+	return skb->data[DECT_HDR_TA_OFF] & DECT_HDR_TA_MASK;
+}
+
+static enum dect_b_identifications dect_parse_b_id(const struct sk_buff *skb)
+{
+	return skb->data[DECT_HDR_TA_OFF] & DECT_HDR_BA_MASK;
+}
+
+static int dect_parse_identities_information(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_idi *idi = &tm->idi;
+	u8 ari_len, rpn_len;
+
+	ari_len = dect_parse_ari(&idi->pari, t << DECT_RFPI_ARI_SHIFT);
+	if (ari_len == 0)
+		return -1;
+	rpn_len = BITS_PER_BYTE * DECT_NT_ID_RFPI_LEN - 1 - ari_len;
+
+	idi->e   = (t & DECT_RFPI_E_FLAG);
+	idi->rpn = (t >> DECT_RFPI_RPN_SHIFT) & ((1 << rpn_len) - 1);
+	tm->type = DECT_TM_TYPE_ID;
+
+	pr_debug("identities information: e: %u class: %u emc: %.4x "
+		 "fpn: %.5x rpn: %x\n", idi->e, idi->pari.arc,
+		 idi->pari.emc, idi->pari.fpn, idi->rpn);
+	return 0;
+}
+
+static u64 dect_build_identities_information(const struct dect_idi *idi)
+{
+	return dect_build_rfpi(idi);
+}
+
+static int dect_parse_static_system_information(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_ssi *ssi = &tm->ssi;
+
+	ssi->nr	    = (t & DECT_QT_SSI_NR_FLAG);
+	ssi->sn     = (t & DECT_QT_SSI_SN_MASK) >> DECT_QT_SSI_SN_SHIFT;
+	ssi->sp     = (t & DECT_QT_SSI_SP_MASK) >> DECT_QT_SSI_SP_SHIFT;
+	ssi->txs    = (t & DECT_QT_SSI_TXS_MASK) >> DECT_QT_SSI_TXS_SHIFT;
+	ssi->mc     = (t & DECT_QT_SSI_MC_FLAG);
+	ssi->rfcars = (t & DECT_QT_SSI_RFCARS_MASK) >> DECT_QT_SSI_RFCARS_SHIFT;
+	ssi->cn     = (t & DECT_QT_SSI_CN_MASK) >> DECT_QT_SSI_CN_SHIFT;
+	ssi->pscn   = (t & DECT_QT_SSI_PSCN_MASK) >> DECT_QT_SSI_PSCN_SHIFT;
+
+	if (ssi->sn > 11 || ssi->cn > 9 || ssi->pscn > 9 || ssi->rfcars == 0)
+		return -1;
+	tm->type = DECT_TM_TYPE_SSI;
+
+	pr_debug("static system information: nr: %u sn: %u cn: %u pscn: %u\n",
+		 ssi->nr, ssi->sn, ssi->cn, ssi->pscn);
+	return 0;
+}
+
+static u64 dect_build_static_system_information(const struct dect_ssi *ssi)
+{
+	u64 t = 0;
+
+	t |= ssi->nr ? DECT_QT_SSI_NR_FLAG : 0;
+	t |= (u64)ssi->sn << DECT_QT_SSI_SN_SHIFT;
+	t |= (u64)ssi->sp << DECT_QT_SSI_SP_SHIFT;
+	t |= (u64)ssi->txs << DECT_QT_SSI_TXS_SHIFT;
+	t |= (u64)ssi->cn << DECT_QT_SSI_CN_SHIFT;
+	t |= ssi->mc ? DECT_QT_SSI_MC_FLAG : 0;
+	t |= (u64)ssi->rfcars << DECT_QT_SSI_RFCARS_SHIFT;
+	t |= (u64)ssi->pscn << DECT_QT_SSI_PSCN_SHIFT;
+	t |= DECT_QT_SI_SSI;
+	return t;
+}
+
+static int dect_parse_extended_rf_carrier_information(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_erfc *erfc = &tm->erfc;
+
+	erfc->rfcars	 = (t & DECT_QT_ERFC_RFCARS_MASK) >>
+			   DECT_QT_ERFC_RFCARS_SHIFT;
+	erfc->band	 = (t & DECT_QT_ERFC_RFBAND_MASK) >>
+			   DECT_QT_ERFC_RFBAND_SHIFT;
+	erfc->num_rfcars = (t & DECT_QT_ERFC_NUM_RFCARS_MASK) >
+			   DECT_QT_ERFC_NUM_RFCARS_SHIFT;
+	tm->type = DECT_TM_TYPE_ERFC;
+
+	pr_debug("extended rf carrier information: rfcars %.6x band %u num %u\n",
+		 erfc->rfcars, erfc->band, erfc->num_rfcars);
+	return 0;
+}
+
+static u64 dect_build_extended_rf_carrier_information(const struct dect_erfc *erfc)
+{
+	u64 t = 0;
+
+	t |= (u64)erfc->rfcars << DECT_QT_ERFC_RFCARS_SHIFT;
+	t |= (u64)erfc->band << DECT_QT_ERFC_RFBAND_SHIFT;
+	t |= (u64)erfc->num_rfcars << DECT_QT_ERFC_NUM_RFCARS_SHIFT;
+	t |= DECT_QT_SI_ERFC;
+	return t;
+}
+
+static int dect_parse_fixed_part_capabilities(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_fpc *fpc = &tm->fpc;
+
+	fpc->fpc = (t & DECT_QT_FPC_CAPABILITY_MASK) >>
+		   DECT_QT_FPC_CAPABILITY_SHIFT;
+	fpc->hlc = (t & DECT_QT_FPC_HLC_MASK) >> DECT_QT_FPC_HLC_SHIFT;
+	tm->type = DECT_TM_TYPE_FPC;
+
+	pr_debug("fixed part capabilities: fpc: %.5x hlc: %.4x\n",
+		 fpc->fpc, fpc->hlc);
+	return 0;
+}
+
+static u64 dect_build_fixed_part_capabilities(const struct dect_fpc *fpc)
+{
+	u64 t = 0;
+
+	t |= (u64)fpc->fpc << DECT_QT_FPC_CAPABILITY_SHIFT;
+	t |= (u64)fpc->hlc << DECT_QT_FPC_HLC_SHIFT;
+	t |= DECT_QT_SI_FPC;
+	return t;
+}
+
+static int dect_parse_extended_fixed_part_capabilities(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_efpc *efpc = &tm->efpc;
+
+	efpc->fpc = (t & DECT_QT_EFPC_EFPC_MASK) >> DECT_QT_EFPC_EFPC_SHIFT;
+	efpc->hlc = (t & DECT_QT_EFPC_EHLC_MASK) >> DECT_QT_EFPC_EHLC_SHIFT;
+	tm->type  = DECT_TM_TYPE_EFPC;
+
+	pr_debug("extended fixed part capabilities: fpc: %.5x hlc: %.6x\n",
+		 efpc->fpc, efpc->hlc);
+	return 0;
+}
+
+static u64 dect_build_extended_fixed_part_capabilities(const struct dect_efpc *efpc)
+{
+	u64 t = 0;
+
+	t |= (u64)efpc->fpc << DECT_QT_EFPC_EFPC_SHIFT;
+	t |= (u64)efpc->hlc << DECT_QT_EFPC_EHLC_SHIFT;
+	t |= DECT_QT_SI_EFPC;
+	return t;
+}
+
+static int dect_parse_extended_fixed_part_capabilities2(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_efpc2 *efpc2 = &tm->efpc2;
+
+	efpc2->fpc = (t & DECT_QT_EFPC2_FPC_MASK) >> DECT_QT_EFPC2_FPC_SHIFT;
+	efpc2->hlc = (t & DECT_QT_EFPC2_HLC_MASK) >> DECT_QT_EFPC2_HLC_SHIFT;
+	tm->type   = DECT_TM_TYPE_EFPC2;
+
+	pr_debug("extended fixed part capabilities2: fpc: %x hlc: %x\n",
+		 efpc2->fpc, efpc2->hlc);
+	return 0;
+}
+
+static u64 dect_build_extended_fixed_part_capabilities2(const struct dect_efpc2 *efpc2)
+{
+	u64 t = 0;
+
+	t |= (u64)efpc2->fpc << DECT_QT_EFPC2_FPC_SHIFT;
+	t |= (u64)efpc2->hlc << DECT_QT_EFPC2_HLC_SHIFT;
+	t |= DECT_QT_SI_EFPC2;
+	return t;
+}
+
+static int dect_parse_sari(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_sari *sari = &tm->sari;
+
+	sari->list_cycle = (((t & DECT_QT_SARI_LIST_CYCLE_MASK) >>
+			     DECT_QT_SARI_LIST_CYCLE_SHIFT) + 1) * 2;
+	sari->tari  = (t & DECT_QT_SARI_TARI_FLAG);
+	sari->black = (t & DECT_QT_SARI_BLACK_FLAG);
+	dect_parse_ari(&sari->ari, t << DECT_QT_SARI_ARI_SHIFT);
+	tm->type = DECT_TM_TYPE_SARI;
+
+	pr_debug("sari: cycle %u tari: %u black: %u\n",
+		 sari->list_cycle, sari->tari, sari->black);
+	return 0;
+}
+
+static u64 dect_build_sari(const struct dect_sari *sari)
+{
+	u64 t = 0;
+
+	t |= sari->tari ? DECT_QT_SARI_TARI_FLAG : 0;
+	t |= sari->black ? DECT_QT_SARI_BLACK_FLAG : 0;
+	t |= dect_build_ari(&sari->ari) >> DECT_QT_SARI_ARI_SHIFT;
+	t |= DECT_QT_SI_SARI;
+	return t;
+}
+
+static int dect_parse_multiframe_number(struct dect_tail_msg *tm, u64 t)
+{
+	tm->mfn.num = (t & DECT_QT_MFN_MASK) >> DECT_QT_MFN_SHIFT;
+	tm->type = DECT_TM_TYPE_MFN;
+
+	pr_debug("multi-frame number: %u\n", tm->mfn.num);
+	return 0;
+}
+
+static u64 dect_build_multiframe_number(const struct dect_mfn *mfn)
+{
+	u64 t = 0;
+
+	t |= (u64)mfn->num << DECT_QT_MFN_SHIFT;
+	t |= DECT_QT_SI_MFN;
+	return t;
+}
+
+static int dect_parse_system_information(struct dect_tail_msg *tm, u64 t)
+{
+	/* clear of memcmp */
+	memset(((void *)tm) + offsetof(struct dect_tail_msg, ssi), 0,
+	       sizeof(*tm) - offsetof(struct dect_tail_msg, ssi));
+
+	switch (t & DECT_QT_H_MASK) {
+	case DECT_QT_SI_SSI:
+	case DECT_QT_SI_SSI2:
+		return dect_parse_static_system_information(tm, t);
+	case DECT_QT_SI_ERFC:
+		return dect_parse_extended_rf_carrier_information(tm, t);
+	case DECT_QT_SI_FPC:
+		return dect_parse_fixed_part_capabilities(tm, t);
+	case DECT_QT_SI_EFPC:
+		return dect_parse_extended_fixed_part_capabilities(tm, t);
+	case DECT_QT_SI_EFPC2:
+		return dect_parse_extended_fixed_part_capabilities2(tm, t);
+	case DECT_QT_SI_SARI:
+		return dect_parse_sari(tm, t);
+	case DECT_QT_SI_MFN:
+		return dect_parse_multiframe_number(tm, t);
+	default:
+		pr_debug("unknown system information type %llx\n",
+			 (unsigned long long)t & DECT_QT_H_MASK);
+		return -1;
+	}
+}
+
+static int dect_parse_blind_full_slots(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_bfs *bfs = &tm->bfs;
+
+	bfs->mask = (t & DECT_PT_BFS_MASK) >> DECT_PT_BFS_SHIFT;
+	tm->type = DECT_TM_TYPE_BFS;
+
+	pr_debug("page: RFPI: %.3x blind full slots: %.3x\n",
+		 tm->page.rfpi, bfs->mask);
+	return 0;
+}
+
+static u64 dect_build_blind_full_slots(const struct dect_bfs *bfs)
+{
+	u64 t = 0;
+
+	t |= (u64)bfs->mask << DECT_PT_BFS_SHIFT;
+	t |= DECT_PT_IT_BLIND_FULL_SLOT;
+	return t;
+}
+
+static int dect_parse_bearer_description(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_bearer_desc *bd = &tm->bd;
+
+	bd->bt = (t & DECT_PT_INFO_TYPE_MASK);
+	bd->sn = (t & DECT_PT_BEARER_SN_MASK) >> DECT_PT_BEARER_SN_SHIFT;
+	bd->sp = (t & DECT_PT_BEARER_SP_MASK) >> DECT_PT_BEARER_SP_SHIFT;
+	bd->cn = (t & DECT_PT_BEARER_CN_MASK) >> DECT_PT_BEARER_CN_SHIFT;
+	if (bd->sn >= DECT_HALF_FRAME_SIZE)
+		return -1;
+	tm->type = DECT_TM_TYPE_BD;
+
+	pr_debug("page: RFPI: %.3x bearer description: bt: %llx sn: %u sp: %u cn: %u\n",
+		 tm->page.rfpi, (unsigned long long)bd->bt, bd->sn, bd->sp, bd->cn);
+	return 0;
+}
+
+static u64 dect_build_bearer_description(const struct dect_bearer_desc *bd)
+{
+	u64 t = 0;
+
+	t |= (u64)bd->sn << DECT_PT_BEARER_SN_SHIFT;
+	t |= (u64)bd->sp << DECT_PT_BEARER_SP_SHIFT;
+	t |= (u64)bd->cn << DECT_PT_BEARER_CN_SHIFT;
+	t |= bd->bt;
+	return t;
+}
+
+static int dect_parse_rfp_identity(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_rfp_id *id = &tm->rfp_id;
+
+	id->id = (t & DECT_PT_RFP_ID_MASK) >> DECT_PT_RFP_ID_SHIFT;
+	tm->type = DECT_TM_TYPE_RFP_ID;
+
+	pr_debug("page: RFPI: %.3x RFP identity: %.3x\n",
+		 tm->page.rfpi, id->id);
+	return 0;
+}
+
+static u64 dect_build_rfp_identity(const struct dect_rfp_id *id)
+{
+	u64 t = 0;
+
+	t |= (u64)id->id << DECT_PT_RFP_ID_SHIFT;
+	t |= DECT_PT_IT_RFP_IDENTITY;
+	return t;
+}
+
+static int dect_parse_rfp_status(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_rfp_status *st = &tm->rfp_status;
+
+	st->rfp_busy = t & DECT_PT_RFPS_RFP_BUSY_FLAG;
+	st->sys_busy = t & DECT_PT_RFPS_SYS_BUSY_FLAG;
+	tm->type = DECT_TM_TYPE_RFP_STATUS;
+
+	pr_debug("page: RFPI: %.3x RFP status: rfp_busy: %d sys_busy: %d\n",
+		 tm->page.rfpi, st->rfp_busy, st->sys_busy);
+	return 0;
+}
+
+static u64 dect_build_rfp_status(const struct dect_rfp_status *st)
+{
+	u64 t = 0;
+
+	t |= st->rfp_busy ? DECT_PT_RFPS_RFP_BUSY_FLAG : 0;
+	t |= st->sys_busy ? DECT_PT_RFPS_SYS_BUSY_FLAG : 0;
+	t |= DECT_PT_IT_RFP_STATUS;
+	return t;
+}
+
+static int dect_parse_active_carriers(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_active_carriers *ac = &tm->active_carriers;
+
+	ac->active = (t & DECT_PT_ACTIVE_CARRIERS_MASK) >>
+		     DECT_PT_ACTIVE_CARRIERS_SHIFT;
+	tm->type = DECT_TM_TYPE_ACTIVE_CARRIERS;
+
+	pr_debug("page: RFPI: %.3x active carriers: %.3x\n",
+		 tm->page.rfpi, ac->active);
+	return 0;
+}
+
+static u64 dect_build_active_carriers(const struct dect_active_carriers *ac)
+{
+	u64 t = 0;
+
+	t |= (u64)ac->active << DECT_PT_ACTIVE_CARRIERS_SHIFT;
+	t |= DECT_PT_IT_ACTIVE_CARRIERS;
+	return t;
+}
+
+static int dect_parse_paging_info(struct dect_tail_msg *tm, u64 t)
+{
+	switch (t & DECT_PT_INFO_TYPE_MASK) {
+	case DECT_PT_IT_BLIND_FULL_SLOT:
+		return dect_parse_blind_full_slots(tm, t);
+	case DECT_PT_IT_OTHER_BEARER:
+	case DECT_PT_IT_RECOMMENDED_OTHER_BEARER:
+	case DECT_PT_IT_GOOD_RFP_BEARER:
+	case DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION:
+	case DECT_PT_IT_CL_BEARER_POSITION:
+		return dect_parse_bearer_description(tm, t);
+	case DECT_PT_IT_RFP_IDENTITY:
+		return dect_parse_rfp_identity(tm, t);
+	case DECT_PT_IT_DUMMY_OR_CL_BEARER_MARKER:
+		pr_debug("dummy or cl bearer marker\n");
+		return 0;
+	case DECT_PT_IT_RFP_STATUS:
+		return dect_parse_rfp_status(tm, t);
+	case DECT_PT_IT_ACTIVE_CARRIERS:
+		return dect_parse_active_carriers(tm, t);
+	default:
+		pr_debug("unknown MAC page info %llx\n",
+			 (unsigned long long)t & DECT_PT_INFO_TYPE_MASK);
+		return -1;
+	}
+}
+
+static int dect_parse_paging_msg(struct dect_tail_msg *tm, u64 t)
+{
+	tm->page.extend = t & DECT_PT_HDR_EXTEND_FLAG;
+	tm->page.length = t & DECT_PT_HDR_LENGTH_MASK;
+
+	switch (tm->page.length) {
+	case DECT_PT_ZERO_PAGE:
+		tm->page.rfpi = (t & DECT_PT_ZP_RFPI_MASK) >>
+				DECT_PT_ZP_RFPI_SHIFT;
+
+		return dect_parse_paging_info(tm, t);
+	case DECT_PT_SHORT_PAGE:
+		tm->page.rfpi = 0;
+		return dect_parse_paging_info(tm, t);
+	case DECT_PT_FULL_PAGE:
+	case DECT_PT_LONG_PAGE:
+	case DECT_PT_LONG_PAGE_FIRST:
+	case DECT_PT_LONG_PAGE_LAST:
+	case DECT_PT_LONG_PAGE_ALL:
+		tm->type = DECT_TM_TYPE_PAGE;
+		pr_debug("full/long page: extend: %u length: %llx\n",
+			 tm->page.extend, (unsigned long long)tm->page.length);
+		return 0;
+	default:
+		pr_debug("invalid page length %llx\n",
+			 (unsigned long long)tm->page.length);
+		return -1;
+	}
+}
+
+static int dect_parse_cctrl_common(struct dect_cctrl *cctl, u64 t)
+{
+	cctl->fmid = (t & DECT_CCTRL_FMID_MASK) >> DECT_CCTRL_FMID_SHIFT;
+	cctl->pmid = (t & DECT_CCTRL_PMID_MASK) >> DECT_CCTRL_PMID_SHIFT;
+
+	pr_debug("cctrl: cmd: %llx fmid: %.3x pmid: %.5x\n",
+		 (unsigned long long)cctl->cmd, cctl->fmid, cctl->pmid);
+	return 0;
+}
+
+static u64 dect_build_cctrl_common(const struct dect_cctrl *cctl)
+{
+	u64 t = 0;
+
+	t |= cctl->cmd;
+	t |= (u64)cctl->fmid << DECT_CCTRL_FMID_SHIFT;
+	t |= (u64)cctl->pmid << DECT_CCTRL_PMID_SHIFT;
+	return t;
+}
+
+static int dect_parse_cctrl_attr(struct dect_cctrl *cctl, u64 t)
+{
+	cctl->ecn        = (t & DECT_CCTRL_ATTR_ECN_MASK) >> DECT_CCTRL_ATTR_ECN_SHIFT;
+	cctl->lbn        = (t & DECT_CCTRL_ATTR_LBN_MASK) >> DECT_CCTRL_ATTR_LBN_SHIFT;
+	cctl->type       = (t & DECT_CCTRL_ATTR_TYPE_MASK) >> DECT_CCTRL_ATTR_TYPE_SHIFT;
+	cctl->service    = (t & DECT_CCTRL_ATTR_SERVICE_MASK) >> DECT_CCTRL_ATTR_SERVICE_SHIFT;
+	cctl->slot       = (t & DECT_CCTRL_ATTR_SLOT_MASK) >> DECT_CCTRL_ATTR_SLOT_SHIFT;
+	cctl->cf         = (t & DECT_CCTRL_ATTR_CF_FLAG);
+	cctl->a_mod      = (t & DECT_CCTRL_ATTR_A_MOD_MASK) >> DECT_CCTRL_ATTR_A_MOD_SHIFT;
+	cctl->bz_mod     = (t & DECT_CCTRL_ATTR_BZ_MOD_MASK) >> DECT_CCTRL_ATTR_BZ_MOD_SHIFT;
+	cctl->bz_ext_mod = (t & DECT_CCTRL_ATTR_BZ_EXT_MOD_MASK) >> DECT_CCTRL_ATTR_BZ_EXT_MOD_SHIFT;
+	cctl->acr        = (t & DECT_CCTRL_ATTR_ACR_MASK) >> DECT_CCTRL_ATTR_ACR_SHIFT;
+
+	pr_debug("cctrl: cmd: %llx ecn: %x lbn: %x type: %x "
+		 "service: %x slot: %x cf %d a_mod %x bz_mod %x bz_ext_mod %x acr %x\n",
+		 (unsigned long long)cctl->cmd, cctl->ecn, cctl->lbn,
+		 cctl->type, cctl->service, cctl->slot, cctl->cf,
+		 cctl->a_mod, cctl->bz_mod, cctl->bz_ext_mod, cctl->acr);
+	return 0;
+}
+
+static u64 dect_build_cctrl_attr(const struct dect_cctrl *cctl)
+{
+	u64 t = 0;
+
+	t |= cctl->cmd;
+	t |= (u64)cctl->ecn << DECT_CCTRL_ATTR_ECN_SHIFT;
+	t |= (u64)cctl->lbn << DECT_CCTRL_ATTR_LBN_SHIFT;
+	t |= (u64)cctl->type << DECT_CCTRL_ATTR_TYPE_SHIFT;
+	t |= (u64)cctl->service << DECT_CCTRL_ATTR_SERVICE_SHIFT;
+	t |= (u64)cctl->slot << DECT_CCTRL_ATTR_SLOT_SHIFT;
+	t |= cctl->cf ? DECT_CCTRL_ATTR_CF_FLAG : 0;
+	t |= (u64)cctl->a_mod << DECT_CCTRL_ATTR_A_MOD_SHIFT;
+	t |= (u64)cctl->bz_mod << DECT_CCTRL_ATTR_BZ_MOD_SHIFT;
+	t |= (u64)cctl->bz_ext_mod << DECT_CCTRL_ATTR_BZ_EXT_MOD_SHIFT;
+	t |= (u64)cctl->acr << DECT_CCTRL_ATTR_ACR_SHIFT;
+	return t;
+}
+
+static int dect_parse_cctrl_release(struct dect_cctrl *cctl, u64 t)
+{
+	cctl->lbn    = (t & DECT_CCTRL_RELEASE_LBN_MASK) >>
+		       DECT_CCTRL_RELEASE_LBN_SHIFT;
+	cctl->reason = (t & DECT_CCTRL_RELEASE_REASON_MASK) >>
+		       DECT_CCTRL_RELEASE_REASON_SHIFT;
+	cctl->pmid   = (t & DECT_CCTRL_RELEASE_PMID_MASK) >>
+		       DECT_CCTRL_RELEASE_PMID_SHIFT;
+
+	pr_debug("cctrl: release: pmid: %.5x lbn: %x reason: %x\n",
+		 cctl->pmid, cctl->lbn, cctl->reason);
+	return 0;
+}
+
+static u64 dect_build_cctrl_release(const struct dect_cctrl *cctl)
+{
+	u64 t = 0;
+
+	t |= cctl->cmd;
+	t |= (u64)cctl->lbn << DECT_CCTRL_RELEASE_LBN_SHIFT;
+	t |= (u64)cctl->reason << DECT_CCTRL_RELEASE_REASON_SHIFT;
+	t |= (u64)cctl->pmid << DECT_CCTRL_RELEASE_PMID_SHIFT;
+	return t;
+}
+
+static int dect_parse_basic_cctrl(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_cctrl *cctl = &tm->cctl;
+
+	cctl->cmd = t & DECT_MT_CMD_MASK;
+	switch (cctl->cmd) {
+	case DECT_CCTRL_ACCESS_REQ:
+	case DECT_CCTRL_BEARER_HANDOVER_REQ:
+	case DECT_CCTRL_CONNECTION_HANDOVER_REQ:
+	case DECT_CCTRL_UNCONFIRMED_ACCESS_REQ:
+	case DECT_CCTRL_BEARER_CONFIRM:
+	case DECT_CCTRL_WAIT:
+		return dect_parse_cctrl_common(cctl, t);
+	case DECT_CCTRL_ATTRIBUTES_T_REQUEST:
+	case DECT_CCTRL_ATTRIBUTES_T_CONFIRM:
+		return dect_parse_cctrl_attr(cctl, t);
+	case DECT_CCTRL_RELEASE:
+		return dect_parse_cctrl_release(cctl, t);
+	default:
+		pr_debug("unknown cctrl command: %llx\n",
+			 (unsigned long long)cctl->cmd);
+		return -1;
+	}
+}
+
+static int dect_parse_advanced_cctrl(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_cctrl *cctl = &tm->cctl;
+
+	cctl->cmd = t & DECT_MT_CMD_MASK;
+	switch (cctl->cmd) {
+	case DECT_CCTRL_UNCONFIRMED_DUMMY:
+	case DECT_CCTRL_UNCONFIRMED_HANDOVER:
+		return dect_parse_cctrl_common(cctl, t);
+	case DECT_CCTRL_BANDWIDTH_T_REQUEST:
+	case DECT_CCTRL_BANDWIDTH_T_CONFIRM:
+		return -1;
+	default:
+		return dect_parse_basic_cctrl(tm, t);
+	}
+}
+
+static int dect_parse_encryption_ctrl(struct dect_tail_msg *tm, u64 t)
+{
+	struct dect_encctrl *ectl = &tm->encctl;
+
+	ectl->cmd  = (t & DECT_ENCCTRL_CMD_MASK) >> DECT_ENCCTRL_CMD_SHIFT;
+	ectl->fmid = (t & DECT_ENCCTRL_FMID_MASK) >> DECT_ENCCTRL_FMID_SHIFT;
+	ectl->pmid = (t & DECT_ENCCTRL_PMID_MASK) >> DECT_ENCCTRL_PMID_SHIFT;
+	pr_debug("encctrl: cmd: %x fmid: %.4x pmid: %.5x\n",
+		 ectl->cmd, ectl->fmid, ectl->pmid);
+	return 0;
+}
+
+static u64 dect_build_encryption_ctrl(const struct dect_encctrl *ectl)
+{
+	u64 t = 0;
+
+	t |= (u64)DECT_ENCCTRL_FILL_MASK;
+	t |= (u64)ectl->cmd << DECT_ENCCTRL_CMD_SHIFT;
+	t |= (u64)ectl->fmid << DECT_ENCCTRL_FMID_SHIFT;
+	t |= (u64)ectl->pmid << DECT_ENCCTRL_PMID_SHIFT;
+	return t;
+}
+
+static int dect_parse_mac_ctrl(struct dect_tail_msg *tm, u64 t)
+{
+	switch (t & DECT_MT_HDR_MASK) {
+	case DECT_MT_BASIC_CCTRL:
+		if (dect_parse_basic_cctrl(tm, t) < 0)
+			return -1;
+		tm->type = DECT_TM_TYPE_BCCTRL;
+		return 0;
+	case DECT_MT_ADV_CCTRL:
+		if (dect_parse_advanced_cctrl(tm, t) < 0)
+			return -1;
+		tm->type = DECT_TM_TYPE_ACCTRL;
+		return 0;
+	case DECT_MT_ENC_CTRL:
+		if (dect_parse_encryption_ctrl(tm, t) < 0)
+			return -1;
+		tm->type = DECT_TM_TYPE_ENCCTRL;
+		return 0;
+	default:
+		pr_debug("Unknown MAC control %llx\n",
+			 (unsigned long long)t & DECT_MT_HDR_MASK);
+		return -1;
+	}
+}
+
+static u64 dect_build_cctrl(const struct dect_cctrl *cctl)
+{
+	switch (cctl->cmd) {
+	case DECT_CCTRL_ACCESS_REQ:
+	case DECT_CCTRL_BEARER_HANDOVER_REQ:
+	case DECT_CCTRL_CONNECTION_HANDOVER_REQ:
+	case DECT_CCTRL_UNCONFIRMED_ACCESS_REQ:
+	case DECT_CCTRL_BEARER_CONFIRM:
+	case DECT_CCTRL_WAIT:
+	case DECT_CCTRL_UNCONFIRMED_DUMMY:
+	case DECT_CCTRL_UNCONFIRMED_HANDOVER:
+		return dect_build_cctrl_common(cctl);
+	case DECT_CCTRL_ATTRIBUTES_T_REQUEST:
+	case DECT_CCTRL_ATTRIBUTES_T_CONFIRM:
+		return dect_build_cctrl_attr(cctl);
+	case DECT_CCTRL_BANDWIDTH_T_REQUEST:
+	case DECT_CCTRL_BANDWIDTH_T_CONFIRM:
+	case DECT_CCTRL_CHANNEL_LIST:
+		return 0;
+	case DECT_CCTRL_RELEASE:
+		return dect_build_cctrl_release(cctl);
+	default:
+		return 0;
+	}
+}
+
+static int dect_parse_ct_data(struct dect_tail_msg *tm, u64 t, u8 seq)
+{
+	struct dect_ct_data *ctd = &tm->ctd;
+
+	ctd->seq = seq;
+	tm->type = DECT_TM_TYPE_CT;
+	pr_debug("C_S tail sequence number %u\n", seq);
+	return 0;
+}
+
+static int dect_parse_tail_msg(struct dect_tail_msg *tm,
+			       const struct sk_buff *skb)
+{
+	u64 t;
+
+	tm->type = DECT_TM_TYPE_INVALID;
+	WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, __alignof__(u64)));
+	t = be64_to_cpu(*(__be64 *)skb->data);
+
+	switch (dect_parse_tail(skb)) {
+	case DECT_TI_CT_PKT_0:
+		return dect_parse_ct_data(tm, t, 0);
+	case DECT_TI_CT_PKT_1:
+		return dect_parse_ct_data(tm, t, 1);
+	case DECT_TI_NT_CL:
+		pr_debug("connectionless: ");
+	case DECT_TI_NT:
+		return dect_parse_identities_information(tm, t);
+	case DECT_TI_QT:
+		return dect_parse_system_information(tm, t);
+	case DECT_TI_PT:
+		/* Paging tail in direction FP->PP, MAC control otherwise */
+		if (DECT_TRX_CB(skb)->slot < 12)
+			return dect_parse_paging_msg(tm, t);
+	case DECT_TI_MT:
+		return dect_parse_mac_ctrl(tm, t);
+	default:
+		pr_debug("unknown tail %x\n", dect_parse_tail(skb));
+		return -1;
+	}
+}
+
+static struct sk_buff *dect_build_tail_msg(struct sk_buff *skb,
+					   enum dect_tail_msg_types type,
+					   const void *data)
+{
+	enum dect_tail_identifications ti;
+	unsigned int i;
+	u64 t;
+
+	switch (type) {
+	case DECT_TM_TYPE_ID:
+		t = dect_build_identities_information(data);
+		ti = DECT_TI_NT;
+		break;
+	case DECT_TM_TYPE_SSI:
+		t = dect_build_static_system_information(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_ERFC:
+		t = dect_build_extended_rf_carrier_information(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_FPC:
+		t = dect_build_fixed_part_capabilities(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_EFPC:
+		t = dect_build_extended_fixed_part_capabilities(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_EFPC2:
+		t = dect_build_extended_fixed_part_capabilities2(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_SARI:
+		t = dect_build_sari(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_MFN:
+		t = dect_build_multiframe_number(data);
+		ti = DECT_TI_QT;
+		break;
+	case DECT_TM_TYPE_BCCTRL:
+		t = dect_build_cctrl(data) | DECT_MT_BASIC_CCTRL;
+		ti = DECT_TI_MT;
+		break;
+	case DECT_TM_TYPE_ACCTRL:
+		t = dect_build_cctrl(data) | DECT_MT_ADV_CCTRL;
+		ti = DECT_TI_MT;
+		break;
+	case DECT_TM_TYPE_ENCCTRL:
+		t = dect_build_encryption_ctrl(data);
+		ti = DECT_TI_MT;
+		break;
+	default:
+		BUG();
+	}
+
+	skb_put(skb, DECT_T_FIELD_SIZE);
+	for (i = 0; i < DECT_T_FIELD_SIZE; i++)
+		skb->data[i] = t >> ((sizeof(t) - i - 2) * BITS_PER_BYTE);
+
+	DECT_A_CB(skb)->id = ti;
+	return skb;
+}
+
+/**
+ * dect_t_skb_alloc - allocate a socket buffer for the T-Field
+ *
+ */
+static struct sk_buff *dect_t_skb_alloc(void)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(DECT_PREAMBLE_SIZE + DECT_A_FIELD_SIZE, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+
+	/* Reserve space for preamble */
+	skb_reset_mac_header(skb);
+	skb_reserve(skb, DECT_PREAMBLE_SIZE);
+
+	skb_reset_network_header(skb);
+
+	/* Reserve space for Header Field */
+	skb_reserve(skb, DECT_HDR_FIELD_SIZE);
+	return skb;
+}
+
+/*
+ * MAC Bearers
+ */
+
+static void dect_bearer_enable(struct dect_bearer *bearer)
+{
+	switch (bearer->mode) {
+	case DECT_BEARER_RX:
+		dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_RX);
+		break;
+	case DECT_BEARER_TX:
+		dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_TX);
+		break;
+	};
+	dect_set_carrier(bearer->trx, bearer->chd.slot, bearer->chd.carrier);
+	bearer->state = DECT_BEARER_ENABLED;
+}
+
+static void dect_bearer_disable(struct dect_bearer *bearer)
+{
+	dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_IDLE);
+	bearer->trx->slots[bearer->chd.slot].bearer = NULL;
+}
+
+static void dect_bearer_timer_add(struct dect_cell *cell,
+				  struct dect_bearer *bearer,
+				  struct dect_timer *timer,
+				  unsigned int frames)
+{
+	u8 slot = bearer->chd.slot;
+
+	switch (bearer->mode) {
+	case DECT_BEARER_RX:
+		dect_timer_add(cell, timer, DECT_TIMER_RX, frames, slot);
+		break;
+	case DECT_BEARER_TX:
+		dect_timer_add(cell, timer, DECT_TIMER_TX, frames, slot);
+		break;
+	}
+}
+
+/**
+ * dect_bearer_release - release a MAC bearer
+ *
+ * Release a MAC bearer that is no longer used. The unused slot position is
+ * given to IRC and converted to a scan bearer.
+ */
+static void dect_scan_bearer_enable(struct dect_transceiver *trx,
+				    const struct dect_channel_desc *chd);
+
+static void dect_bearer_release(struct dect_cell *cell,
+				struct dect_bearer *bearer)
+{
+	struct dect_transceiver *trx = bearer->trx;
+	u8 slot = bearer->chd.slot;
+
+	__skb_queue_purge(&bearer->m_tx_queue);
+	dect_timer_del(&bearer->tx_timer);
+	dect_bearer_disable(bearer);
+	dect_disable_cipher(trx, bearer->chd.slot);
+
+	if (trx->index < 3 &&
+	    ((slot >= dect_normal_receive_base(cell->mode) &&
+	      slot <= dect_normal_receive_end(cell->mode)) ||
+	     (cell->flags & DECT_CELL_MONITOR)))
+		dect_scan_bearer_enable(trx, &bearer->chd);
+}
+
+static void dect_bearer_init(struct dect_cell *cell, struct dect_bearer *bearer,
+			     const struct dect_bearer_ops *ops,
+			     struct dect_transceiver *trx,
+			     const struct dect_channel_desc *chd,
+			     enum dect_bearer_modes mode, void *data)
+{
+	pr_debug("init bearer: mode: %s slot: %u carrier: %u\n",
+		 mode == DECT_BEARER_RX ? "RX" : "TX" , chd->slot, chd->carrier);
+
+	bearer->ops   = ops;
+	bearer->trx   = trx;
+	bearer->chd   = *chd;
+	bearer->mode  = mode;
+	bearer->state = DECT_BEARER_INACTIVE;
+	dect_timer_setup(&bearer->tx_timer, NULL, NULL);
+	skb_queue_head_init(&bearer->m_tx_queue);
+	bearer->data  = data;
+
+	trx->slots[chd->slot].bearer = bearer;
+	dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_IDLE);
+}
+
+/*
+ * TX bearer activation:
+ *
+ * The first transmission of an outgoing traffic or connectionless bearer is
+ * scheduled for the frame in which the remote sides' scan is on the desired
+ * carrier.
+ *
+ * The noise value of the physical channel must be confirmed not to be more
+ * than 12dBm stronger than the RSSI measurement obtained from the channel
+ * list when selecting the channel within two frames before the first
+ * transmission.
+ *
+ * Dummy bearers are activated immediately after confirming the RSSI.
+ */
+
+static void dect_tx_bearer_report_rssi(struct dect_cell *cell,
+				       struct dect_bearer *bearer,
+				       u8 rssi)
+{
+	rx_debug(cell, "RSSI confirm: last: %u new: %u\n", bearer->rssi, rssi);
+	if (rssi > bearer->rssi + dect_dbm_to_rssi_rel(12))
+		pr_debug("RSSI: too much noise\n");
+	bearer->state = DECT_BEARER_RSSI_CONFIRMED;
+}
+
+static void dect_tx_bearer_enable_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_bearer *bearer = data;
+
+	switch ((int)bearer->state) {
+	case DECT_BEARER_SCHEDULED:
+		tx_debug(cell, "confirm RSSI carrier %u\n", bearer->chd.carrier);
+		dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_RX);
+		dect_set_carrier(bearer->trx, bearer->chd.slot, bearer->chd.carrier);
+		dect_bearer_timer_add(cell, bearer, &bearer->tx_timer, 2);
+		bearer->state = DECT_BEARER_RSSI_CONFIRM;
+		break;
+	case DECT_BEARER_RSSI_CONFIRMED:
+		tx_debug(cell, "enable bearer\n");
+		if (bearer->ops->enable != NULL)
+			bearer->ops->enable(cell, bearer);
+		else
+			dect_bearer_enable(bearer);
+		break;
+	}
+}
+
+static void dect_tx_bearer_schedule(struct dect_cell *cell,
+				    struct dect_bearer *bearer, u8 rssi)
+{
+	u8 delay = 0;
+
+	dect_timer_setup(&bearer->tx_timer, dect_tx_bearer_enable_timer, bearer);
+	if (bearer->ops->state != DECT_DUMMY_BEARER)
+		delay = dect_channel_delay(cell, &bearer->chd) - 2;
+
+	bearer->state = DECT_BEARER_SCHEDULED;
+	bearer->rssi  = rssi;
+
+	if (delay == 0)
+		dect_tx_bearer_enable_timer(cell, bearer);
+	else {
+		dect_bearer_timer_add(cell, bearer, &bearer->tx_timer, delay);
+		tx_debug(cell, "scheduled bearer: delay %u carrier %u pscn %u\n",
+			 delay, bearer->chd.carrier, cell->si.ssi.pscn);
+	}
+}
+
+/*
+ * Broadcast Message Control - decentralized components
+ */
+
+/* Paging:
+ *
+ * The following rules apply to page message transmission:
+ *
+ * - Fast pages may be transmitted in any frame and have priority over normal
+ *   pages.
+ *
+ * - Normal short and full pages, as well as the first segment of a normal long
+ *   page, may only be transmitted in frame 0, or a frame up to 12 if the page
+ *   transmitted in the previously allowed frame had the extend bit bit set.
+ *
+ * - Normal pages must be repeated three times in the frames following their
+ *   first transmission for page detection in low duty idle mode.
+ *
+ * - Fast pages may be repeated up to three times following their first
+ *   transmission. New page message have priority over repetitions.
+ *
+ * FIXME: fast pages should not interrupt repetitions
+ */
+
+static void dect_page_timer_schedule(struct dect_cell *cell)
+{
+	u8 framenum = dect_framenum(cell, DECT_TIMER_TX);
+	u8 frames;
+
+	if ((framenum & 0x1) == 1)
+		frames = 1;
+	 else
+		frames = 2;
+	framenum = dect_framenum_add(framenum, frames);
+
+	if (framenum == 8 || framenum == 14)
+		frames += 2;
+
+	tx_debug(cell, "page timer: schedule in %u frames\n", frames);
+	dect_timer_add(cell, &cell->page_timer, DECT_TIMER_TX, frames, 0);
+}
+
+/**
+ * dect_queue_page - Add a paging message to the appropriate queue
+ *
+ * The first transmission of a page is added to the end of the normal or
+ * fast queue. The first three repetitions of normal pages have priority
+ * over first transmissions.
+ */
+static void dect_queue_page(struct dect_cell *cell, struct sk_buff *skb)
+{
+	u8 repetitions = DECT_BMC_CB(skb)->repetitions;
+	bool fast = DECT_BMC_CB(skb)->fast_page;
+	struct sk_buff_head *page_queue;
+
+	page_queue = fast ? &cell->page_fast_queue : &cell->page_queue;
+	if (!fast && repetitions > 0)
+		skb_queue_head(page_queue, skb);
+	else
+		skb_queue_tail(page_queue, skb);
+
+	dect_page_timer_schedule(cell);
+}
+
+/**
+ * dect_queue_page_segments - perform segmentation and queue the page segments
+ *
+ * Segment a page message into B_S channel sized segments and add them
+ * to the TX queue.
+ */
+static void dect_queue_page_segments(struct sk_buff_head *list,
+				     struct sk_buff *skb)
+{
+	unsigned int len = skb->len;
+	struct sk_buff *seg;
+	u64 t;
+
+	while (skb->len > DECT_PT_LFP_BS_DATA_SIZE) {
+		seg = skb_clone(skb, GFP_ATOMIC);
+		if (seg == NULL)
+			goto err;
+		skb_trim(seg, DECT_PT_LFP_BS_DATA_SIZE);
+
+		if (skb_queue_empty(list))
+			t = DECT_PT_LONG_PAGE_FIRST;
+		else
+			t = DECT_PT_LONG_PAGE;
+
+		seg->data[0] &= 0x0f;
+		seg->data[0] |= t >> 48;
+		pr_debug("queue page segment len %u hdr %x\n",
+			 seg->len, seg->data[0] & 0xf0);
+		__skb_queue_tail(list, seg);
+
+		skb_pull(skb, DECT_PT_LFP_BS_DATA_SIZE);
+	}
+
+	/* Short and full pages have the extend bit set in order to reduce
+	 * the delay for new pages arriving while a page is already active.
+	 */
+	if (skb->len == DECT_PT_SP_BS_DATA_SIZE)
+		t = DECT_PT_SHORT_PAGE | DECT_PT_HDR_EXTEND_FLAG;
+	else if (!DECT_BMC_CB(skb)->long_page)
+		t = DECT_PT_FULL_PAGE | DECT_PT_HDR_EXTEND_FLAG;
+	else if (len == DECT_PT_LFP_BS_DATA_SIZE)
+		t = DECT_PT_LONG_PAGE_ALL;
+	else
+		t = DECT_PT_LONG_PAGE_LAST;
+
+	skb->data[0] &= 0x0f;
+	skb->data[0] |= t >> 48;
+	pr_debug("queue page segment len %u hdr %x\n",
+		 skb->len, skb->data[0] & 0xf0);
+	__skb_queue_tail(list, skb);
+	return;
+
+err:
+	__skb_queue_purge(list);
+	kfree_skb(skb);
+}
+
+/**
+ * dect_page_timer - page message transmission timer
+ *
+ * This timer performs maintenance of the page transmit queue. While the queue
+ * is active, it is advanced by one segment per frame. When a page message has
+ * been fully transmitted, the next message is selected for transmission,
+ * segmented into appropriate units and queued to the transmit queue.
+ */
+static void dect_page_tx_timer(struct dect_cell *cell, void *data)
+{
+	u32 timeout, mfn = dect_mfn(cell, DECT_TIMER_TX);
+	u8 framenum = dect_framenum(cell, DECT_TIMER_TX);
+	struct sk_buff *skb, *last;
+
+	tx_debug(cell, "page timer\n");
+
+	/* Advance the transmit queue by one segment per allowed tail. */
+	if (!skb_queue_empty(&cell->page_tx_queue)) {
+		tx_debug(cell, "advance queue\n");
+		kfree_skb(__skb_dequeue(&cell->page_tx_queue));
+		if (!skb_queue_empty(&cell->page_tx_queue)) {
+			dect_page_timer_schedule(cell);
+			return;
+		}
+	}
+
+	/* Add the last page back to the queue unless its lifetime expired. */
+	last = cell->page_sdu;
+	if (last != NULL) {
+		cell->page_sdu = NULL;
+
+		DECT_BMC_CB(last)->repetitions++;
+		timeout = dect_mfn_add(DECT_BMC_CB(last)->stamp, DECT_PAGE_LIFETIME);
+		if (dect_mfn_before(mfn, timeout))
+			dect_queue_page(cell, last);
+		else
+			kfree_skb(last);
+	}
+
+	/* Get the next page message */
+	while (1) {
+		skb = skb_dequeue(&cell->page_fast_queue);
+		tx_debug(cell, "fast page: %p\n", skb);
+		if (skb == NULL && !skb_queue_empty(&cell->page_queue)) {
+			if (framenum == 0 || (last != NULL && framenum <= 12))
+				skb = skb_dequeue(&cell->page_queue);
+			tx_debug(cell, "normal page: %p\n", skb);
+		}
+		if (skb == NULL)
+			goto out;
+
+		timeout = dect_mfn_add(DECT_BMC_CB(skb)->stamp, DECT_PAGE_LIFETIME);
+		if (dect_mfn_before(mfn, timeout))
+			break;
+		else
+			kfree_skb(skb);
+	}
+
+	/* Save a copy of short and full pages for repetitions. */
+	if (!DECT_BMC_CB(skb)->long_page &&
+	    DECT_BMC_CB(skb)->repetitions < 3)
+		cell->page_sdu = skb_clone(skb, GFP_ATOMIC);
+
+	/* Segment page message and queue segments to tx queue */
+	dect_queue_page_segments(&cell->page_tx_queue, skb);
+out:
+	if (skb != NULL || !skb_queue_empty(&cell->page_queue))
+		dect_page_timer_schedule(cell);
+}
+
+static void dect_cell_schedule_page(struct dect_cell *cell, u32 mask)
+{
+	struct dect_bc *bc;
+
+	list_for_each_entry(bc, &cell->bcs, list)
+		bc->p_tx_mask |= mask;
+}
+
+static void dect_cell_bmc_init(struct dect_cell *cell)
+{
+	skb_queue_head_init(&cell->page_queue);
+	skb_queue_head_init(&cell->page_fast_queue);
+	skb_queue_head_init(&cell->page_tx_queue);
+	dect_timer_setup(&cell->page_timer, dect_page_tx_timer, NULL);
+}
+
+static void dect_cell_bmc_disable(struct dect_cell *cell)
+{
+	dect_timer_del(&cell->page_timer);
+	__skb_queue_purge(&cell->page_tx_queue);
+	__skb_queue_purge(&cell->page_fast_queue);
+	__skb_queue_purge(&cell->page_queue);
+}
+
+/*
+ * Broadcast Control
+ */
+
+static void dect_cell_mac_info_ind(struct dect_cell *cell)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+
+	clh->ops->mac_info_ind(clh, &cell->idi, &cell->si);
+}
+
+static u32 dect_build_page_rfpi(const struct dect_cell *cell)
+{
+	return (dect_build_rfpi(&cell->idi) >> 24) & ((1 << 20) - 1);
+}
+
+static void dect_bc_release(struct dect_bc *bc)
+{
+	kfree_skb(bc->p_rx_skb);
+	list_del(&bc->list);
+}
+
+static void dect_bc_init(struct dect_cell *cell, struct dect_bc *bc)
+{
+	INIT_LIST_HEAD(&bc->list);
+	bc->p_rx_skb = NULL;
+	list_add_tail(&bc->list, &cell->bcs);
+}
+
+static const enum dect_mac_system_information_types dect_bc_q_cycle[] = {
+	DECT_QT_SI_SSI,
+	DECT_QT_SI_ERFC,
+	DECT_QT_SI_SARI,
+	DECT_QT_SI_FPC,
+	DECT_QT_SI_EFPC,
+	DECT_QT_SI_EFPC2,
+	DECT_QT_SI_MFN,
+};
+
+static struct sk_buff *dect_bc_q_dequeue(struct dect_cell *cell,
+					 struct dect_bearer *bearer)
+{
+	const struct dect_si *si = &cell->si;
+	struct dect_ssi ssi;
+	struct dect_mfn mfn;
+	struct sk_buff *skb;
+	unsigned int index;
+
+	skb = dect_t_skb_alloc();
+	if (skb == NULL)
+		return NULL;
+
+	while (1) {
+		index = cell->si_idx++;
+		if (cell->si_idx == ARRAY_SIZE(dect_bc_q_cycle))
+			cell->si_idx = 0;
+
+		switch (dect_bc_q_cycle[index]) {
+		case DECT_QT_SI_SSI:
+			memcpy(&ssi, &si->ssi, sizeof(ssi));
+			ssi.sn = bearer->chd.slot;
+			ssi.cn = bearer->chd.carrier;
+			ssi.sp = 0;
+			ssi.pscn = dect_next_carrier(ssi.rfcars, ssi.pscn);
+
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_SSI, &ssi);
+		case DECT_QT_SI_ERFC:
+			if (!si->ssi.mc)
+				continue;
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_ERFC,
+						   &si->erfc);
+		case DECT_QT_SI_SARI:
+			break;
+		case DECT_QT_SI_FPC:
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_FPC,
+						   &si->fpc);
+		case DECT_QT_SI_EFPC:
+			if (!(si->fpc.fpc & DECT_FPC_EXTENDED_FP_INFO))
+				continue;
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_EFPC,
+						   &si->efpc);
+		case DECT_QT_SI_EFPC2:
+			if (!(si->efpc.fpc & DECT_EFPC_EXTENDED_FP_INFO2))
+				continue;
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_EFPC2,
+						   &si->efpc2);
+		case DECT_QT_SI_MFN:
+			mfn.num = dect_mfn(cell, DECT_TIMER_TX);
+			return dect_build_tail_msg(skb, DECT_TM_TYPE_MFN, &mfn);
+		default:
+			BUG();
+		}
+	}
+}
+
+static void dect_page_add_mac_info(struct dect_cell *cell, struct dect_bc *bc,
+				   struct sk_buff *skb)
+{
+	struct dect_tail_msg tm;
+	struct dect_dbc *dbc;
+	u64 t;
+	u8 *it;
+
+	memset(&tm, 0, sizeof(tm));
+	if (bc->p_tx_mask & (1 << DECT_TM_TYPE_BFS))
+		tm.type = DECT_TM_TYPE_BFS;
+	else if (bc->p_tx_mask & (1 << DECT_TM_TYPE_BD))
+		tm.type = DECT_TM_TYPE_BD;
+	else
+		tm.type = DECT_TM_TYPE_ACTIVE_CARRIERS;
+
+	switch (tm.type) {
+	case DECT_TM_TYPE_BFS:
+		tm.bfs.mask = cell->trg_blind_full_slots;
+		t = dect_build_blind_full_slots(&tm.bfs);
+		break;
+	case DECT_TM_TYPE_BD:
+		dbc = dect_dbc_get(cell);
+		if (dbc == NULL)
+			goto out;
+		tm.bd.bt = DECT_PT_IT_DUMMY_OR_CL_BEARER_POSITION;
+		tm.bd.sn = dbc->bearer.chd.slot;
+		tm.bd.sp = 0;
+		tm.bd.cn = dbc->bearer.chd.carrier;
+		t = dect_build_bearer_description(&tm.bd);
+		break;
+	case DECT_TM_TYPE_RFP_ID:
+		t = dect_build_rfp_identity(&tm.rfp_id);
+		break;
+	case DECT_TM_TYPE_RFP_STATUS:
+		t = dect_build_rfp_status(&tm.rfp_status);
+		break;
+	case DECT_TM_TYPE_ACTIVE_CARRIERS:
+	default:
+		t = dect_build_active_carriers(&tm.active_carriers);
+		break;
+	}
+
+	it = skb_put(skb, DECT_PT_INFO_TYPE_SIZE);
+	it[0] = t >> 24;
+	it[1] = t >> 16;
+out:
+	bc->p_tx_mask &= ~(1 << tm.type);
+}
+
+static struct sk_buff *dect_bc_p_dequeue(struct dect_cell *cell,
+					 struct dect_bearer *bearer,
+					 struct dect_bc *bc)
+{
+	unsigned int headroom, tailroom = 0;
+	struct sk_buff *skb;
+	u8 *hdr;
+	u64 t;
+
+	/* Send higher layer page messages if present */
+	skb = skb_peek(&cell->page_tx_queue);
+	if (skb != NULL) {
+		/* The frame needs headroom for the preamble and hdr-field.
+		 * Short pages need additional tailroom for the MAC Layer
+		 * Information. */
+		headroom = DECT_PREAMBLE_SIZE + DECT_HDR_FIELD_SIZE;
+		if (skb->len == DECT_PT_SP_BS_DATA_SIZE)
+			tailroom = DECT_PT_INFO_TYPE_SIZE;
+
+		skb = skb_copy_expand(skb, headroom, tailroom, GFP_ATOMIC);
+		if (skb == NULL)
+			return NULL;
+		/* Reserve space for preamble */
+		skb_set_mac_header(skb, -headroom);
+	} else {
+		/* Send zero-length page if required */
+		if (dect_framenum(cell, DECT_TIMER_TX) == 0 ||
+		    bc->p_tx_mask == 0)
+			return NULL;
+
+		skb = dect_t_skb_alloc();
+		if (skb == NULL)
+			return NULL;
+
+		t  = DECT_PT_ZERO_PAGE | DECT_PT_HDR_EXTEND_FLAG;
+		t |= (u64)dect_build_page_rfpi(cell) << DECT_PT_ZP_RFPI_SHIFT;
+
+		hdr = skb_put(skb, 3);
+		hdr[0] = t >> 48;
+		hdr[1] = t >> 40;
+		hdr[2] = t >> 32;
+
+		tailroom = DECT_PT_INFO_TYPE_SIZE;
+	}
+
+	DECT_A_CB(skb)->id = DECT_TI_PT;
+	if (tailroom > 0)
+		dect_page_add_mac_info(cell, bc, skb);
+
+	return skb;
+}
+
+static struct sk_buff *dect_bc_dequeue(struct dect_cell *cell,
+				       struct dect_bearer *bearer,
+				       struct dect_bc *bc,
+				       enum dect_mac_channels chan)
+{
+	struct sk_buff *skb;
+
+	switch (chan) {
+	case DECT_MC_P:
+		return dect_bc_p_dequeue(cell, bearer, bc);
+	case DECT_MC_Q:
+		return dect_bc_q_dequeue(cell, bearer);
+	case DECT_MC_N:
+		skb = dect_t_skb_alloc();
+		if (skb == NULL)
+			return NULL;
+		return dect_build_tail_msg(skb, DECT_TM_TYPE_ID, &cell->idi);
+	default:
+		BUG();
+	}
+}
+
+/**
+ * dect_bc_queue_bs_data - queue a page message to the broadcast controller for
+ * 			   reassembly and delivery to broadcast message control.
+ *
+ * @cell:	DECT cell
+ * @bc:		broadcast controller
+ * @skb_in:	DECT frame
+ * @page:	page message
+ */
+static void dect_bc_queue_bs_data(struct dect_cell *cell, struct dect_bc *bc,
+				  struct sk_buff *skb_in, const struct dect_page *page)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+	struct sk_buff *skb, *head;
+
+	if (page->length == DECT_PT_ZERO_PAGE)
+		return;
+
+	skb = skb_clone(skb_in, GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+	skb_pull(skb, DECT_T_FIELD_OFF);
+	DECT_BMC_CB(skb)->long_page = false;
+
+	head = bc->p_rx_skb;
+	switch (page->length) {
+	case DECT_PT_SHORT_PAGE:
+		skb_trim(skb, DECT_PT_SP_BS_DATA_SIZE);
+		break;
+	case DECT_PT_LONG_PAGE_ALL:
+		DECT_BMC_CB(skb)->long_page = true;
+		/* fall through */
+	case DECT_PT_FULL_PAGE:
+		skb_trim(skb, DECT_PT_LFP_BS_DATA_SIZE);
+		break;
+	case DECT_PT_LONG_PAGE_FIRST:
+		if (head != NULL)
+			goto err_free;
+		DECT_BMC_CB(skb)->long_page = true;
+		skb_trim(skb, DECT_PT_LFP_BS_DATA_SIZE);
+		bc->p_rx_skb = skb;
+		return;
+	case DECT_PT_LONG_PAGE:
+		if (head == NULL)
+			goto err_free;
+		skb_trim(skb, DECT_PT_LFP_BS_DATA_SIZE);
+		skb_append_frag(head, skb);
+		if (head->len >= 30)
+			goto err;
+		return;
+	case DECT_PT_LONG_PAGE_LAST:
+		if (head == NULL)
+			goto err_free;
+		skb_trim(skb, DECT_PT_LFP_BS_DATA_SIZE);
+		skb = skb_append_frag(head, skb);
+		bc->p_rx_skb = NULL;
+		break;
+	default:
+		BUG();
+	}
+
+	return clh->ops->bmc_page_ind(clh, skb);
+
+err_free:
+	kfree_skb(skb);
+err:
+	kfree_skb(bc->p_rx_skb);
+	bc->p_rx_skb = NULL;
+}
+
+static bool dect_bc_update_si(struct dect_si *si,
+			      const struct dect_tail_msg *tm)
+{
+	bool notify = false;
+	unsigned int i;
+
+	switch (tm->type) {
+	case DECT_TM_TYPE_SSI:
+		if (memcmp(&si->ssi, &tm->ssi, sizeof(si->ssi)))
+			memcpy(&si->ssi, &tm->ssi, sizeof(si->ssi));
+		break;
+	case DECT_TM_TYPE_ERFC:
+		if (memcmp(&si->erfc, &tm->erfc, sizeof(si->erfc)))
+			memcpy(&si->erfc, &tm->erfc, sizeof(si->erfc));
+		break;
+	case DECT_TM_TYPE_FPC:
+		if (memcmp(&si->fpc, &tm->fpc, sizeof(si->fpc))) {
+			memcpy(&si->fpc, &tm->fpc, sizeof(si->fpc));
+			notify = true;
+		}
+		break;
+	case DECT_TM_TYPE_EFPC:
+		if (memcmp(&si->efpc, &tm->efpc, sizeof(si->efpc))) {
+			memcpy(&si->efpc, &tm->efpc, sizeof(si->efpc));
+			notify = true;
+		}
+		break;
+	case DECT_TM_TYPE_EFPC2:
+		if (memcmp(&si->efpc2, &tm->efpc2, sizeof(si->efpc2))) {
+			memcpy(&si->efpc2, &tm->efpc2, sizeof(si->efpc2));
+			notify = true;
+		}
+		break;
+	case DECT_TM_TYPE_SARI:
+		if (si->num_saris == ARRAY_SIZE(si->sari))
+			break;
+
+		for (i = 0; i < si->num_saris; i++) {
+			if (!dect_ari_cmp(&tm->sari.ari, &si->sari[i].ari))
+				break;
+		}
+		if (i < si->num_saris)
+			break;
+
+		memcpy(&si->sari[si->num_saris++], &tm->sari,
+		       sizeof(si->sari[i]));
+		notify = true;
+		break;
+	case DECT_TM_TYPE_MFN:
+		memcpy(&si->mfn, &tm->mfn, sizeof(si->mfn));
+		break;
+	default:
+		return false;
+	}
+
+	si->mask |= 1 << tm->type;
+	return notify;
+}
+
+static bool dect_bc_si_cycle_complete(struct dect_idi *idi,
+				      const struct dect_si *si)
+{
+	if (!(si->mask & (1 << DECT_TM_TYPE_SSI))) {
+		pr_debug("incomplete: SSI\n");
+		return false;
+	}
+	if (si->ssi.mc &&
+	    !(si->mask & (1 << DECT_TM_TYPE_ERFC))) {
+		pr_debug("incomplete: ERFC\n");
+		return false;
+	}
+
+	if (!(si->mask & (1 << DECT_TM_TYPE_FPC))) {
+		pr_debug("incomplete: FPC\n");
+		return false;
+	}
+	if (si->fpc.fpc & DECT_FPC_EXTENDED_FP_INFO &&
+	    !(si->mask & (1 << DECT_TM_TYPE_EFPC))) {
+		pr_debug("incomplete: EFPC\n");
+		return false;
+	}
+
+	if (si->mask & (1 << DECT_TM_TYPE_EFPC) &&
+	    si->efpc.fpc & DECT_EFPC_EXTENDED_FP_INFO2 &&
+	    !(si->mask & (1 << DECT_TM_TYPE_EFPC2))) {
+		pr_debug("incomplete: EFPC2\n");
+		return false;
+	}
+
+	if (idi->e &&
+	    (!(si->mask & (1 << DECT_TM_TYPE_SARI)) ||
+	     si->num_saris != si->sari[0].list_cycle)) {
+		pr_debug("incomplete: SARI\n");
+		return false;
+	}
+
+	pr_debug("complete\n");
+	return true;
+}
+
+static void dect_bc_rcv(struct dect_cell *cell, struct dect_bc *bc,
+			struct sk_buff *skb, const struct dect_tail_msg *tm)
+{
+	enum dect_tail_identifications ti;
+	bool notify;
+
+	if (cell->mode != DECT_MODE_PP)
+		return;
+
+	ti = dect_parse_tail(skb);
+	if (ti == DECT_TI_QT) {
+		/* Q-channel information is broadcast in frame 8 */
+		dect_timer_synchronize_framenum(cell, DECT_Q_CHANNEL_FRAME);
+		if (tm->type == DECT_TM_TYPE_MFN)
+			dect_timer_synchronize_mfn(cell, tm->mfn.num);
+
+		notify = dect_bc_update_si(&cell->si, tm);
+		if (dect_bc_si_cycle_complete(&cell->idi, &cell->si) && notify)
+			dect_cell_mac_info_ind(cell);
+	} else if (ti == DECT_TI_PT) {
+		if (tm->page.length == DECT_PT_ZERO_PAGE &&
+		    tm->page.rfpi != dect_build_page_rfpi(cell))
+			pr_debug("RFPI mismatch %.3x %.3x\n",
+				 tm->page.rfpi, dect_build_page_rfpi(cell));
+	}
+
+	switch (tm->type) {
+	case DECT_TM_TYPE_BFS:
+		cell->blind_full_slots = tm->bfs.mask;
+	case DECT_TM_TYPE_BD:
+	case DECT_TM_TYPE_RFP_ID:
+	case DECT_TM_TYPE_RFP_STATUS:
+	case DECT_TM_TYPE_ACTIVE_CARRIERS:
+	case DECT_TM_TYPE_PAGE:
+		dect_bc_queue_bs_data(cell, bc, skb, &tm->page);
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * Traffic Bearer Control (TBC)
+ */
+
+#define tbc_debug(tbc, fmt, args...) \
+	pr_debug("TBC (TBEI %u/%s): PMID: %s %x FMID: %.3x: " fmt, \
+		 (tbc)->id.tbei, tbc_states[(tbc)->state], \
+		 (tbc)->id.pmid.type == DECT_PMID_DEFAULT ? "default" : \
+		 (tbc)->id.pmid.type == DECT_PMID_ASSIGNED ? "assigned" : \
+		 (tbc)->id.pmid.type == DECT_PMID_EMERGENCY ? "emergency" : "?", \
+		 (tbc)->id.pmid.tpui, (tbc)->cell->fmid, ## args);
+
+static const char *tbc_states[] = {
+	[DECT_TBC_NONE]			= "NONE",
+	[DECT_TBC_REQ_SENT]		= "REQ_SENT",
+	[DECT_TBC_WAIT_RCVD]		= "WAIT_RCVD",
+	[DECT_TBC_REQ_RCVD]		= "REQ_RCVD",
+	[DECT_TBC_RESPONSE_SENT]	= "RESPONSE_SENT",
+	[DECT_TBC_OTHER_WAIT]		= "OTHER_WAIT",
+	[DECT_TBC_ESTABLISHED]		= "ESTABLISHED",
+	[DECT_TBC_RELEASING]		= "RELEASING",
+	[DECT_TBC_RELEASED]		= "RELEASED",
+};
+
+static struct dect_tbc *dect_tbc_get_by_tbei(const struct dect_cell *cell, u32 tbei)
+{
+	struct dect_tbc *tbc;
+
+	list_for_each_entry(tbc, &cell->tbcs, list) {
+		if (tbc->id.tbei == tbei)
+			return tbc;
+	}
+	return NULL;
+}
+
+static u32 dect_tbc_alloc_tbei(struct dect_cell *cell)
+{
+	u32 tbei;
+
+	while (1) {
+		tbei = ++cell->tbei_rover;
+		if (tbei == 0)
+			continue;
+		if (dect_tbc_get_by_tbei(cell, tbei))
+			continue;
+		return tbei;
+	}
+}
+
+static void dect_tbc_queue_mac_control(struct dect_tbc *tbc, struct sk_buff *skb)
+{
+	skb_queue_tail(&tbc->txb.m_tx_queue, skb);
+}
+
+static struct sk_buff *dect_tbc_build_cctrl(const struct dect_tbc *tbc,
+					    enum dect_cctrl_cmds cmd)
+{
+	struct dect_cctrl cctl;
+	struct sk_buff *skb;
+
+	skb = dect_t_skb_alloc();
+	if (skb == NULL)
+		return NULL;
+
+	cctl.fmid = tbc->cell->fmid;
+	cctl.pmid = dect_build_pmid(&tbc->id.pmid);
+	cctl.cmd  = cmd;
+
+	if (tbc->type == DECT_MAC_CONN_BASIC)
+		return dect_build_tail_msg(skb, DECT_TM_TYPE_BCCTRL, &cctl);
+	else
+		return dect_build_tail_msg(skb, DECT_TM_TYPE_ACCTRL, &cctl);
+}
+
+static struct sk_buff *dect_tbc_build_encctrl(const struct dect_tbc *tbc,
+					      enum dect_encctrl_cmds cmd)
+{
+	struct dect_encctrl ectl;
+	struct sk_buff *skb;
+
+	skb = dect_t_skb_alloc();
+	if (skb == NULL)
+		return NULL;
+
+	ectl.fmid = tbc->cell->fmid;
+	ectl.pmid = dect_build_pmid(&tbc->id.pmid);
+	ectl.cmd  = cmd;
+
+	return dect_build_tail_msg(skb, DECT_TM_TYPE_ENCCTRL, &ectl);
+}
+
+static int dect_tbc_send_confirm(struct dect_tbc *tbc)
+{
+	struct sk_buff *skb;
+
+	tbc_debug(tbc, "TX CONFIRM\n");
+	skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_BEARER_CONFIRM);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	/* The first response is permitted in any frame */
+	if (tbc->state == DECT_TBC_REQ_RCVD)
+		skb->priority = DECT_MT_HIGH_PRIORITY;
+	dect_tbc_queue_mac_control(tbc, skb);
+	return 0;
+}
+
+static int dect_tbc_send_attributes_confirm(struct dect_tbc *tbc)
+{
+	struct dect_cctrl cctl;
+	struct sk_buff *skb;
+
+	tbc_debug(tbc, "TX ATTRIBUTES_T_CONFIRM\n");
+	skb = dect_t_skb_alloc();
+	if (skb == NULL)
+		return -ENOMEM;
+
+	cctl.cmd     = DECT_CCTRL_ATTRIBUTES_T_CONFIRM;
+	cctl.ecn     = tbc->id.ecn;
+	cctl.lbn     = tbc->id.lbn;
+	cctl.type    = DECT_CCTRL_TYPE_SYMETRIC_BEARER;
+	cctl.service = tbc->service;
+	cctl.cf      = false;
+
+	cctl.slot   = DECT_FULL_SLOT;
+	cctl.a_mod  = DECT_MODULATION_2_LEVEL;
+	cctl.bz_mod = DECT_MODULATION_2_LEVEL;
+	cctl.bz_ext_mod = 7;
+	cctl.acr    = 0;
+
+	if (tbc->type == DECT_MAC_CONN_BASIC)
+		dect_build_tail_msg(skb, DECT_TM_TYPE_BCCTRL, &cctl);
+	else
+		dect_build_tail_msg(skb, DECT_TM_TYPE_ACCTRL, &cctl);
+
+	dect_tbc_queue_mac_control(tbc, skb);
+	return 0;
+}
+
+static int dect_tbc_send_release(struct dect_tbc *tbc,
+				 enum dect_release_reasons reason)
+{
+	struct dect_cctrl cctl;
+	struct sk_buff *skb;
+
+	tbc_debug(tbc, "TX RELEASE: reason: %x\n", reason);
+	skb = dect_t_skb_alloc();
+	if (skb == NULL)
+		return -ENOMEM;
+
+	cctl.cmd     = DECT_CCTRL_RELEASE;
+	cctl.pmid    = dect_build_pmid(&tbc->id.pmid);
+	cctl.reason  = reason;
+
+	if (tbc->type == DECT_MAC_CONN_BASIC)
+		dect_build_tail_msg(skb, DECT_TM_TYPE_BCCTRL, &cctl);
+	else
+		dect_build_tail_msg(skb, DECT_TM_TYPE_ACCTRL, &cctl);
+
+	/* RELEASE messages may appear in any frame */
+	skb->priority = DECT_MT_HIGH_PRIORITY;
+	dect_tbc_queue_mac_control(tbc, skb);
+	return 0;
+}
+
+static void dect_tbc_state_change(struct dect_tbc *tbc, enum dect_tbc_state state)
+{
+	struct dect_cell *cell = tbc->cell;
+
+	tbc_debug(tbc, "state change: %s (%u) -> %s (%u)\n",
+		  tbc_states[tbc->state], tbc->state, tbc_states[state], state);
+
+	if (tbc->state == DECT_TBC_ESTABLISHED) {
+		cell->tbc_num_est--;
+		cell->tbc_last_chd = tbc->rxb.chd;
+	} else if (state == DECT_TBC_ESTABLISHED)
+		cell->tbc_num_est++;
+
+	tbc->state = state;
+}
+
+static int dect_tbc_event(const struct dect_tbc *tbc, enum dect_tbc_event event)
+{
+	const struct dect_cluster_handle *clh = tbc->cell->handle.clh;
+
+	return clh->ops->tbc_event_ind(clh, &tbc->id, event);
+}
+
+static int dect_tbc_establish_cfm(const struct dect_tbc *tbc, bool success)
+{
+	const struct dect_cluster_handle *clh = tbc->cell->handle.clh;
+
+	return clh->ops->tbc_establish_cfm(clh, &tbc->id, success,
+					   tbc->rxb.chd.slot);
+}
+
+static void dect_tbc_release_notify(const struct dect_tbc *tbc,
+				    enum dect_release_reasons reason)
+{
+	const struct dect_cluster_handle *clh = tbc->cell->handle.clh;
+
+	clh->ops->tbc_dis_ind(clh, &tbc->id, reason);
+}
+
+static void dect_tdd_channel_desc(struct dect_channel_desc *dst,
+				  const struct dect_channel_desc *chd)
+{
+	dst->pkt     = chd->pkt;
+	dst->b_fmt   = chd->b_fmt;
+	dst->carrier = chd->carrier;
+	dst->slot    = dect_tdd_slot(chd->slot);
+}
+
+static void dect_tbc_destroy(struct dect_cell *cell, struct dect_tbc *tbc)
+{
+	tbc_debug(tbc, "destroy\n");
+	dect_tbc_state_change(tbc, DECT_TBC_NONE);
+
+	dect_timer_del(&tbc->wd_timer);
+	dect_timer_del(&tbc->wait_timer);
+	dect_timer_del(&tbc->release_timer);
+	dect_timer_del(&tbc->enc_timer);
+	dect_bc_release(&tbc->bc);
+
+	dect_channel_release(cell, tbc->txb.trx, &tbc->txb.chd);
+	dect_bearer_release(cell, &tbc->txb);
+
+	dect_channel_release(cell, tbc->rxb.trx, &tbc->rxb.chd);
+	dect_bearer_release(cell, &tbc->rxb);
+
+	list_del(&tbc->list);
+	kfree_skb(tbc->cs_tx_skb);
+	kfree(tbc);
+}
+
+static void dect_tbc_release_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_tbc *tbc = data;
+
+	switch (tbc->state) {
+	default:
+		dect_tbc_state_change(tbc, DECT_TBC_RELEASING);
+		break;
+	case DECT_TBC_RELEASING:
+		dect_tbc_state_change(tbc, DECT_TBC_RELEASED);
+		break;
+	case DECT_TBC_NONE:
+	case DECT_TBC_REQ_SENT:
+	case DECT_TBC_RELEASED:
+		return dect_tbc_destroy(cell, tbc);
+	}
+
+	dect_tbc_send_release(tbc, tbc->release_reason);
+	dect_bearer_timer_add(tbc->cell, &tbc->txb, &tbc->release_timer,
+			      DECT_MT_FRAME_RATE);
+}
+
+static void dect_tbc_begin_release(struct dect_cell *cell, struct dect_tbc *tbc,
+				   enum dect_release_reasons reason)
+{
+	tbc->release_reason = reason;
+	dect_tbc_release_timer(cell, tbc);
+}
+
+static void dect_tbc_dis_req(const struct dect_cell_handle *ch,
+			     const struct dect_tbc_id *id,
+			     enum dect_release_reasons reason)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		return;
+	tbc_debug(tbc, "TBC_DIS-req: reason: %u\n", reason);
+	dect_tbc_begin_release(cell, tbc, reason);
+}
+
+static int dect_tbc_establish(struct dect_cell *cell, struct dect_tbc *tbc)
+{
+	dect_tbc_state_change(tbc, DECT_TBC_ESTABLISHED);
+	if (dect_tbc_establish_cfm(tbc, true) < 0)
+		return -1;
+	return 0;
+}
+
+/**
+ * dect_watchdog_timer - connection watchdog timer
+ *
+ * The watchdog timer is forwarded when an expected event occurs, on expiry
+ * it will release the TBC. The relevant event depends on the TBC's state:
+ *
+ * Until ESTABLISHED state, P_T tails must be sent in every allowed frame.
+ * The timer is forwarded when receiving a P_T tail in an allowed frame.
+ *
+ * In ESTABLISHED state, an RFPI handshake must be received at least
+ * every T201 (5) seconds. The timer is forwarded when receiving an N_T
+ * tail containing a matching RFPI.
+ */
+static void dect_tbc_watchdog_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_tbc *tbc = data;
+
+	tbc_debug(tbc, "watchdog expire\n");
+	if (tbc->state != DECT_TBC_ESTABLISHED) {
+		dect_tbc_establish_cfm(tbc, false);
+		dect_tbc_begin_release(cell, tbc, DECT_REASON_BEARER_SETUP_OR_HANDOVER_FAILED);
+	} else {
+		dect_tbc_release_notify(tbc, DECT_REASON_TIMEOUT_LOST_HANDSHAKE);
+		dect_tbc_begin_release(cell, tbc, DECT_REASON_TIMEOUT_LOST_HANDSHAKE);
+	}
+}
+
+static void dect_tbc_watchdog_reschedule(struct dect_cell *cell,
+					 struct dect_tbc *tbc)
+{
+	u16 timeout;
+
+	if (tbc->state == DECT_TBC_ESTABLISHED)
+		timeout = DECT_TBC_RFPI_TIMEOUT;
+	else
+		timeout = DECT_MT_FRAME_RATE;
+
+	tbc_debug(tbc, "watchdog reschedule timeout: %u\n", timeout);
+	dect_bearer_timer_add(cell, &tbc->rxb, &tbc->wd_timer, timeout);
+}
+
+static int dect_tbc_check_attributes(struct dect_cell *cell, struct dect_tbc *tbc,
+				     const struct dect_cctrl *cctl)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+
+	tbc_debug(tbc, "RX ATTRIBUTES_T_REQUEST\n");
+	tbc->id.ecn  = cctl->ecn;
+	tbc->id.lbn  = cctl->lbn;
+	tbc->service = cctl->service;
+
+	if (clh->ops->tbc_establish_ind(clh, &cell->handle, &tbc->id,
+					tbc->service, tbc->handover) < 0)
+		return -1;
+	return 0;
+}
+
+/**
+ * dect_tbc_state_process - connection setup and maintenance state proccesing
+ *
+ * Process all messages before ESTABLISHED state, as well as all connection
+ * control messages in ESTABLISHED state.
+ */
+static int dect_tbc_state_process(struct dect_cell *cell, struct dect_tbc *tbc,
+				  const struct dect_tail_msg *tm)
+{
+	const struct dect_cctrl *cctl = &tm->cctl;
+	struct sk_buff *m_skb;
+	u8 framenum;
+
+	if (tbc->state == DECT_TBC_OTHER_WAIT) {
+		tbc_debug(tbc, "RX in OTHER_WAIT\n");
+		/* Any message except RELEASE switches the bearer to
+		 * ESTABLISHED state.
+		 */
+		if ((tm->type == DECT_TM_TYPE_BCCTRL ||
+		     tm->type == DECT_TM_TYPE_ACCTRL) &&
+		    (cctl->fmid != cell->fmid ||
+		     cctl->pmid != dect_build_pmid(&tbc->id.pmid) ||
+		     cctl->cmd == DECT_CCTRL_RELEASE))
+			goto release;
+
+		if (dect_tbc_establish(cell, tbc) < 0)
+			goto release;
+		goto out;
+	}
+
+	/* Before OTHER_WAIT state, M_T tails must be received in every allowed
+	 * frame. FPs may send M_T tails in uneven frames, PTs in even frames.
+	 * Additionally FPs may transmit responses to BEARER_REQUEST messages in
+	 * every frame.
+	 */
+	framenum = dect_framenum(cell, DECT_TIMER_RX);
+	if (cell->mode == DECT_MODE_FP) {
+		if ((framenum & 0x1) == 1)
+			return 1;
+	} else {
+		if ((framenum & 0x1) == 0 && tbc->state != DECT_TBC_REQ_SENT)
+			return 1;
+	}
+
+	if (tm->type != DECT_TM_TYPE_BCCTRL && tm->type != DECT_TM_TYPE_ACCTRL)
+		goto release;
+
+	switch (cctl->cmd) {
+	case DECT_CCTRL_ATTRIBUTES_T_REQUEST:
+	case DECT_CCTRL_ATTRIBUTES_T_CONFIRM:
+	case DECT_CCTRL_BANDWIDTH_T_REQUEST:
+	case DECT_CCTRL_BANDWIDTH_T_CONFIRM:
+	case DECT_CCTRL_CHANNEL_LIST:
+		break;
+	default:
+		if (cctl->fmid != cell->fmid)
+			goto release;
+		/* fall through */
+	case DECT_CCTRL_RELEASE:
+		if (cctl->pmid != dect_build_pmid(&tbc->id.pmid))
+			goto release;
+	}
+
+	switch ((int)tbc->state) {
+	case DECT_TBC_NONE:
+		/*
+		 * Receiving side, initial request.
+		 */
+		dect_tbc_state_change(tbc, DECT_TBC_REQ_RCVD);
+		break;
+
+	case DECT_TBC_REQ_RCVD:
+	case DECT_TBC_RESPONSE_SENT:
+		/*
+		 * Receiving side: waiting for LLME to create MBC. Only "WAIT"
+		 * messages are valid in both directions.
+		 */
+		tbc_debug(tbc, "RX in REQ_RCVD: %llx\n",
+			  (unsigned long long)cctl->cmd);
+
+		if (tbc->type == DECT_MAC_CONN_ADVANCED &&
+		    cctl->cmd == DECT_CCTRL_ATTRIBUTES_T_REQUEST)
+			dect_tbc_check_attributes(cell, tbc, cctl);
+		else if (cctl->cmd != DECT_CCTRL_WAIT)
+			goto release;
+
+		m_skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_WAIT);
+		if (m_skb == NULL)
+			goto release;
+		dect_tbc_queue_mac_control(tbc, m_skb);
+		break;
+
+	case DECT_TBC_REQ_SENT:
+	case DECT_TBC_WAIT_RCVD:
+		/*
+		 * Initiator: request was sent, waiting for confirm. "WAIT"
+		 * messages must be responded to with another "WAIT" message.
+		 */
+		tbc_debug(tbc, "Reply in REQ_SENT %u\n", tm->type);
+		if (cctl->cmd != DECT_CCTRL_BEARER_CONFIRM) {
+			if (cctl->cmd != DECT_CCTRL_WAIT)
+				goto release;
+
+			m_skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_WAIT);
+			if (m_skb == NULL)
+				goto release;
+			dect_tbc_queue_mac_control(tbc, m_skb);
+
+			dect_tbc_state_change(tbc, DECT_TBC_WAIT_RCVD);
+		} else {
+			tbc_debug(tbc, "Confirmed\n");
+			m_skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_WAIT);
+			if (m_skb == NULL)
+				goto release;
+			dect_tbc_queue_mac_control(tbc, m_skb);
+
+			dect_tbc_state_change(tbc, DECT_TBC_OTHER_WAIT);
+		}
+		break;
+
+	case DECT_TBC_ESTABLISHED:
+		if (cctl->cmd != DECT_CCTRL_RELEASE)
+			break;
+		/* Immediate release */
+		dect_tbc_release_notify(tbc, DECT_REASON_BEARER_RELEASE);
+		dect_tbc_destroy(cell, tbc);
+		return 0;
+
+	case DECT_TBC_RELEASING:
+		/*
+		 * Unacknowledged release procedure in progress, ignore the
+		 * packet unless its a release message, in which case the
+		 * bearer can be destroyed immediately (crossed bearer release
+		 * procedure).
+		 */
+		if (cctl->cmd == DECT_CCTRL_RELEASE)
+			dect_tbc_destroy(cell, tbc);
+
+	case DECT_TBC_RELEASED:
+		return 0;
+	}
+
+out:
+	dect_tbc_watchdog_reschedule(cell, tbc);
+	return 1;
+
+release:
+	dect_tbc_establish_cfm(tbc, false);
+	dect_tbc_begin_release(cell, tbc, DECT_REASON_UNKNOWN);
+	return 0;
+}
+
+static void dect_tbc_enc_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_tbc *tbc = data;
+	enum dect_encctrl_cmds cmd;
+	struct sk_buff *skb;
+
+	tbc_debug(tbc, "encryption timer: state: %u cnt: %u\n",
+		  tbc->enc_state, tbc->enc_msg_cnt);
+
+	if (++tbc->enc_msg_cnt > 5) {
+		dect_tbc_release_notify(tbc, DECT_REASON_BEARER_RELEASE);
+		return dect_tbc_begin_release(cell, tbc, DECT_REASON_BEARER_RELEASE);
+	}
+
+	dect_bearer_timer_add(cell, &tbc->txb, &tbc->enc_timer, 2);
+
+	switch (tbc->enc_state) {
+	case DECT_TBC_ENC_START_REQ_RCVD:
+		tbc_debug(tbc, "TX encryption enabled\n");
+		dect_enable_cipher(tbc->txb.trx, tbc->txb.chd.slot, tbc->ck);
+		/* fall through */
+	case DECT_TBC_ENC_START_CFM_SENT:
+		tbc->enc_state = DECT_TBC_ENC_START_CFM_SENT;
+		cmd = DECT_ENCCTRL_START_CONFIRM;
+		break;
+	case DECT_TBC_ENC_START_REQ_SENT:
+		cmd = DECT_ENCCTRL_START_REQUEST;
+		break;
+	default:
+		return;
+	}
+
+	skb = dect_tbc_build_encctrl(tbc, cmd);
+	if (skb != NULL)
+		dect_tbc_queue_mac_control(tbc, skb);
+}
+
+static int dect_tbc_enc_state_process(struct dect_cell *cell,
+				      struct dect_tbc *tbc,
+				      const struct dect_tail_msg *tm)
+{
+	const struct dect_encctrl *ectl = &tm->encctl;
+	struct sk_buff *skb;
+
+	if (ectl->fmid != cell->fmid ||
+	    ectl->pmid != dect_build_pmid(&tbc->id.pmid))
+		return 0;
+
+	switch (ectl->cmd) {
+	case DECT_ENCCTRL_START_REQUEST:
+		if (tbc->enc_state != DECT_TBC_ENC_DISABLED ||
+		    cell->mode != DECT_MODE_FP)
+			break;
+		tbc->enc_state = DECT_TBC_ENC_START_REQ_RCVD;
+		tbc->enc_msg_cnt = 0;
+
+		dect_bearer_timer_add(cell, &tbc->txb, &tbc->enc_timer, 0);
+		break;
+	case DECT_ENCCTRL_START_CONFIRM:
+		if (tbc->enc_state == DECT_TBC_ENC_START_REQ_SENT) {
+			tbc->enc_state = DECT_TBC_ENC_START_CFM_RCVD;
+			tbc->enc_msg_cnt = 0;
+
+			dect_timer_del(&tbc->enc_timer);
+			tbc_debug(tbc, "TX encryption enabled\n");
+			dect_enable_cipher(tbc->txb.trx, tbc->txb.chd.slot,
+					   tbc->ck);
+		}
+		if (tbc->enc_state == DECT_TBC_ENC_START_CFM_RCVD) {
+			skb = dect_tbc_build_encctrl(tbc, DECT_ENCCTRL_START_GRANT);
+			if (skb != NULL)
+				dect_tbc_queue_mac_control(tbc, skb);
+		}
+		break;
+	case DECT_ENCCTRL_START_GRANT:
+		if (tbc->enc_state != DECT_TBC_ENC_START_CFM_SENT)
+			break;
+		tbc->enc_state = DECT_TBC_ENC_ENABLED;
+
+		dect_timer_del(&tbc->enc_timer);
+		tbc_debug(tbc, "RX encryption enabled\n");
+		dect_enable_cipher(tbc->rxb.trx, tbc->rxb.chd.slot, tbc->ck);
+		dect_tbc_event(tbc, DECT_TBC_CIPHER_ENABLED);
+		break;
+	case DECT_ENCCTRL_STOP_REQUEST:
+		break;
+	case DECT_ENCCTRL_STOP_CONFIRM:
+		break;
+	case DECT_ENCCTRL_STOP_GRANT:
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void dect_tbc_queue_cs_data(struct dect_cell *cell, struct dect_tbc *tbc,
+				   struct sk_buff *skb,
+				   const struct dect_tail_msg *tm)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+
+	skb = skb_clone(skb, GFP_ATOMIC);
+	if (skb == NULL)
+		return;
+	skb_pull(skb, DECT_T_FIELD_OFF);
+	skb_trim(skb, DECT_C_S_SDU_SIZE);
+
+	DECT_CS_CB(skb)->seq = tm->ctd.seq;
+	clh->ops->tbc_data_ind(clh, &tbc->id, DECT_MC_C_S, skb);
+}
+
+static void dect_tbc_update_handover_state(struct dect_tbc *tbc, bool ok)
+{
+	const struct dect_cluster_handle *clh = tbc->cell->handle.clh;
+
+	if (ok) {
+		tbc->handover_tokens += DECT_TBC_HO_TOKENS_OK;
+		if (tbc->handover_tokens > DECT_TBC_HO_TOKENS_MAX)
+			tbc->handover_tokens = DECT_TBC_HO_TOKENS_MAX;
+	} else {
+		tbc->handover_tokens -= DECT_TBC_HO_TOKENS_ERROR;
+		if (tbc->handover_tokens < 0)
+			tbc->handover_tokens = 0;
+	}
+
+	tbc_debug(tbc, "handover: ok: %u tokens: %d\n", ok, tbc->handover_tokens);
+	if (tbc->handover_tokens == 0)
+		clh->ops->tbc_handover_req(clh, &tbc->id);
+}
+
+static void dect_tbc_report_rssi(struct dect_cell *cell,
+				 struct dect_bearer *bearer,
+				 u8 slot, u8 rssi)
+{
+	struct dect_tbc *tbc = bearer->tbc;
+
+	/* A RSSI report implies an In-Sync error */
+	if (cell->mode == DECT_MODE_PP)
+		dect_tbc_update_handover_state(tbc, false);
+}
+
+#define DECT_CHECKSUM_ALL \
+	(DECT_CHECKSUM_A_CRC_OK | DECT_CHECKSUM_X_CRC_OK | DECT_CHECKSUM_Z_CRC_OK)
+
+static bool dect_tbc_checksum_ok(const struct sk_buff *skb)
+{
+	return (DECT_TRX_CB(skb)->csum & DECT_CHECKSUM_ALL) == DECT_CHECKSUM_ALL;
+}
+
+static void dect_tbc_rcv(struct dect_cell *cell, struct dect_bearer *bearer,
+			 struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+	struct dect_tbc *tbc = bearer->tbc;
+	struct dect_tail_msg _tm, *tm = &_tm;
+	bool a_crc_ok, collision;
+	bool q1, q2;
+
+	dect_raw_rcv(skb);
+
+	if (cell->mode == DECT_MODE_PP)
+		dect_tbc_update_handover_state(tbc, dect_tbc_checksum_ok(skb));
+
+	/* Verify A-field checksum. Sucessful reception of the A-field is
+	 * indicated by transmitting the Q2 bit in the reverse direction
+	 * or the Q1 bit in the direction FP->PP when Q1 is set to 0.
+	 */
+	if (DECT_TRX_CB(skb)->csum & DECT_CHECKSUM_A_CRC_OK)
+		tbc->txb.q = DECT_HDR_Q2_FLAG;
+	else
+		goto rcv_b_field;
+
+	/* Q1 and Q2 bit settings for MAC service IN as per section 10.8.1.3.1 */
+	q1 = skb->data[DECT_HDR_Q1_OFF] & DECT_HDR_Q1_FLAG;
+	q2 = skb->data[DECT_HDR_Q2_OFF] & DECT_HDR_Q2_FLAG;
+
+	if (cell->mode == DECT_MODE_FP)
+		a_crc_ok = q2;
+	else {
+		if (q2) {
+			a_crc_ok  = true;
+			collision = q1;
+			/* ignore collision indications for now as the
+			 * transceiver firmware seems to improperly transmit
+			 * the Z-Field.
+			 */
+			collision = false;
+		} else {
+			a_crc_ok  = q1;
+			collision = false;
+		}
+
+		dect_tbc_update_handover_state(tbc, a_crc_ok && !collision);
+	}
+
+	if (tbc->cs_tx_ok) {
+		if (a_crc_ok) {
+			tbc_debug(tbc, "ARQ acknowledgement\n");
+			dect_tbc_event(tbc, DECT_TBC_ACK_RECEIVED);
+		} else
+			tbc_debug(tbc, "C-channel data lost\n");
+	}
+
+	if (dect_parse_tail_msg(tm, skb) < 0)
+		goto err;
+
+	if (tbc->state != DECT_TBC_ESTABLISHED ||
+	    tm->type == DECT_TM_TYPE_BCCTRL ||
+	    tm->type == DECT_TM_TYPE_ACCTRL) {
+		if (!dect_tbc_state_process(cell, tbc, tm))
+			goto err;
+	}
+
+	tbc_debug(tbc, "receive\n");
+
+	/* Reschedule watchdog on successful RFPI handshake. */
+	if (tm->type == DECT_TM_TYPE_ID && !dect_rfpi_cmp(&tm->idi, &cell->idi))
+		dect_tbc_watchdog_reschedule(cell, tbc);
+
+	if (tm->type == DECT_TM_TYPE_ENCCTRL) {
+		if (!dect_tbc_enc_state_process(cell, tbc, tm))
+			goto err;
+	}
+
+	dect_bc_rcv(cell, &tbc->bc, skb, tm);
+
+	switch (tbc->enc_state) {
+	case DECT_TBC_ENC_START_REQ_SENT:
+	case DECT_TBC_ENC_START_CFM_SENT:
+		goto err;
+	default:
+		break;
+	}
+
+	if (tbc->state != DECT_TBC_REQ_RCVD &&
+	    tbc->state != DECT_TBC_RESPONSE_SENT) {
+		if (tm->type == DECT_TM_TYPE_CT)
+			dect_tbc_queue_cs_data(cell, tbc, skb, tm);
+	}
+
+rcv_b_field:
+	switch (dect_parse_b_id(skb)) {
+	case DECT_BI_UTYPE_0:
+	case DECT_BI_UTYPE_1:
+		break;
+	default:
+		goto err;
+	}
+
+	skb_pull(skb, DECT_A_FIELD_SIZE);
+	skb_trim(skb, dect_b_field_size(&bearer->chd));
+	clh->ops->tbc_data_ind(clh, &tbc->id, DECT_MC_I_N, skb);
+	return;
+
+err:
+	kfree_skb(skb);
+}
+
+static void dect_tbc_data_req(const struct dect_cell_handle *ch,
+			      const struct dect_tbc_id *id,
+			      enum dect_data_channels chan,
+			      struct sk_buff *skb)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		goto err;
+	tbc_debug(tbc, "TBC_DATA-req: chan: %u len: %u\n", chan, skb->len);
+
+	switch (chan) {
+	case DECT_MC_C_S:
+		DECT_A_CB(skb)->id = DECT_CS_CB(skb)->seq ? DECT_TI_CT_PKT_1 :
+							    DECT_TI_CT_PKT_0;
+		tbc->cs_tx_skb = skb;
+		break;
+	case DECT_MC_I_N:
+		tbc->b_tx_skb = skb;
+		break;
+	default:
+		goto err;
+	}
+	return;
+
+err:
+	kfree_skb(skb);
+}
+
+static int dect_tbc_enc_req(const struct dect_cell_handle *ch,
+			    const struct dect_tbc_id *id, u64 ck)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		return -ENOENT;
+
+	tbc_debug(tbc, "RX/TX encryption enabled: ck: %.16llx\n",
+		  (unsigned long long)ck);
+
+	tbc->ck = ck;
+	dect_enable_cipher(tbc->rxb.trx, tbc->rxb.chd.slot, tbc->ck);
+	dect_enable_cipher(tbc->txb.trx, tbc->txb.chd.slot, tbc->ck);
+	return 0;
+}
+
+static int dect_tbc_enc_eks_req(const struct dect_cell_handle *ch,
+				const struct dect_tbc_id *id,
+				enum dect_cipher_states status)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+	struct sk_buff *skb;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		return -ENOENT;
+
+	tbc_debug(tbc, "TBC_ENC_EKS-req: status: %u\n", status);
+	skb = dect_tbc_build_encctrl(tbc, DECT_ENCCTRL_START_REQUEST);
+	if (skb != NULL)
+		dect_tbc_queue_mac_control(tbc, skb);
+
+	tbc->enc_state = DECT_TBC_ENC_START_REQ_SENT;
+	tbc->enc_msg_cnt = 0;
+	dect_bearer_timer_add(cell, &tbc->txb, &tbc->enc_timer, 0);
+
+	tbc_debug(tbc, "RX encryption enabled\n");
+	dect_enable_cipher(tbc->rxb.trx, tbc->rxb.chd.slot, tbc->ck);
+	return 0;
+}
+
+static int dect_tbc_enc_key_req(const struct dect_cell_handle *ch,
+				const struct dect_tbc_id *id, u64 ck)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		return -ENOENT;
+
+	tbc_debug(tbc, "TBC_ENC_KEY-req: key: %.16llx\n", (unsigned long long)ck);
+	tbc->ck = ck;
+	return 0;
+}
+
+static void dect_tbc_enable(struct dect_cell *cell, struct dect_tbc *tbc)
+{
+	dect_bearer_enable(&tbc->rxb);
+	dect_bearer_enable(&tbc->txb);
+	dect_bc_init(cell, &tbc->bc);
+}
+
+/*
+ * Activation timer: enable the bearer once the TX channel is accessible,
+ * which is defined by the receivers scanning sequence.
+ */
+static void dect_tbc_enable_timer(struct dect_cell *cell,
+				  struct dect_bearer *bearer)
+{
+	struct dect_tbc *tbc = bearer->tbc;
+	struct sk_buff *skb;
+	enum dect_cctrl_cmds cmd;
+
+	tbc_debug(tbc, "TX ACCESS_REQUEST\n");
+	if (tbc->handover)
+		cmd = DECT_CCTRL_BEARER_HANDOVER_REQ;
+	else
+		cmd = DECT_CCTRL_ACCESS_REQ;
+
+	skb = dect_tbc_build_cctrl(tbc, cmd);
+	if (skb == NULL)
+		return;
+
+	/* The packet overrides the T-MUX rules. PPs use a special tail
+	 * coding for the first transmission. */
+	skb->priority = DECT_MT_HIGH_PRIORITY;
+	if (cell->mode == DECT_MODE_FP)
+		DECT_A_CB(skb)->id = DECT_TI_MT;
+	else
+		DECT_A_CB(skb)->id = DECT_TI_MT_PKT_0;
+
+	dect_tbc_enable(cell, tbc);
+	dect_tbc_queue_mac_control(tbc, skb);
+	dect_tbc_state_change(tbc, DECT_TBC_REQ_SENT);
+
+	/* Start watchdog */
+	dect_bearer_timer_add(cell, &tbc->rxb, &tbc->wd_timer, 1);
+}
+
+static const struct dect_bearer_ops dect_tbc_ops = {
+	.state		= DECT_TRAFFIC_BEARER,
+	.enable		= dect_tbc_enable_timer,
+	.rcv		= dect_tbc_rcv,
+	.report_rssi	= dect_tbc_report_rssi,
+};
+
+/**
+ * dect_tbc_init - initialise a traffic bearer control instance
+ *
+ * @cell:	DECT cell
+ * @id:		MBC ID
+ * @rchd:	RX channel description
+ * @tchd:	TX channel description
+ */
+static struct dect_tbc *dect_tbc_init(struct dect_cell *cell,
+				      const struct dect_tbc_id *id,
+				      struct dect_transceiver *rtrx,
+				      struct dect_transceiver *ttrx,
+				      const struct dect_channel_desc *rchd,
+				      const struct dect_channel_desc *tchd)
+
+{
+	struct dect_tbc *tbc;
+
+	tbc = kzalloc(sizeof(*tbc), GFP_ATOMIC);
+	if (tbc == NULL)
+		return NULL;
+
+	tbc->cell	     = cell;
+	tbc->id		     = *id;
+	tbc->handover_tokens = DECT_TBC_HO_TOKENS_INITIAL;
+
+	INIT_LIST_HEAD(&tbc->bc.list);
+	dect_timer_init(&tbc->wait_timer);
+	dect_timer_setup(&tbc->wd_timer, dect_tbc_watchdog_timer, tbc);
+	dect_timer_setup(&tbc->release_timer, dect_tbc_release_timer, tbc);
+	dect_timer_setup(&tbc->enc_timer, dect_tbc_enc_timer, tbc);
+
+	dect_bearer_init(cell, &tbc->rxb, &dect_tbc_ops,
+			 rtrx, rchd, DECT_BEARER_RX, tbc);
+	dect_bearer_init(cell, &tbc->txb, &dect_tbc_ops,
+			 ttrx, tchd, DECT_BEARER_TX, tbc);
+
+	list_add_tail(&tbc->list, &cell->tbcs);
+	return tbc;
+}
+
+static int dect_tbc_establish_req(const struct dect_cell_handle *ch,
+				  const struct dect_tbc_id *id,
+				  const struct dect_channel_desc *chd,
+				  enum dect_mac_service_types service,
+				  bool handover)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_transceiver *ttrx, *rtrx;
+	struct dect_channel_desc tchd, rchd;
+	struct dect_tbc *tbc;
+	u8 rssi;
+	int err;
+
+	/* Select TDD slot pair and reserve transceiver resources */
+	tchd.pkt   = chd->pkt;
+	tchd.b_fmt = chd->b_fmt;
+	err = dect_select_channel(cell, &ttrx, &tchd, &rssi, true);
+	if (err < 0)
+		goto err1;
+	dect_channel_reserve(cell, ttrx, &tchd);
+
+	err = -ENOSPC;
+	dect_tdd_channel_desc(&rchd, &tchd);
+	rtrx = dect_select_transceiver(cell, &rchd);
+	if (rtrx == NULL)
+		goto err2;
+	dect_channel_reserve(cell, rtrx, &rchd);
+
+	err = -ENOMEM;
+	tbc = dect_tbc_init(cell, id, rtrx, ttrx, &rchd, &tchd);
+	if (tbc == NULL)
+		goto err3;
+	tbc->id.tbei  = dect_tbc_alloc_tbei(cell);
+	tbc->service  = service;
+	tbc->handover = handover;
+
+	tbc_debug(tbc, "TBC_ESTABLISH-req: handover: %d\n", handover);
+	dect_tx_bearer_schedule(cell, &tbc->txb, rssi);
+	return 0;
+
+err3:
+	dect_channel_release(cell, rtrx, &rchd);
+err2:
+	dect_channel_release(cell, ttrx, &tchd);
+err1:
+	return err;
+}
+
+/* TBC establishment confirmation from CCF */
+static int dect_tbc_establish_res(const struct dect_cell_handle *ch,
+				  const struct dect_tbc_id *id)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_tbc *tbc;
+	int err;
+
+	tbc = dect_tbc_get_by_tbei(cell, id->tbei);
+	if (tbc == NULL)
+		return -ENOENT;
+	tbc_debug(tbc, "TBC_ESTABLISH-res\n");
+	WARN_ON(tbc->state != DECT_TBC_REQ_RCVD);
+
+	/* Stop wait timer and send CONFIRM */
+	dect_timer_del(&tbc->wait_timer);
+	if (tbc->type == DECT_MAC_CONN_BASIC)
+		err = dect_tbc_send_confirm(tbc);
+	else
+		err = dect_tbc_send_attributes_confirm(tbc);
+	if (err < 0)
+		return err;
+
+	dect_tbc_state_change(tbc, DECT_TBC_OTHER_WAIT);
+	return 0;
+}
+
+static void dect_tbc_wait_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_tbc *tbc = data;
+	struct sk_buff *skb;
+
+	tbc_debug(tbc, "wait timer\n");
+	skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_WAIT);
+	if (skb == NULL)
+		return;
+
+	/* The first response is permitted in any frame */
+	if (tbc->state == DECT_TBC_REQ_RCVD)
+		skb->priority = DECT_MT_HIGH_PRIORITY;
+	dect_tbc_queue_mac_control(tbc, skb);
+
+	dect_tbc_state_change(tbc, DECT_TBC_RESPONSE_SENT);
+}
+
+/**
+ * dect_tbc_rcv_request - handle incoming connection setup attempts
+ *
+ *
+ */
+static void dect_tbc_rcv_request(struct dect_cell *cell,
+				 const struct dect_transceiver_slot *ts,
+				 const struct dect_tail_msg *tm,
+				 struct sk_buff *skb)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+	struct dect_transceiver *rtrx, *ttrx;
+	struct dect_channel_desc rchd, tchd;
+	struct dect_tbc_id id;
+	struct dect_tbc *tbc;
+	bool handover = false;
+
+	if (tm->cctl.fmid != cell->fmid)
+		goto err1;
+	dect_raw_rcv(skb);
+
+	switch (tm->cctl.cmd) {
+	case DECT_CCTRL_ACCESS_REQ:
+		break;
+	case DECT_CCTRL_BEARER_HANDOVER_REQ:
+	case DECT_CCTRL_CONNECTION_HANDOVER_REQ:
+		/* Handover can only be initiated by the PP */
+		if (cell->mode == DECT_MODE_FP) {
+			handover = true;
+			break;
+		}
+	default:
+		rx_debug(cell, "unhandled TBC request: %llu\n",
+			 (unsigned long long)tm->cctl.cmd);
+		goto err1;
+	}
+
+	/* Select transceivers for RX/TX and reserve resources */
+	memcpy(&rchd, &ts->chd, sizeof(rchd));
+	rchd.pkt   = DECT_PACKET_P32;
+	rchd.b_fmt = DECT_B_UNPROTECTED;
+	rtrx = dect_select_transceiver(cell, &rchd);
+	if (rtrx == NULL)
+		goto err1;
+	dect_channel_reserve(cell, rtrx, &rchd);
+
+	dect_tdd_channel_desc(&tchd, &rchd);
+	ttrx = dect_select_transceiver(cell, &tchd);
+	if (ttrx == NULL)
+		goto err2;
+	dect_channel_reserve(cell, ttrx, &tchd);
+
+	memset(&id, 0, sizeof(id));
+	memcpy(&id.ari, &cell->idi.pari, sizeof(id.ari));
+	dect_parse_pmid(&id.pmid, tm->cctl.pmid);
+	id.lbn  = 0xf;
+	id.ecn  = 0;
+	id.tbei = dect_tbc_alloc_tbei(cell);
+
+	/* Initialize TBC */
+	tbc = dect_tbc_init(cell, &id, rtrx, ttrx, &rchd, &tchd);
+	if (tbc == NULL)
+		goto err3;
+
+	tbc->handover = handover;
+
+	if (tm->type == DECT_TM_TYPE_BCCTRL) {
+		/* Basic MAC connections only support the I_N_minimal_delay service */
+		tbc->type    = DECT_MAC_CONN_BASIC;
+		tbc->service = DECT_SERVICE_IN_MIN_DELAY;
+	} else {
+		/* Service is unknown at this time */
+		tbc->type    = DECT_MAC_CONN_ADVANCED;
+		tbc->service = DECT_SERVICE_UNKNOWN;
+	}
+
+	dect_tbc_state_change(tbc, DECT_TBC_REQ_RCVD);
+	tbc_debug(tbc, "RCV ACCESS_REQUEST\n");
+
+	/* Set Q2 bit on first response */
+	tbc->txb.q = DECT_HDR_Q2_FLAG;
+
+	/* Start the WAIT transmit timer */
+	dect_timer_setup(&tbc->wait_timer, dect_tbc_wait_timer, tbc);
+	dect_bearer_timer_add(cell, &tbc->txb, &tbc->wait_timer, 1);
+
+	/* Start watchdog timer: until ESTABLISHED state, the remote side
+	 * must transmit a M-tail in every allowed frame. */
+	dect_tbc_watchdog_reschedule(cell, tbc);
+	dect_tbc_enable(cell, tbc);
+
+	if (tbc->type == DECT_MAC_CONN_BASIC) {
+		if (clh->ops->tbc_establish_ind(clh, &cell->handle, &tbc->id,
+						tbc->service, tbc->handover) < 0)
+			goto err4;
+	} else {
+		if (dect_tbc_send_confirm(tbc) < 0)
+			goto err4;
+		dect_tbc_state_change(tbc, DECT_TBC_RESPONSE_SENT);
+	}
+
+	kfree_skb(skb);
+	return;
+
+err4:
+	dect_tbc_destroy(cell, tbc);
+err3:
+	dect_channel_release(cell, ttrx, &tchd);
+err2:
+	dect_channel_release(cell, rtrx, &rchd);
+err1:
+	kfree_skb(skb);
+}
+
+#if 0
+/*
+ * Connectionless Bearer Control (CBC)
+ */
+
+static void dect_cbc_rcv(struct dect_cell *cell, struct dect_bearer *bearer,
+			 struct sk_buff *skb)
+{
+	struct dect_cbc *cbc = bearer->cbc;
+	struct dect_tail_msg tm;
+
+	dect_parse_tail_msg(&tm, skb);
+	dect_bc_rcv(cell, &cbc->bc, skb, &tm);
+	kfree_skb(skb);
+}
+
+static const struct dect_bearer_ops dect_cbc_ops = {
+	.state		= DECT_CL_BEARER,
+	.rcv		= dect_cbc_rcv,
+};
+
+/**
+ * dect_cbc_init - Initialise a connectionless bearer control
+ *
+ * @cell:	DECT cell
+ * @chd:	channel description
+ */
+static struct dect_cbc *dect_cbc_init(struct dect_cell *cell,
+				      struct dect_channel_desc *chd)
+{
+	struct dect_bearer *bearer;
+	enum dect_slot_states mode;
+	struct dect_cbc *cbc = NULL;
+
+	bearer = dect_bearer_init(cell, &dect_cbc_ops, DECT_SIMPLEX_BEARER,
+				  NULL, chd, mode, cbc);
+	if (bearer == NULL)
+		return NULL;
+	cbc->dl_bearer = bearer;
+
+	dect_bc_init(cell, &cbc->bc);
+	return cbc;
+}
+#endif
+
+/*
+ * Dummy Bearer Control (DBC)
+ */
+
+#define dbc_debug(dbc, fmt, args...) \
+	pr_debug("DBC slot %u carrier %u: " fmt, \
+		 (dbc)->bearer.chd.slot, (dbc)->bearer.chd.carrier, ## args)
+
+static void dect_dbc_rcv(struct dect_cell *cell, struct dect_bearer *bearer,
+			 struct sk_buff *skb)
+{
+	struct dect_dbc *dbc = bearer->dbc;
+	struct dect_tail_msg tm;
+
+	/* Update A-field receive time stamp (A-field CRC is always correct) */
+	if (dect_framenum(cell, DECT_TIMER_RX) == 0)
+		cell->a_rcv_stamp = jiffies;
+
+	dect_raw_rcv(skb);
+
+	if (dect_parse_tail_msg(&tm, skb) < 0)
+		goto err;
+
+	/* Update Nt receive stamp if PARI matches */
+	if (tm.type == DECT_TM_TYPE_ID && !dect_rfpi_cmp(&tm.idi, &cell->idi))
+		cell->nt_rcv_stamp = jiffies;
+
+	dect_bc_rcv(cell, &dbc->bc, skb, &tm);
+err:
+	kfree_skb(skb);
+}
+
+static void dect_dbc_report_rssi(struct dect_cell *cell,
+				 struct dect_bearer *bearer,
+				 u8 slot, u8 rssi)
+{
+	dbc_debug(bearer->dbc, "RSSI: selection: %u now: %u\n", bearer->rssi, rssi);
+}
+
+static void dect_dbc_quality_control_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_dbc *dbc = data;
+	struct dect_bearer *bearer = &dbc->bearer;
+
+	switch (dbc->qctrl) {
+	case DECT_BEARER_QCTRL_WAIT:
+		dbc_debug(dbc, "quality control: confirm quality\n");
+		dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_RX);
+		dect_set_carrier(bearer->trx, bearer->chd.slot, bearer->chd.carrier);
+		dbc->qctrl = DECT_BEARER_QCTRL_CONFIRM;
+		dect_timer_add(cell, &dbc->qctrl_timer, DECT_TIMER_TX,
+			       1, bearer->chd.slot);
+		break;
+	case DECT_BEARER_QCTRL_CONFIRM:
+		dbc_debug(dbc, "quality control: wait\n");
+		dect_set_channel_mode(bearer->trx, &bearer->chd, DECT_SLOT_TX);
+		dect_set_carrier(bearer->trx, bearer->chd.slot, bearer->chd.carrier);
+		dbc->qctrl = DECT_BEARER_QCTRL_WAIT;
+		dect_timer_add(cell, &dbc->qctrl_timer, DECT_TIMER_TX,
+			       DECT_BEARER_QCTRL_PERIOD - 1, bearer->chd.slot);
+		break;
+	}
+}
+
+static void dect_dbc_enable(struct dect_cell *cell, struct dect_bearer *bearer)
+{
+	struct dect_dbc *dbc = bearer->dbc;
+	u8 framenum = dect_framenum(cell, DECT_TIMER_TX);
+	u8 extra;
+
+	extra = DECT_BEARER_QCTRL_FRAMENUM - framenum;
+	dbc->qctrl = DECT_BEARER_QCTRL_WAIT;
+	dect_timer_add(cell, &dbc->qctrl_timer, DECT_TIMER_TX,
+		       DECT_BEARER_QCTRL_PERIOD + extra, bearer->chd.slot);
+
+	dect_bearer_enable(bearer);
+}
+
+static const struct dect_bearer_ops dect_dbc_ops = {
+	.state		= DECT_DUMMY_BEARER,
+	.enable		= dect_dbc_enable,
+	.report_rssi	= dect_dbc_report_rssi,
+	.rcv		= dect_dbc_rcv,
+};
+
+static void dect_dbc_release(struct dect_dbc *dbc)
+{
+	struct dect_cell *cell = dbc->cell;
+
+	dect_channel_release(cell, dbc->bearer.trx, &dbc->bearer.chd);
+	dect_bearer_release(cell, &dbc->bearer);
+
+	dect_timer_del(&dbc->qctrl_timer);
+	dect_bc_release(&dbc->bc);
+	list_del(&dbc->list);
+	kfree(dbc);
+}
+
+/**
+ * dect_dbc_init - initialise dummy bearer control
+ *
+ * @cell:	DECT cell
+ * @chd:	channel description (PP only)
+ */
+static struct dect_dbc *dect_dbc_init(struct dect_cell *cell,
+				      const struct dect_channel_desc *chd)
+{
+	struct dect_channel_desc tchd;
+	struct dect_transceiver *trx;
+	enum dect_bearer_modes mode;
+	struct dect_dbc *dbc;
+	u8 uninitialized_var(rssi);
+
+	/* Transmission is always in direction FP -> PP */
+	if (cell->mode == DECT_MODE_FP) {
+		tchd.pkt   = DECT_PACKET_P00;
+		tchd.b_fmt = DECT_B_NONE;
+		if (dect_select_channel(cell, &trx, &tchd, &rssi, false) < 0)
+			goto err1;
+		chd = &tchd;
+
+		mode = DECT_BEARER_TX;
+	} else {
+		trx = dect_select_transceiver(cell, chd);
+		if (trx == NULL)
+			goto err1;
+		mode = DECT_BEARER_RX;
+	}
+
+	dect_channel_reserve(cell, trx, chd);
+
+	dbc = kzalloc(sizeof(*dbc), GFP_ATOMIC);
+	if (dbc == NULL)
+		goto err2;
+	dbc->cell = cell;
+	dect_timer_setup(&dbc->qctrl_timer, dect_dbc_quality_control_timer, dbc);
+	dect_bc_init(cell, &dbc->bc);
+
+	dect_bearer_init(cell, &dbc->bearer, &dect_dbc_ops, trx, chd, mode, dbc);
+
+	if (cell->mode == DECT_MODE_FP)
+		dect_tx_bearer_schedule(cell, &dbc->bearer, rssi);
+	else {
+		dect_bearer_enable(&dbc->bearer);
+
+		cell->a_rcv_stamp  = jiffies;
+		cell->nt_rcv_stamp = jiffies;
+	}
+
+	list_add_tail(&dbc->list, &cell->dbcs);
+	return dbc;
+
+err2:
+	dect_channel_release(cell, trx, chd);
+err1:
+	return NULL;
+}
+
+/*
+ * Monitor Bearer
+ */
+
+static void dect_dmb_release(struct dect_cell *cell, struct dect_dmb *dmb)
+{
+	cell->tbc_last_chd = dmb->rxb2.chd;
+
+	dect_timer_del(&dmb->wd_timer);
+
+	dect_transceiver_release(&cell->trg, dmb->rxb1.trx, &dmb->rxb1.chd);
+	dect_bearer_release(dmb->cell, &dmb->rxb1);
+
+	dect_transceiver_release(&cell->trg, dmb->rxb2.trx, &dmb->rxb2.chd);
+	dect_bearer_release(dmb->cell, &dmb->rxb2);
+
+	dect_bc_release(&dmb->bc);
+	list_del(&dmb->list);
+	kfree(dmb);
+}
+
+static void dect_dmb_watchdog_timer(struct dect_cell *cell, void *data)
+{
+	dect_dmb_release(cell, data);
+}
+
+static void dect_dmb_watchdog_reschedule(struct dect_cell *cell,
+					 struct dect_dmb *dmb)
+{
+	dect_bearer_timer_add(cell, &dmb->rxb1, &dmb->wd_timer,
+			      DECT_TBC_RFPI_TIMEOUT);
+}
+
+static void dect_dmb_rcv(struct dect_cell *cell, struct dect_bearer *bearer,
+			 struct sk_buff *skb)
+{
+	struct dect_dmb *dmb = bearer->dmb;
+	struct dect_tail_msg tm;
+
+	dect_raw_rcv(skb);
+
+	if (dect_parse_tail_msg(&tm, skb) < 0)
+		goto err;
+
+	/* Reschedule watchdog on successful RFPI handshake. */
+	if (tm.type == DECT_TM_TYPE_ID && !dect_rfpi_cmp(&tm.idi, &cell->idi))
+		dect_dmb_watchdog_reschedule(cell, dmb);
+
+	dect_bc_rcv(cell, &dmb->bc, skb, &tm);
+
+	switch (tm.type) {
+	case DECT_TM_TYPE_BCCTRL:
+	case DECT_TM_TYPE_ACCTRL:
+		if (tm.cctl.cmd == DECT_CCTRL_RELEASE)
+			return dect_dmb_release(cell, dmb);
+		break;
+	default:
+		break;
+	}
+err:
+	kfree_skb(skb);
+}
+
+static const struct dect_bearer_ops dect_dmb_ops = {
+	.state		= DECT_MONITOR_BEARER,
+	.rcv		= dect_dmb_rcv,
+};
+
+static struct dect_dmb *dect_dmb_init(struct dect_cell *cell,
+				      struct dect_transceiver *trx1,
+				      struct dect_transceiver *trx2,
+				      const struct dect_channel_desc *chd1,
+				      const struct dect_channel_desc *chd2)
+{
+	struct dect_dmb *dmb;
+
+	dmb = kzalloc(sizeof(*dmb), GFP_ATOMIC);
+	if (dmb == NULL)
+		return NULL;
+	dmb->cell = cell;
+
+	dect_timer_setup(&dmb->wd_timer, dect_dmb_watchdog_timer, dmb);
+	dect_bearer_init(cell, &dmb->rxb1, &dect_dmb_ops,
+			 trx1, chd1, DECT_BEARER_RX, dmb);
+	dect_bearer_init(cell, &dmb->rxb2, &dect_dmb_ops,
+			 trx2, chd2, DECT_BEARER_RX, dmb);
+	dect_bc_init(cell, &dmb->bc);
+
+	list_add_tail(&dmb->list, &cell->dmbs);
+	return dmb;
+}
+
+static void dect_dmb_rcv_request(struct dect_cell *cell,
+				 const struct dect_transceiver_slot *ts,
+				 const struct dect_tail_msg *tm,
+				 struct sk_buff *skb)
+{
+	struct dect_transceiver *trx1, *trx2;
+	struct dect_channel_desc chd1, chd2;
+	struct dect_dmb *dmb;
+
+	if (tm->cctl.fmid != cell->fmid)
+		goto err1;
+	dect_raw_rcv(skb);
+
+	switch (tm->cctl.cmd) {
+	case DECT_CCTRL_ACCESS_REQ:
+	case DECT_CCTRL_BEARER_HANDOVER_REQ:
+	case DECT_CCTRL_CONNECTION_HANDOVER_REQ:
+		break;
+	default:
+		rx_debug(cell, "unhandled DMB request: %llu\n",
+			 (unsigned long long)tm->cctl.cmd);
+		goto err1;
+	}
+
+	rx_debug(cell, "DMB: RCV ACCESS_REQUEST\n");
+
+	/* Select transceivers for RX/TX and reserve resources */
+	memcpy(&chd1, &ts->chd, sizeof(chd1));
+	chd1.pkt   = DECT_PACKET_P32;
+	chd1.b_fmt = DECT_B_UNPROTECTED;
+	trx1 = dect_select_transceiver(cell, &chd1);
+	if (trx1 == NULL)
+		goto err1;
+	dect_transceiver_reserve(&cell->trg, trx1, &chd1);
+
+	dect_tdd_channel_desc(&chd2, &chd1);
+	trx2 = dect_select_transceiver(cell, &chd2);
+	if (trx2 == NULL)
+		goto err2;
+	dect_transceiver_reserve(&cell->trg, trx2, &chd2);
+
+	dmb = dect_dmb_init(cell, trx1, trx2, &chd1, &chd2);
+	if (dmb == NULL)
+		goto err3;
+
+	dect_bearer_enable(&dmb->rxb1);
+	dect_bearer_enable(&dmb->rxb2);
+
+	dect_bearer_timer_add(cell, &dmb->rxb1, &dmb->wd_timer,
+			      DECT_TBC_RFPI_TIMEOUT);
+
+	kfree_skb(skb);
+	return;
+
+err3:
+	dect_transceiver_release(&cell->trg, trx2, &chd2);
+err2:
+	dect_transceiver_release(&cell->trg, trx1, &chd1);
+err1:
+	kfree_skb(skb);
+}
+
+/*
+ * Idle Receiver Control
+ */
+
+static void dect_initiate_scan(struct dect_transceiver *trx,
+			       const struct dect_ari *ari,
+			       const struct dect_ari *ari_mask,
+			       void (*notify)(struct dect_cell *,
+					      struct dect_transceiver *,
+					      enum dect_scan_status))
+{
+	struct dect_irc *irc = trx->irc;
+
+	if (ari != NULL) {
+		memcpy(&irc->ari, ari, sizeof(irc->ari));
+		if (ari_mask != NULL)
+			memcpy(&irc->ari_mask, ari_mask, sizeof(irc->ari_mask));
+		else
+			memset(&irc->ari_mask, 0xff, sizeof(irc->ari_mask));
+	}
+
+	memset(&irc->si, 0, sizeof(irc->si));
+	irc->notify = notify;
+
+	dect_transceiver_enable(trx);
+	dect_set_channel_mode(trx, &trx->slots[DECT_SCAN_SLOT].chd, DECT_SLOT_SCANNING);
+}
+
+static void dect_restart_scan(struct dect_cell *cell,
+			      struct dect_transceiver *trx)
+{
+	struct dect_irc *irc = trx->irc;
+
+	memset(&irc->si, 0, sizeof(irc->si));
+	dect_transceiver_unlock(trx);
+	dect_set_channel_mode(trx, &trx->slots[DECT_SCAN_SLOT].chd, DECT_SLOT_SCANNING);
+}
+
+/* This function controls the transceiver while scanning. It collects the
+ * information requested in struct dect_scan_ctrl and invokes the completion
+ * handler once all information is available.
+ */
+void dect_mac_irc_rcv(struct dect_transceiver *trx, struct sk_buff *skb)
+{
+	struct dect_cell *cell = trx->cell;
+	struct dect_irc *irc = trx->irc;
+	struct dect_tail_msg tm;
+
+	if (dect_parse_tail_msg(&tm, skb) < 0)
+		goto err;
+
+	switch (trx->state) {
+	case DECT_TRANSCEIVER_UNLOCKED:
+		if (tm.type != DECT_TM_TYPE_ID)
+			break;
+		if (dect_ari_masked_cmp(&tm.idi.pari, &irc->ari, &irc->ari_mask))
+			break;
+		memcpy(&irc->idi, &tm.idi, sizeof(irc->idi));
+
+		irc->timeout = 16 * DECT_FRAMES_PER_MULTIFRAME;
+		irc->rssi    = dect_average_rssi(0, DECT_TRX_CB(skb)->rssi);
+		dect_transceiver_confirm(trx);
+		break;
+	case DECT_TRANSCEIVER_LOCK_PENDING:
+		irc->rssi = dect_average_rssi(irc->rssi, DECT_TRX_CB(skb)->rssi);
+		if (dect_parse_tail(skb) == DECT_TI_QT) {
+			dect_bc_update_si(&irc->si, &tm);
+			if (dect_bc_si_cycle_complete(&irc->idi, &irc->si) &&
+			    tm.type == DECT_TM_TYPE_MFN)
+				irc->notify(cell, trx, DECT_SCAN_COMPLETE);
+		}
+		break;
+	default:
+		break;
+	}
+err:
+	kfree_skb(skb);
+}
+
+void dect_mac_irc_tick(struct dect_transceiver *trx)
+{
+	struct dect_cell *cell = trx->cell;
+	struct dect_irc *irc = trx->irc;
+
+	switch (trx->state) {
+	case DECT_TRANSCEIVER_UNLOCKED:
+		/* maintain scan until clock is running */
+		irc->rx_scn = dect_next_carrier(0x3ff, irc->rx_scn);
+		dect_set_carrier(trx, DECT_SCAN_SLOT, irc->rx_scn);
+		break;
+	case DECT_TRANSCEIVER_LOCK_PENDING:
+		irc->si.ssi.pscn = dect_next_carrier(0x3ff, irc->si.ssi.pscn);
+		if (--irc->timeout == 0)
+			irc->notify(cell, trx, DECT_SCAN_TIMEOUT);
+		break;
+	default:
+		break;
+	}
+}
+
+static void dect_scan_bearer_rcv(struct dect_cell *cell,
+				 struct dect_bearer *bearer,
+				 struct sk_buff *skb)
+{
+	struct dect_transceiver *trx = bearer->trx;
+	struct dect_transceiver_slot *ts;
+	enum dect_tail_identifications ti;
+	struct dect_tail_msg tm;
+	bool monitor = false;
+
+	ti = dect_parse_tail(skb);
+	/* A PP uses a special encoding for the first transmission */
+	if (cell->mode == DECT_MODE_FP && ti != DECT_TI_MT_PKT_0)
+		goto out;
+	if (cell->mode == DECT_MODE_PP) {
+		if (cell->flags & DECT_CELL_MONITOR && ti == DECT_TI_MT_PKT_0)
+			monitor = true;
+		else if (ti != DECT_TI_MT)
+			goto out;
+	}
+
+	if (dect_parse_tail_msg(&tm, skb) < 0)
+		goto out;
+
+	ts = &trx->slots[DECT_TRX_CB(skb)->slot];
+	switch (tm.type) {
+	case DECT_TM_TYPE_BCCTRL:
+	case DECT_TM_TYPE_ACCTRL:
+		if (monitor)
+			return dect_dmb_rcv_request(cell, ts, &tm, skb);
+		else
+			return dect_tbc_rcv_request(cell, ts, &tm, skb);
+	default:
+		break;
+	}
+out:
+	kfree_skb(skb);
+}
+
+static void dect_scan_bearer_report_rssi(struct dect_cell *cell,
+					 struct dect_bearer *bearer,
+					 u8 slot, u8 rssi)
+{
+	if (cell->chl == NULL)
+		return;
+	dect_chl_update(cell, cell->chl, &bearer->trx->slots[slot].chd, rssi);
+}
+
+static const struct dect_bearer_ops dect_scan_ops = {
+	.report_rssi	= dect_scan_bearer_report_rssi,
+	.rcv		= dect_scan_bearer_rcv,
+};
+
+static void dect_scan_channel_desc(struct dect_channel_desc *chd)
+{
+	memset(chd, 0, sizeof(*chd));
+	chd->pkt   = DECT_PACKET_P32;
+	chd->b_fmt = DECT_B_UNPROTECTED;
+}
+
+static void dect_chl_scan_channel_desc(struct dect_channel_desc *chd,
+				       const struct dect_channel_list *chl)
+{
+	memset(chd, 0, sizeof(*chd));
+	chd->pkt = chl->pkt;
+	if (chl->pkt == DECT_PACKET_P00)
+		chd->b_fmt = DECT_B_NONE;
+	else
+		chd->b_fmt = DECT_B_UNPROTECTED;
+}
+
+static void dect_scan_bearer_enable(struct dect_transceiver *trx,
+				    const struct dect_channel_desc *chd)
+{
+	trx->slots[chd->slot].bearer = &trx->irc->scan_bearer;
+	dect_set_channel_mode(trx, chd, DECT_SLOT_SCANNING);
+}
+
+static void dect_scan_bearer_disable(struct dect_transceiver *trx,
+				     const struct dect_channel_desc *chd)
+{
+	dect_set_channel_mode(trx, chd, DECT_SLOT_IDLE);
+	trx->slots[chd->slot].bearer = NULL;
+}
+
+static void dect_irc_tx_frame_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_irc *irc = data;
+	struct dect_transceiver *trx = irc->trx;
+	struct dect_channel_desc chd;
+	u8 end;
+
+	irc->tx_scn = dect_next_carrier(cell->si.ssi.rfcars, irc->tx_scn);
+
+	/* Begin a pending channel list update:
+	 *
+	 * The IRC of the first transceiver that reaches a new frame queues the
+	 * channel list. All IRCs then switch the idle normal transmit slots
+	 * to scanning mode and switch all scanning slots to the lists physical
+	 * channel type. The actual update will begin once the receive side
+	 * reaches the same frame.
+	 */
+	if (cell->chl == NULL && cell->chl_next == NULL)
+		cell->chl_next = dect_chl_get_pending(cell);
+
+	if (cell->chl_next != NULL) {
+		dect_chl_scan_channel_desc(&chd, cell->chl_next);
+		dect_foreach_receive_slot(chd.slot, end, cell) {
+			if (trx->slots[chd.slot].state != DECT_SLOT_IDLE &&
+			    trx->slots[chd.slot].state != DECT_SLOT_SCANNING)
+				continue;
+			if (!dect_transceiver_channel_available(trx, &chd))
+				continue;
+
+			dect_scan_bearer_enable(trx, &chd);
+		}
+		dect_foreach_transmit_slot(chd.slot, end, cell) {
+			if (trx->slots[chd.slot].state != DECT_SLOT_IDLE)
+				continue;
+			if (!dect_transceiver_channel_available(trx, &chd))
+				continue;
+			dect_scan_bearer_enable(trx, &chd);
+		}
+	} else if (cell->chl == NULL) {
+		/* Switch back primary, secondary and tertiary scan to proper
+		 * packet format and disable scan on remaining transceivers
+		 * after the channel list update is complete.
+		 */
+		dect_scan_channel_desc(&chd);
+		dect_foreach_receive_slot(chd.slot, end, cell) {
+			if (trx->slots[chd.slot].state != DECT_SLOT_SCANNING)
+				continue;
+
+			if (trx->index < 3)
+				dect_scan_bearer_enable(trx, &chd);
+			else
+				dect_scan_bearer_disable(trx, &chd);
+		}
+
+		/* In monitor mode, transmit slots keep scanning for FP setup
+		 * attempts.
+		 */
+		if (!(cell->flags & DECT_CELL_MONITOR)) {
+			dect_foreach_transmit_slot(chd.slot, end, cell) {
+				if (trx->slots[chd.slot].state != DECT_SLOT_SCANNING)
+					continue;
+				dect_scan_bearer_disable(trx, &chd);
+			}
+		}
+	}
+
+	dect_timer_add(cell, &irc->tx_frame_timer, DECT_TIMER_TX, 1, 0);
+}
+
+static void dect_irc_rx_frame_timer(struct dect_cell *cell, void *data)
+{
+	struct dect_irc *irc = data;
+
+	/* Update the list status at the end of a frame in case of an
+	 * active update or activate an update before a new frame begins.
+	 */
+	if (cell->chl != NULL)
+		dect_chl_update_carrier(cell, irc->rx_scn);
+	else if (cell->chl_next != NULL) {
+		cell->chl = cell->chl_next;
+		cell->chl_next = NULL;
+		chl_debug(cell, cell->chl, "begin update\n");
+	}
+
+	irc->rx_scn = dect_next_carrier(cell->si.ssi.rfcars, irc->rx_scn);
+	dect_timer_add(cell, &irc->rx_frame_timer, DECT_TIMER_RX, 1, 23);
+}
+
+/* Primary, secondary and tertiary scan: the secondary scan lags behind the
+ * primary scan by 6 TDMA frames, the tertiary scan by 3 TDMA frames.
+ *
+ * Additional transceivers don't scan for setup attempts, however they each
+ * cover a different carrier in order to speed up channel list construction.
+ */
+static const u8 scn_off_tbl[10] = {
+	[0]	= 0,
+	[1]	= 6,
+	[2]	= 3,
+	[3]	= 8,
+	[4]	= 1,
+	[5]	= 4,
+	[6]	= 9,
+	[7]	= 2,
+	[8]	= 5,
+	[9]	= 7,
+};
+
+static void dect_irc_enable(struct dect_cell *cell, struct dect_irc *irc)
+{
+	struct dect_transceiver *trx = irc->trx;
+	struct dect_channel_desc chd;
+	u8 end, scn_off, scn;
+
+	if (trx->index >= 10)
+		return;
+
+	scn_off = scn_off_tbl[trx->index];
+	scn = dect_carrier_sub(cell->si.ssi.rfcars, cell->si.ssi.pscn, scn_off);
+	irc->rx_scn = scn;
+	irc->tx_scn = scn;
+
+	if (trx->index < 3) {
+		/* Set all idle slots to scanning */
+		dect_scan_channel_desc(&chd);
+		dect_foreach_receive_slot(chd.slot, end, cell) {
+			if (trx->slots[chd.slot].state != DECT_SLOT_IDLE)
+				continue;
+			if (!dect_transceiver_channel_available(trx, &chd))
+				continue;
+			dect_scan_bearer_enable(trx, &chd);
+		}
+
+		if (cell->flags & DECT_CELL_MONITOR) {
+			dect_foreach_transmit_slot(chd.slot, end, cell) {
+				if (!dect_transceiver_channel_available(trx, &chd))
+					continue;
+				dect_scan_bearer_enable(trx, &chd);
+			}
+		}
+	}
+
+	/* Start frame timers */
+	dect_timer_add(cell, &irc->tx_frame_timer, DECT_TIMER_TX, 1, 0);
+	dect_timer_add(cell, &irc->rx_frame_timer, DECT_TIMER_RX, 0, 23);
+}
+
+static void dect_irc_disable(struct dect_cell *cell, struct dect_irc *irc)
+{
+	struct dect_transceiver *trx = irc->trx;
+	u8 slot;
+
+	dect_timer_del(&irc->rx_frame_timer);
+	dect_timer_del(&irc->tx_frame_timer);
+
+	dect_foreach_slot(slot) {
+		if (trx->slots[slot].state != DECT_SLOT_SCANNING)
+			continue;
+		dect_scan_bearer_disable(trx, &trx->slots[slot].chd);
+	}
+}
+
+static struct dect_irc *dect_irc_init(struct dect_cell *cell,
+				      struct dect_transceiver *trx)
+{
+	struct dect_irc *irc;
+
+	irc = kzalloc(sizeof(*irc), GFP_KERNEL);
+	if (irc == NULL)
+		return NULL;
+
+	irc->cell = cell;
+	dect_timer_setup(&irc->rx_frame_timer, dect_irc_rx_frame_timer, irc);
+	dect_timer_setup(&irc->tx_frame_timer, dect_irc_tx_frame_timer, irc);
+	irc->scan_bearer.ops   = &dect_scan_ops;
+	irc->scan_bearer.irc   = irc;
+	irc->scan_bearer.trx   = trx;
+	irc->scan_bearer.mode  = DECT_BEARER_RX;
+	irc->scan_bearer.state = DECT_BEARER_ENABLED;
+	irc->trx = trx;
+	trx->irc = irc;
+	return irc;
+}
+
+static void dect_lock_fp(struct dect_cell *cell, struct dect_transceiver *trx,
+			 enum dect_scan_status status)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+	struct dect_irc *irc = trx->irc;
+	struct dect_si *si = &irc->si;
+	struct dect_channel_desc chd;
+	struct dect_dbc *dbc;
+
+	switch (status) {
+	case DECT_SCAN_FAIL:
+	case DECT_SCAN_TIMEOUT:
+		return dect_restart_scan(cell, trx);
+	case DECT_SCAN_COMPLETE:
+		break;
+	}
+
+	dect_set_channel_mode(trx, &trx->slots[DECT_SCAN_SLOT].chd, DECT_SLOT_IDLE);
+
+	chd.slot    = si->ssi.sn + (si->ssi.nr ? DECT_HALF_FRAME_SIZE : 0);
+	chd.carrier = si->ssi.cn;
+	chd.pkt     = DECT_PACKET_P00;
+	chd.b_fmt   = DECT_B_NONE;
+
+	if (!dect_transceiver_channel_available(trx, &chd))
+		return dect_restart_scan(cell, trx);
+
+	if (cell->mode != DECT_MODE_FP) {
+		memcpy(&cell->idi, &irc->idi, sizeof(cell->idi));
+		cell->fmid = dect_build_fmid(&cell->idi);
+		memcpy(&cell->si, si, sizeof(cell->si));
+
+		/* Q-channel information is broadcast in frame 8 */
+		dect_timer_synchronize_framenum(cell, DECT_Q_CHANNEL_FRAME);
+		dect_timer_synchronize_mfn(cell, si->mfn.num);
+
+		/* Lock framing based on slot position and create DBC */
+		dect_transceiver_lock(trx, chd.slot);
+		dect_dbc_init(cell, &chd);
+
+		clh->ops->mac_info_ind(clh, &cell->idi, &cell->si);
+	} else {
+		/* secondary transceiver */
+		dbc = dect_dbc_get(cell);
+		if (!(cell->flags & DECT_CELL_SLAVE) &&
+		    (dbc == NULL ||
+		     dbc->bearer.chd.slot    != chd.slot ||
+		     dbc->bearer.chd.carrier != chd.carrier))
+			return dect_restart_scan(cell, trx);
+
+		dect_transceiver_lock(trx, chd.slot);
+
+		/* Lock to the primary dummy bearer to keep the radio synchronized */
+		/* FIXME: do this cleanly */
+		dect_set_channel_mode(trx, &chd, DECT_SLOT_RX);
+		dect_set_flags(trx, chd.slot, DECT_SLOT_SYNC);
+		dect_set_carrier(trx, chd.slot, chd.carrier);
+	}
+
+	/* Enable IRC */
+	dect_irc_enable(cell, irc);
+}
+
+static void dect_attempt_lock(struct dect_cell *cell,
+			      struct dect_transceiver *trx)
+{
+	dect_initiate_scan(trx, &cell->idi.pari, NULL, dect_lock_fp);
+}
+
+/*
+ * Transmission: A- and B-Field MUXes
+ */
+
+static struct sk_buff *dect_u_mux(struct dect_cell *cell,
+				  struct dect_bearer *bearer)
+{
+	struct sk_buff *skb = NULL;
+	struct dect_tbc *tbc;
+	u8 b_field_size;
+
+	if (bearer->ops->state == DECT_TRAFFIC_BEARER) {
+		tbc = bearer->tbc;
+		skb = tbc->b_tx_skb;
+		tbc->b_tx_skb = NULL;
+	}
+
+	if (skb == NULL) {
+		b_field_size = dect_b_field_size(&bearer->chd);
+		skb = alloc_skb(b_field_size, GFP_ATOMIC);
+		if (skb == NULL)
+			return NULL;
+		skb_put(skb, b_field_size);
+		memset(skb->data, 0xff, b_field_size);
+		DECT_B_CB(skb)->id = DECT_BI_UTYPE_0;
+	}
+	return skb;
+}
+
+static struct sk_buff *dect_eu_mux(struct dect_cell *cell,
+				   struct dect_bearer *bearer)
+{
+	return dect_u_mux(cell, bearer);
+}
+
+static struct sk_buff *dect_b_map(struct dect_cell *cell,
+				  struct dect_bearer *bearer)
+{
+	return dect_eu_mux(cell, bearer);
+}
+
+#define tmux_debug(cell, fmt, args...) \
+	tx_debug(cell, "%s T-MUX: " fmt, \
+		 cell->mode == DECT_MODE_FP ? "FT" : "PT", ## args)
+
+/**
+ * dect_pt_t_mux - DECT T-MUX for PT transmissions
+ *
+ * @cell:	DECT cell
+ * @bearer:	MAC bearer
+ *
+ * The PT T-MUX sequence is used by PTs for all traffic bearers in connection
+ * oriented services and is defined as:
+ *
+ * Even frames: 	M_T, C_T, N_T
+ * Uneven frames:	N_T
+ *
+ * Exception: M_T tails containing "bearer request" or "bearer release"
+ * messages may be placed in any frame.
+ */
+static struct sk_buff *dect_pt_t_mux(struct dect_cell *cell,
+				     struct dect_bearer *bearer)
+{
+	struct dect_tbc *tbc = NULL;
+	struct sk_buff *skb;
+
+	switch (bearer->ops->state) {
+	case DECT_DUMMY_BEARER:
+	case DECT_CL_BEARER:
+	case DECT_MONITOR_BEARER:
+		WARN_ON(0);
+		break;
+	case DECT_TRAFFIC_BEARER:
+		tbc = bearer->tbc;
+		tbc->cs_tx_ok = false;
+		break;
+	}
+
+	if ((dect_framenum(cell, DECT_TIMER_TX) & 0x1) == 0) {
+		skb = skb_dequeue(&bearer->m_tx_queue);
+		if (skb != NULL) {
+			tmux_debug(cell, "M-channel\n");
+			return skb;
+		}
+		if (tbc != NULL && tbc->cs_tx_skb != NULL) {
+			skb = tbc->cs_tx_skb;
+			tbc->cs_tx_skb = NULL;
+			tbc->cs_tx_ok = true;
+			tmux_debug(cell, "C-channel\n");
+			return skb;
+		}
+	} else {
+		skb = skb_peek(&bearer->m_tx_queue);
+		if (skb != NULL && skb->priority == DECT_MT_HIGH_PRIORITY) {
+			tmux_debug(cell, "M-channel (high priority)\n");
+			skb_unlink(skb, &bearer->m_tx_queue);
+			return skb;
+		}
+	}
+
+	tmux_debug(cell, "N-channel\n");
+	return dect_bc_dequeue(cell, bearer, &tbc->bc, DECT_MC_N);
+}
+
+/**
+ * dect_rfp_t_mux - DECT T-MUX for RFP transmissions
+ *
+ * @cell:	DECT cell
+ * @bearer:	MAC bearer
+ *
+ * The RFP T-MUX sequence is used for all RFP transmissions and is defined as:
+ *
+ * Frame 8:		Q_T
+ * Frame 14:		N_T
+ * Other even frames: 	P_T, N_T
+ * Uneven frames:	M_T, C_T, N_T
+ *
+ * Exception: M_T tails sent in response to "bearer request" messages or during
+ * bearer release may be placed in any frame.
+ */
+static struct sk_buff *dect_rfp_t_mux(struct dect_cell *cell,
+				      struct dect_bearer *bearer)
+{
+	u8 framenum = dect_framenum(cell, DECT_TIMER_TX);
+	struct dect_tbc *tbc = NULL;
+	struct dect_bc *bc = NULL;
+	struct sk_buff *skb;
+
+	switch (bearer->ops->state) {
+	case DECT_DUMMY_BEARER:
+		bc = &bearer->dbc->bc;
+		break;
+	case DECT_TRAFFIC_BEARER:
+		tbc = bearer->tbc;
+		tbc->cs_tx_ok = false;
+		bc = &bearer->tbc->bc;
+		break;
+	case DECT_CL_BEARER:
+	case DECT_MONITOR_BEARER:
+		break;
+	}
+
+	if ((framenum & 0x1) == 0) {
+		skb = skb_peek(&bearer->m_tx_queue);
+		if (skb != NULL && skb->priority == DECT_MT_HIGH_PRIORITY) {
+			tmux_debug(cell, "M-channel (high priority)\n");
+			skb_unlink(skb, &bearer->m_tx_queue);
+			return skb;
+		}
+
+		if (framenum == 8) {
+			tmux_debug(cell, "Q-channel\n");
+			return dect_bc_dequeue(cell, bearer, bc, DECT_MC_Q);
+		}
+		if (framenum == 14) {
+			tmux_debug(cell, "N-channel\n");
+			return dect_bc_dequeue(cell, bearer, bc, DECT_MC_N);
+		}
+
+		skb = dect_bc_dequeue(cell, bearer, bc, DECT_MC_P);
+		if (skb != NULL) {
+			tmux_debug(cell, "P-channel\n");
+			return skb;
+		}
+	} else {
+		skb = skb_dequeue(&bearer->m_tx_queue);
+		if (skb != NULL) {
+			tmux_debug(cell, "M-channel\n");
+			return skb;
+		}
+		if (tbc != NULL && tbc->cs_tx_skb != NULL) {
+			skb = tbc->cs_tx_skb;
+			tbc->cs_tx_skb = NULL;
+			tbc->cs_tx_ok = true;
+			tmux_debug(cell, "C-channel\n");
+			return skb;
+		}
+	}
+
+	tmux_debug(cell, "N-channel\n");
+	return dect_bc_dequeue(cell, bearer, bc, DECT_MC_N);
+}
+
+/**
+ * dect_a_map - DECT A-Field mapping
+ *
+ * @cell:	DECT cell
+ * @bearer:	MAC bearer
+ *
+ * Combine the H-, T- and RA-Fields into the A-Field.
+ */
+static struct sk_buff *dect_a_map(struct dect_cell *cell,
+				  struct dect_bearer *bearer)
+{
+	struct sk_buff *skb;
+
+	switch (cell->mode) {
+	case DECT_MODE_PP:
+		skb = dect_pt_t_mux(cell, bearer);
+		break;
+	case DECT_MODE_FP:
+		skb = dect_rfp_t_mux(cell, bearer);
+		break;
+	default:
+		skb = NULL;
+		break;
+	}
+
+	if (skb == NULL)
+		return NULL;
+
+	/* Append empty RA-Field */
+	memset(skb_put(skb, DECT_RA_FIELD_SIZE), 0, DECT_RA_FIELD_SIZE);
+
+	/* Prepend Header field */
+	skb_push(skb, DECT_HDR_FIELD_SIZE);
+	skb->data[DECT_HDR_FIELD_OFF] = DECT_A_CB(skb)->id;
+	skb->data[DECT_HDR_FIELD_OFF] |= bearer->q;
+	bearer->q = 0;
+	return skb;
+}
+
+static struct sk_buff *dect_raw_tx_peek(struct dect_cell *cell)
+{
+	struct dect_timer_base *base = &cell->timer_base[DECT_TIMER_TX];
+	struct dect_skb_trx_cb *cb;
+	struct sk_buff *skb;
+
+	skb = skb_peek(&cell->raw_tx_queue);
+	if (skb == NULL)
+		return NULL;
+	cb = DECT_TRX_CB(skb);
+
+	if ((!cb->mfn || cb->mfn == base->mfn) &&
+	    (!cb->frame || cb->frame == base->framenum) &&
+	    cb->slot == dect_slotnum(cell, DECT_TIMER_TX))
+		return skb;
+
+	return NULL;
+}
+
+static void dect_raw_tx_configure(struct dect_cell *cell,
+				  struct dect_transceiver *trx,
+				  struct dect_transceiver_slot *ts)
+{
+	if (dect_raw_tx_peek(cell)) {
+		if (ts->state == DECT_SLOT_RX) {
+			tx_debug(cell, "enable raw TX\n");
+			dect_set_channel_mode(trx, &ts->chd, DECT_SLOT_TX);
+			dect_set_carrier(trx, ts->chd.slot, ts->chd.carrier);
+			ts->priv_flags |= DECT_SLOT_RAW_TX;
+		}
+	} else if (ts->priv_flags & DECT_SLOT_RAW_TX) {
+		tx_debug(cell, "disable raw TX\n");
+		dect_set_channel_mode(trx, &ts->chd, DECT_SLOT_RX);
+		dect_set_carrier(trx, ts->chd.slot, ts->chd.carrier);
+		ts->priv_flags &= ~DECT_SLOT_RAW_TX;
+	}
+}
+
+static struct sk_buff *dect_raw_tx(struct dect_cell *cell)
+{
+	struct sk_buff *skb;
+
+	skb = dect_raw_tx_peek(cell);
+	if (skb == NULL)
+		return NULL;
+
+	tx_debug(cell, "raw transmit\n");
+	skb_unlink(skb, &cell->raw_tx_queue);
+	return skb;
+}
+
+/**
+ * dect_d_map - DECT D-Field mapping
+ *
+ * @cell:	DECT cell
+ * @bearer:	MAC bearer
+ *
+ * Combine the A- and B-Fields from their respective MAPs into one D-Field.
+ */
+static struct sk_buff *dect_d_map(struct dect_cell *cell,
+				  struct dect_bearer *bearer)
+{
+	struct sk_buff *skb_a, *skb_b, *skb;
+
+	skb = dect_raw_tx(cell);
+	if (skb != NULL)
+		return skb;
+
+	skb_a = dect_a_map(cell, bearer);
+	if (skb_a == NULL)
+		goto err1;
+
+	if (bearer->chd.pkt != DECT_PACKET_P00) {
+		skb_b = dect_b_map(cell, bearer);
+		if (skb_b == NULL)
+			goto err2;
+		skb_a->data[DECT_HDR_BA_OFF] |= DECT_B_CB(skb_b)->id;
+
+		skb = skb_append_frag(skb_a, skb_b);
+		if (skb_linearize(skb) < 0) {
+			kfree_skb(skb);
+			skb = NULL;
+		}
+	} else {
+		skb_a->data[DECT_HDR_BA_OFF] |= DECT_BI_NONE;
+		skb = skb_a;
+	}
+
+	return skb;
+
+err2:
+	kfree_skb(skb_a);
+err1:
+	return NULL;
+}
+
+static void dect_mac_xmit_frame(struct dect_transceiver *trx,
+				struct dect_transceiver_slot *ts)
+{
+	struct dect_cell *cell = trx->cell;
+	struct dect_bearer *bearer = ts->bearer;
+	struct sk_buff *skb;
+
+	skb = dect_d_map(cell, bearer);
+	if (skb == NULL)
+		return;
+
+	tx_debug(cell, "%s: Q1: %d Q2: %d A/B: %02x carrier: %u PSCN: %u\n",
+		 trx->name,
+		 skb->data[DECT_HDR_Q1_OFF] & DECT_HDR_Q1_FLAG ? 1 : 0,
+		 skb->data[DECT_HDR_Q2_OFF] & DECT_HDR_Q2_FLAG ? 1 : 0,
+		 skb->data[DECT_HDR_TA_OFF] &
+		 (DECT_HDR_TA_MASK | DECT_HDR_BA_MASK),
+		 ts->chd.carrier, cell->si.ssi.pscn);
+
+	switch (cell->mode) {
+	case DECT_MODE_FP:
+		skb->mac_len = sizeof(dect_fp_preamble);
+		memcpy(skb_mac_header(skb), dect_fp_preamble, skb->mac_len);
+		break;
+	case DECT_MODE_PP:
+		skb->mac_len = sizeof(dect_pp_preamble);
+		memcpy(skb_mac_header(skb), dect_pp_preamble, skb->mac_len);
+		break;
+	}
+
+	DECT_TRX_CB(skb)->trx   = trx;
+	DECT_TRX_CB(skb)->slot  = ts->chd.slot;
+	DECT_TRX_CB(skb)->frame = dect_framenum(cell, DECT_TIMER_TX);
+	DECT_TRX_CB(skb)->mfn	= dect_mfn(cell, DECT_TIMER_TX);
+	dect_raw_rcv(skb);
+
+	dect_transceiver_tx(trx, skb);
+}
+
+void dect_mac_rcv(struct dect_transceiver *trx,
+		  struct dect_transceiver_slot *ts,
+		  struct sk_buff *skb)
+{
+	struct dect_cell *cell = trx->cell;
+
+	DECT_TRX_CB(skb)->frame = dect_framenum(cell, DECT_TIMER_RX);
+	DECT_TRX_CB(skb)->mfn	= dect_mfn(cell, DECT_TIMER_RX);
+
+	/* TX bearers can temporarily switch to RX mode for noise measurement */
+	if (ts->bearer != NULL &&
+	    ts->bearer->mode == DECT_BEARER_RX) {
+		rx_debug(cell, "%s: Q1: %d Q2: %d A/B: %02x carrier: %u %s%s%s",
+			 trx->name,
+			 skb->data[DECT_HDR_Q1_OFF] & DECT_HDR_Q1_FLAG ? 1 : 0,
+			 skb->data[DECT_HDR_Q2_OFF] & DECT_HDR_Q2_FLAG ? 1 : 0,
+			 skb->data[DECT_HDR_TA_OFF] &
+			 (DECT_HDR_TA_MASK | DECT_HDR_BA_MASK), ts->chd.carrier,
+			 DECT_TRX_CB(skb)->csum & DECT_CHECKSUM_A_CRC_OK ?
+			 "" : "A-CRC: 0 ",
+			 ts->chd.pkt == DECT_PACKET_P00 ||
+			 DECT_TRX_CB(skb)->csum & DECT_CHECKSUM_X_CRC_OK ?
+			 "" : "X-CRC: 0 ",
+			 ts->chd.pkt == DECT_PACKET_P00 ||
+			 DECT_TRX_CB(skb)->csum & DECT_CHECKSUM_Z_CRC_OK ?
+			 "" : "Z-CRC: 0 ");
+
+		ts->bearer->ops->rcv(cell, ts->bearer, skb);
+	} else
+		kfree_skb(skb);
+}
+
+void dect_mac_report_rssi(struct dect_transceiver *trx,
+			  struct dect_transceiver_slot *ts,
+			  u8 rssi)
+{
+	struct dect_cell *cell = trx->cell;
+
+	if (ts->bearer == NULL) {
+		pr_debug("%s: RSSI: slot: %u carrier: %u state: %u no bearer\n",
+			 trx->name, ts->chd.slot, ts->chd.carrier, ts->state);
+		return;
+	}
+	if (ts->bearer->state != DECT_BEARER_ENABLED)
+		dect_tx_bearer_report_rssi(cell, ts->bearer, rssi);
+	else if (ts->bearer->ops->report_rssi != NULL)
+		ts->bearer->ops->report_rssi(cell, ts->bearer, ts->chd.slot, rssi);
+}
+
+static void dect_fp_state_process(struct dect_cell *cell)
+{
+	if (list_empty(&cell->dbcs))
+		dect_dbc_init(cell, NULL);
+
+	if (time_before(cell->bfs_xmit_stamp + HZ, jiffies)) {
+		dect_cell_schedule_page(cell, (1 << DECT_TM_TYPE_BFS) |
+					      (1 << DECT_TM_TYPE_BD));
+		cell->bfs_xmit_stamp = jiffies;
+	}
+}
+
+static bool dect_pp_idle_timeout(const struct dect_cell *cell)
+{
+	u32 mfn;
+
+	mfn = dect_mfn_add(cell->timer_sync_stamp, DECT_CELL_TIMER_RESYNC_TIMEOUT);
+	if (dect_mfn_after(dect_mfn(cell, DECT_TIMER_RX), mfn) ||
+	    time_after(jiffies, cell->a_rcv_stamp + DECT_CELL_A_RCV_TIMEOUT) ||
+	    time_after(jiffies, cell->nt_rcv_stamp + DECT_CELL_NT_RCV_TIMEOUT)) {
+		pr_debug("timeout, unlock, a: %ld nt: %ld mfn: %d\n",
+			 jiffies - cell->a_rcv_stamp, jiffies - cell->nt_rcv_stamp,
+			 dect_mfn(cell, DECT_TIMER_RX) - cell->timer_sync_stamp);
+		return true;
+	}
+
+	return false;
+}
+
+static void dect_pp_state_process(struct dect_cell *cell)
+{
+	struct dect_transceiver *trx = cell->trg.trx[0];
+	struct dect_dbc *dbc, *next;
+
+	if (cell->tbc_num_est || !list_empty(&cell->dmbs)) {
+		/* Active locked state: release DBCs */
+		list_for_each_entry_safe(dbc, next, &cell->dbcs, list)
+			dect_dbc_release(dbc);
+	} else if (trx->state == DECT_TRANSCEIVER_LOCKED) {
+		/* Idle locked state: install DBC if none present */
+		if (list_empty(&cell->dbcs) &&
+		    list_empty(&cell->tbcs) &&
+		    list_empty(&cell->dmbs)) {
+			struct dect_channel_desc chd;
+
+			chd.pkt		= DECT_PACKET_P00;
+			chd.b_fmt	= DECT_B_NONE;
+			chd.slot	= cell->tbc_last_chd.slot;
+			chd.carrier	= cell->tbc_last_chd.carrier;
+
+			dect_dbc_init(cell, &chd);
+		}
+
+		if (!list_empty(&cell->dbcs) && dect_pp_idle_timeout(cell)) {
+			list_for_each_entry_safe(dbc, next, &cell->dbcs, list)
+				dect_dbc_release(dbc);
+
+			dect_irc_disable(cell, trx->irc);
+			dect_transceiver_unlock(trx);
+
+			/* Clear system information */
+			memset(&cell->si, 0, sizeof(cell->si));
+			dect_cell_mac_info_ind(cell);
+
+			dect_attempt_lock(cell, trx);
+		}
+	}
+}
+
+static void dect_cell_state_process(struct dect_cell *cell)
+{
+	if (list_empty(&cell->chanlists) && list_empty(&cell->chl_pending)) {
+		dect_chl_schedule_update(cell, DECT_PACKET_P00);
+		dect_chl_schedule_update(cell, DECT_PACKET_P32);
+	}
+
+	switch (cell->mode) {
+	case DECT_MODE_FP:
+		return dect_fp_state_process(cell);
+	case DECT_MODE_PP:
+		return dect_pp_state_process(cell);
+	}
+}
+
+static void dect_cluster_time_ind(struct dect_cell *cell,
+				  enum dect_timer_bases base,
+				  u8 slot)
+{
+	struct dect_cluster_handle *clh = cell->handle.clh;
+
+	clh->ops->time_ind(clh, base,
+			   dect_mfn(cell, base),
+			   dect_framenum(cell, base),
+			   slot);
+}
+
+void dect_mac_rx_tick(struct dect_transceiver_group *grp, u8 slot)
+{
+	struct dect_cell *cell = container_of(grp, struct dect_cell, trg);
+
+	dect_run_timers(cell, DECT_TIMER_RX);
+	dect_timer_base_update(cell, DECT_TIMER_RX, slot);
+	dect_cluster_time_ind(cell, DECT_TIMER_RX, slot);
+}
+
+void dect_mac_tx_tick(struct dect_transceiver_group *grp, u8 slot)
+{
+	struct dect_cell *cell = container_of(grp, struct dect_cell, trg);
+	struct dect_transceiver_slot *ts;
+	struct dect_transceiver *trx;
+	u8 scn;
+
+	/* TX timers run at the beginning of a slot, update the time first */
+	dect_timer_base_update(cell, DECT_TIMER_TX, slot);
+	dect_cluster_time_ind(cell, DECT_TIMER_TX, slot);
+	dect_run_timers(cell, DECT_TIMER_TX);
+
+	dect_cell_state_process(cell);
+
+	dect_foreach_transceiver(trx, grp) {
+		if (trx->state != DECT_TRANSCEIVER_LOCKED)
+			continue;
+		ts = &trx->slots[slot];
+
+		dect_raw_tx_configure(cell, trx, ts);
+
+		switch (ts->state) {
+		case DECT_SLOT_SCANNING:
+			scn = trx->irc->tx_scn;
+
+			if (cell->flags & DECT_CELL_MONITOR &&
+			    slot >= DECT_HALF_FRAME_SIZE)
+				scn = dect_prev_carrier(cell->si.ssi.rfcars, scn);
+
+			dect_set_carrier(trx, slot, scn);
+			break;
+		case DECT_SLOT_TX:
+			dect_mac_xmit_frame(trx, ts);
+			break;
+		}
+	}
+
+	if (slot == DECT_FRAME_SIZE - 1)
+		cell->si.ssi.pscn = dect_next_carrier(cell->si.ssi.rfcars,
+						      cell->si.ssi.pscn);
+}
+
+static void dect_fp_init_primary(struct dect_cell *cell,
+				 struct dect_transceiver *trx)
+{
+	dect_transceiver_enable(trx);
+	dect_irc_enable(cell, trx->irc);
+}
+
+static void dect_cell_enable_transceiver(struct dect_cell *cell,
+					 struct dect_transceiver *trx)
+{
+	/* The primary transceiver of a FP is a timing master. All other
+	 * transceivers need to synchronize.
+	 */
+	if (trx->index == 0 && cell->mode == DECT_MODE_FP &&
+	    !(cell->flags & DECT_CELL_SLAVE)) {
+		trx->mode = DECT_TRANSCEIVER_MASTER;
+		dect_fp_init_primary(cell, trx);
+	} else {
+		trx->mode = DECT_TRANSCEIVER_SLAVE;
+		dect_attempt_lock(cell, trx);
+	}
+}
+
+static int dect_cell_preload(const struct dect_cell_handle *ch,
+			     const struct dect_ari *pari, u8 rpn,
+			     const struct dect_si *si)
+{
+	struct dect_cell *cell = dect_cell(ch);
+
+	/* Initialise identity */
+	spin_lock_bh(&cell->lock);
+	cell->idi.e = false;
+	memcpy(&cell->idi.pari, pari, sizeof(cell->idi.pari));
+	cell->idi.rpn = rpn;
+	cell->fmid = dect_build_fmid(&cell->idi);
+
+	cell->si.ssi.rfcars = 0x3ff;
+	memcpy(&cell->si.erfc, &si->erfc, sizeof(cell->si.erfc));
+	memcpy(&cell->si.fpc, &si->fpc, sizeof(cell->si.fpc));
+	memcpy(&cell->si.efpc, &si->efpc, sizeof(cell->si.efpc));
+	memcpy(&cell->si.efpc2, &si->efpc2, sizeof(cell->si.efpc2));
+	memcpy(&cell->si.mfn, &si->mfn, sizeof(cell->si.mfn));
+	memcpy(cell->si.sari, si->sari, sizeof(cell->si.sari));
+	cell->si.num_saris = si->num_saris;
+	dect_timer_synchronize_mfn(cell, cell->si.mfn.num);
+	spin_unlock_bh(&cell->lock);
+	return 0;
+}
+
+static int dect_cell_enable(const struct dect_cell_handle *ch)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_transceiver *trx;
+
+	cell->state |= DECT_CELL_ENABLED;
+	dect_foreach_transceiver(trx, &cell->trg) {
+		dect_cell_enable_transceiver(cell, trx);
+		if (cell->mode == DECT_MODE_PP)
+			break;
+	}
+	return 0;
+}
+
+static void dect_scan_report(struct dect_cell *cell,
+			     struct dect_transceiver *trx,
+			     enum dect_scan_status status)
+{
+	const struct dect_cluster_handle *clh = cell->handle.clh;
+	const struct dect_irc *irc = trx->irc;
+	struct dect_scan_result res;
+
+	switch (status) {
+	case DECT_SCAN_FAIL:
+		break;
+	case DECT_SCAN_TIMEOUT:
+		pr_debug("timeout\n");
+	case DECT_SCAN_COMPLETE:
+		res.lreq = irc->lreq;
+		res.rssi = irc->rssi;
+		res.idi  = irc->idi;
+		res.si   = irc->si;
+		clh->ops->scan_report(clh, &res);
+		break;
+	}
+
+	return dect_restart_scan(cell, trx);
+}
+
+static int dect_cell_scan(const struct dect_cell_handle *ch,
+			  const struct dect_llme_req *lreq,
+			  const struct dect_ari *pari,
+			  const struct dect_ari *pari_mask)
+{
+	struct dect_cell *cell = dect_cell(ch);
+	struct dect_transceiver *trx = cell->trg.trx[0];
+
+	if (trx == NULL)
+		return -ENODEV;
+	// FIXME
+	memcpy(&trx->irc->lreq, lreq, sizeof(trx->irc->lreq));
+	dect_initiate_scan(trx, pari, pari_mask, dect_scan_report);
+	return 0;
+}
+
+static int dect_cell_set_mode(const struct dect_cell_handle *ch,
+			      enum dect_cluster_modes mode)
+{
+	struct dect_cell *cell = dect_cell(ch);
+
+	cell->mode = mode;
+	return 0;
+}
+
+static void dect_cell_page_req(const struct dect_cell_handle *ch,
+			       struct sk_buff *skb)
+{
+	struct dect_cell *cell = dect_cell(ch);
+
+	DECT_BMC_CB(skb)->stamp = dect_mfn(cell, DECT_TIMER_TX);
+	dect_queue_page(cell, skb);
+}
+
+static const struct dect_csf_ops dect_csf_ops = {
+	.set_mode		= dect_cell_set_mode,
+	.scan			= dect_cell_scan,
+	.enable			= dect_cell_enable,
+	.preload		= dect_cell_preload,
+	.page_req		= dect_cell_page_req,
+	.tbc_establish_req	= dect_tbc_establish_req,
+	.tbc_establish_res	= dect_tbc_establish_res,
+	.tbc_dis_req		= dect_tbc_dis_req,
+	.tbc_enc_key_req	= dect_tbc_enc_key_req,
+	.tbc_enc_eks_req	= dect_tbc_enc_eks_req,
+	.tbc_enc_req		= dect_tbc_enc_req,
+	.tbc_data_req		= dect_tbc_data_req,
+};
+
+static int dect_cell_bind(struct dect_cell *cell, u8 index)
+{
+	struct dect_cluster_handle *clh;
+	struct dect_cluster *cl;
+
+	if (cell->flags & DECT_CELL_CCP) {
+		clh = dect_ccp_cell_init(cell, index);
+		if (IS_ERR(clh))
+			return PTR_ERR(clh);
+	} else {
+		cl = dect_cluster_get_by_index(index);
+		if (cl == NULL)
+			return -ENOENT;
+		clh = &cl->handle;
+	}
+
+	return clh->ops->bind(clh, &cell->handle);
+}
+
+static void dect_cell_shutdown(struct dect_cell *cell)
+{
+	struct dect_cluster_handle *clh = cell->handle.clh;
+	struct dect_transceiver *trx;
+
+	if (clh != NULL)
+		clh->ops->unbind(clh, &cell->handle);
+
+	dect_foreach_transceiver(trx, &cell->trg)
+		dect_cell_detach_transceiver(cell, trx);
+	dect_cell_bmc_disable(cell);
+	skb_queue_purge(&cell->raw_tx_queue);
+}
+
+/**
+ * dect_mac_init_cell - Initialize a DECT cell
+ */
+static void dect_cell_init(struct dect_cell *cell)
+{
+	spin_lock_init(&cell->lock);
+	INIT_LIST_HEAD(&cell->bcs);
+	INIT_LIST_HEAD(&cell->dbcs);
+	INIT_LIST_HEAD(&cell->tbcs);
+	INIT_LIST_HEAD(&cell->dmbs);
+	INIT_LIST_HEAD(&cell->chl_pending);
+	INIT_LIST_HEAD(&cell->chanlists);
+	dect_timer_base_init(cell->timer_base, DECT_TIMER_TX);
+	dect_timer_base_init(cell->timer_base, DECT_TIMER_RX);
+	skb_queue_head_init(&cell->raw_tx_queue);
+	dect_cell_bmc_init(cell);
+	cell->blind_full_slots = (1 << DECT_HALF_FRAME_SIZE) - 1;
+	cell->trg_blind_full_slots = (1 << DECT_HALF_FRAME_SIZE) - 1;
+	dect_transceiver_group_init(&cell->trg);
+	cell->handle.ops = &dect_csf_ops;
+}
+
+/**
+ * dect_cell_attach_transceiver - attach a transceiver to a DECT cell
+ *
+ * Attach the transceiver to the cell's transceiver group and initialize
+ * an idle receiver control instance.
+ */
+int dect_cell_attach_transceiver(struct dect_cell *cell,
+				 struct dect_transceiver *trx)
+{
+	int err;
+
+	if (trx->cell != NULL)
+		return -EBUSY;
+
+	err = dect_transceiver_group_add(&cell->trg, trx);
+	if (err < 0)
+		goto err1;
+
+	err = -ENOMEM;
+	if (!dect_irc_init(cell, trx))
+		goto err2;
+
+	trx->cell = cell;
+	if (cell->state & DECT_CELL_ENABLED)
+		dect_cell_enable_transceiver(cell, trx);
+
+	return 0;
+
+err2:
+	dect_transceiver_group_remove(&cell->trg, trx);
+err1:
+	return err;
+}
+
+/**
+ * dect_cell_detach_transceiver - detach a transceiver from a DECT cell
+ *
+ * Detach the transceiver from the cell's transceiver group and release
+ * the associated resources.
+ */
+void dect_cell_detach_transceiver(struct dect_cell *cell,
+				  struct dect_transceiver *trx)
+{
+	dect_irc_disable(cell, trx->irc);
+	dect_transceiver_disable(trx);
+	dect_transceiver_group_remove(&cell->trg, trx);
+	kfree(trx->irc);
+	trx->cell = NULL;
+
+	dect_notify_cell(DECT_NEW_CELL, cell, NULL, 0);
+}
+
+/*
+ * Cell netlink interface
+ */
+
+static u32 dect_cell_alloc_index(void)
+{
+	static u32 index;
+
+	for (;;) {
+		if (++index == 0)
+			index = 1;
+		if (!dect_cell_get_by_index(index))
+			return index;
+	}
+}
+
+static int dect_fill_cell(struct sk_buff *skb,
+			  const struct dect_cell *cell,
+			  u16 type, u32 pid, u32 seq, u16 flags)
+{
+	const struct dect_transceiver *trx;
+	struct nlmsghdr *nlh;
+	struct dectmsg *dm;
+	struct nlattr *nest;
+
+	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*dm), flags);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+	dm = nlmsg_data(nlh);
+	dm->dm_index = cell->index;
+
+	nla_put_string(skb, DECTA_CELL_NAME, cell->name);
+	if (cell->flags != 0)
+		nla_put_u32(skb, DECTA_CELL_FLAGS, cell->flags);
+	if (cell->trg.trxmask != 0) {
+		nest = nla_nest_start(skb, DECTA_CELL_TRANSCEIVERS);
+		if (nest == NULL)
+			goto nla_put_failure;
+		dect_foreach_transceiver(trx, &cell->trg)
+			nla_put_string(skb, DECTA_LIST_ELEM, trx->name);
+		nla_nest_end(skb, nest);
+	}
+	if (cell->handle.clh != NULL)
+		nla_put_u8(skb, DECTA_CELL_CLUSTER, cell->handle.clh->index);
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static int dect_dump_cell(struct sk_buff *skb,
+			  struct netlink_callback *cb)
+{
+	const struct dect_cell *cell;
+	unsigned int idx, s_idx;
+
+	s_idx = cb->args[0];
+	idx = 0;
+	list_for_each_entry(cell, &dect_cell_list, list) {
+		if (idx < s_idx)
+			goto cont;
+		if (dect_fill_cell(skb, cell, DECT_NEW_CELL,
+				   NETLINK_CB(cb->skb).portid,
+				   cb->nlh->nlmsg_seq, NLM_F_MULTI) <= 0)
+			break;
+cont:
+		idx++;
+	}
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
+static void dect_notify_cell(u16 event, const struct dect_cell *cell,
+			     const struct nlmsghdr *nlh, u32 pid)
+{
+	struct sk_buff *skb;
+	bool report = nlh ? nlmsg_report(nlh) : 0;
+	u32 seq = nlh ? nlh->nlmsg_seq : 0;
+	int err = -ENOBUFS;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		goto err;
+
+	err = dect_fill_cell(skb, cell, event, pid, seq, NLMSG_DONE);
+	if (err < 0) {
+		WARN_ON(err == -EMSGSIZE);
+		kfree_skb(skb);
+		goto err;
+	}
+	nlmsg_notify(dect_nlsk, skb, pid, DECTNLGRP_CELL, report, GFP_KERNEL);
+err:
+	if (err < 0)
+		netlink_set_err(dect_nlsk, pid, DECTNLGRP_CELL, err);
+}
+
+static const struct nla_policy dect_cell_policy[DECTA_CELL_MAX + 1] = {
+	[DECTA_CELL_NAME]		= { .type = NLA_STRING, .len = DECTNAMSIZ },
+	[DECTA_CELL_FLAGS]		= { .type = NLA_U32 },
+	[DECTA_CELL_CLUSTER]		= { .type = NLA_U8 },
+};
+
+static int dect_new_cell(const struct sk_buff *skb,
+			 const struct nlmsghdr *nlh,
+			 const struct nlattr *tb[DECTA_CELL_MAX + 1])
+{
+	struct dect_cell *cell;
+	struct dectmsg *dm;
+	u32 flags = 0;
+	u8 cli = 0;
+	int err;
+
+	dm = nlmsg_data(nlh);
+	if (dm->dm_index != 0)
+		cell = dect_cell_get_by_index(dm->dm_index);
+	else if (tb[DECTA_CELL_NAME] != NULL)
+		cell = dect_cell_get_by_name(tb[DECTA_CELL_NAME]);
+	else
+		return -EINVAL;
+
+	if (tb[DECTA_CELL_FLAGS] != NULL) {
+		flags = nla_get_u32(tb[DECTA_CELL_FLAGS]);
+		if (flags & ~(DECT_CELL_CCP | DECT_CELL_SLAVE |
+			      DECT_CELL_MONITOR))
+			return -EINVAL;
+	}
+
+	if (tb[DECTA_CELL_CLUSTER] != NULL)
+		cli = nla_get_u8(tb[DECTA_CELL_CLUSTER]);
+
+	if (cell != NULL) {
+		if (nlh->nlmsg_flags & NLM_F_EXCL)
+			return -EEXIST;
+
+		if (tb[DECTA_CELL_CLUSTER] != NULL) {
+			if (cell->handle.clh != NULL)
+				return -EBUSY;
+			if (cli != 0)
+				return dect_cell_bind(cell, cli);
+		}
+		return 0;
+	}
+
+	if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+		return -ENOENT;
+
+	cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+	if (cell == NULL)
+		return -ENOMEM;
+	cell->index = dect_cell_alloc_index();
+	nla_strlcpy(cell->name, tb[DECTA_CELL_NAME], sizeof(cell->name));
+	cell->flags = flags;
+	dect_cell_init(cell);
+
+	if (cli != 0) {
+		err = dect_cell_bind(cell, cli);
+		if (err < 0)
+			goto err;
+	}
+
+	list_add_tail(&cell->list, &dect_cell_list);
+	dect_notify_cell(DECT_NEW_CELL, cell, nlh, NETLINK_CB(skb).portid);
+	return 0;
+
+err:
+	kfree(cell);
+	return err;
+}
+
+static int dect_del_cell(const struct sk_buff *skb,
+			 const struct nlmsghdr *nlh,
+			 const struct nlattr *tb[DECTA_CELL_MAX + 1])
+{
+	struct dect_cell *cell = NULL;
+	struct dectmsg *dm;
+
+	dm = nlmsg_data(nlh);
+	if (dm->dm_index != 0)
+		cell = dect_cell_get_by_index(dm->dm_index);
+	else if (tb[DECTA_CELL_NAME] != NULL)
+		cell = dect_cell_get_by_name(tb[DECTA_CELL_NAME]);
+	if (cell == NULL)
+		return -ENODEV;
+
+	cell = dect_cell_get_by_name(tb[DECTA_CELL_NAME]);
+	if (cell == NULL)
+		return -ENOENT;
+
+	dect_cell_shutdown(cell);
+	list_del(&cell->list);
+	dect_notify_cell(DECT_DEL_CELL, cell, nlh, NETLINK_CB(skb).portid);
+	kfree(cell);
+	return 0;
+}
+
+static int dect_get_cell(const struct sk_buff *in_skb,
+			 const struct nlmsghdr *nlh,
+			 const struct nlattr *tb[DECTA_CELL_MAX + 1])
+{
+	u32 pid = NETLINK_CB(in_skb).portid;
+	const struct dect_cell *cell = NULL;
+	struct dectmsg *dm;
+	struct sk_buff *skb;
+	int err;
+
+	dm = nlmsg_data(nlh);
+	if (dm->dm_index != 0)
+		cell = dect_cell_get_by_index(dm->dm_index);
+	else if (tb[DECTA_CELL_NAME] != NULL)
+		cell = dect_cell_get_by_name(tb[DECTA_CELL_NAME]);
+	if (cell == NULL)
+		return -ENODEV;
+
+	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOMEM;
+	err = dect_fill_cell(skb, cell, DECT_NEW_CELL, pid, nlh->nlmsg_seq,
+			     NLMSG_DONE);
+	if (err < 0)
+		goto err1;
+	return nlmsg_unicast(dect_nlsk, skb, pid);
+
+err1:
+	kfree_skb(skb);
+	return err;
+}
+
+static const struct dect_netlink_handler dect_cell_handlers[] = {
+       {
+	       /* DECT_NEW_CELL */
+		.policy		= dect_cell_policy,
+		.maxtype	= DECTA_CELL_MAX,
+		.doit		= dect_new_cell,
+	},
+	{
+		/* DECT_DEL_CELL */
+		.policy		= dect_cell_policy,
+		.maxtype	= DECTA_CELL_MAX,
+		.doit		= dect_del_cell,
+	},
+	{
+		/* DECT_GET_CELL */
+		.policy		= dect_cell_policy,
+		.maxtype	= DECTA_CELL_MAX,
+		.doit		= dect_get_cell,
+		.dump		= dect_dump_cell,
+	},
+};
+
+static int __init dect_csf_module_init(void)
+{
+	int err;
+
+	err = dect_transceiver_module_init();
+	if (err < 0)
+		return err;
+
+	dect_netlink_register_handlers(dect_cell_handlers, DECT_NEW_CELL,
+				       ARRAY_SIZE(dect_cell_handlers));
+	return 0;
+}
+
+static void __exit dect_csf_module_exit(void)
+{
+	dect_netlink_unregister_handlers(DECT_NEW_CELL,
+						 ARRAY_SIZE(dect_cell_handlers));
+	dect_transceiver_module_exit();
+}
+
+module_init(dect_csf_module_init);
+module_exit(dect_csf_module_exit);
diff --git a/target/linux/generic/files/net/dect/raw.c b/target/linux/generic/files/net/dect/raw.c
new file mode 100644
index 0000000..86056cf
--- /dev/null
+++ b/target/linux/generic/files/net/dect/raw.c
@@ -0,0 +1,267 @@
+/*
+ * DECT RAW sockets
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/dect.h>
+#include <net/sock.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+
+static HLIST_HEAD(dect_raw_sockets);
+
+struct dect_raw_sk {
+	struct sock		sk;
+};
+
+static inline struct dect_raw_sk *dect_raw_sk(struct sock *sk)
+{
+	return (struct dect_raw_sk *)sk;
+}
+
+static void __dect_raw_rcv(struct sk_buff *skb)
+{
+	struct dect_cell *cell = DECT_TRX_CB(skb)->trx->cell;
+	struct sk_buff *skb2;
+	struct sock *sk;
+
+	sk_for_each_bound(sk, &dect_raw_sockets) {
+		if (sk->sk_bound_dev_if &&
+		    sk->sk_bound_dev_if != cell->index)
+			continue;
+		if (skb->sk == sk)
+			continue;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 == NULL) {
+			sk->sk_err = -ENOMEM;
+			sk->sk_error_report(sk);
+		} else {
+			/* Release the transceiver reference, it is only valid
+			 * in IRQ and softirq context.
+			 */
+			DECT_TRX_CB(skb2)->trx = NULL;
+			if (dect_sock_queue_rcv_skb(sk, skb2) < 0)
+				kfree_skb(skb2);
+		}
+	}
+}
+
+static void dect_raw_close(struct sock *sk, long timeout)
+{
+	sk_common_release(sk);
+}
+
+static int dect_raw_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
+
+	if (len < sizeof(*addr) || addr->dect_family != AF_DECT)
+		return -EINVAL;
+
+	if (addr->dect_index != 0 &&
+	    !dect_cell_get_by_index(addr->dect_index))
+		return -ENODEV;
+
+	lock_sock(sk);
+	sk->sk_bound_dev_if = addr->dect_index;
+	if (!hlist_unhashed(&sk->sk_bind_node))
+		__sk_del_bind_node(sk);
+	sk_add_bind_node(sk, &dect_raw_sockets);
+	release_sock(sk);
+	return 0;
+}
+
+static void dect_raw_unhash(struct sock *sk)
+{
+	if (!hlist_unhashed(&sk->sk_bind_node))
+		__sk_del_bind_node(sk);
+}
+
+static int dect_raw_getname(struct sock *sk, struct sockaddr *uaddr, int *len,
+			    int peer)
+{
+	struct sockaddr_dect *addr = (struct sockaddr_dect *)uaddr;
+
+	if (peer)
+		return -EOPNOTSUPP;
+
+	addr->dect_family = AF_DECT;
+	addr->dect_index  = sk->sk_bound_dev_if;
+	*len = sizeof(*addr);
+	return 0;
+}
+
+static int dect_raw_recvmsg(struct kiocb *iocb, struct sock *sk,
+			    struct msghdr *msg, size_t len,
+			    int noblock, int flags, int *addrlen)
+{
+	struct sockaddr_dect *addr;
+	struct dect_raw_auxdata aux;
+	struct sk_buff *skb;
+	size_t copied = 0;
+	int err;
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	skb = skb_recv_datagram(sk, flags, noblock, &err);
+	if (skb == NULL)
+		goto out;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	if (err < 0)
+		goto out_free;
+
+	if (msg->msg_name != NULL) {
+		addr = (struct sockaddr_dect *)msg->msg_name;
+		addr->dect_family = AF_DECT;
+		addr->dect_index  = DECT_SK_CB(skb)->index;
+		msg->msg_namelen = sizeof(*addr);
+	}
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	aux.mfn   = DECT_TRX_CB(skb)->mfn;
+	aux.frame = DECT_TRX_CB(skb)->frame;
+	aux.slot  = DECT_TRX_CB(skb)->slot;
+	aux.rssi  = DECT_TRX_CB(skb)->rssi;
+	put_cmsg(msg, SOL_DECT, DECT_RAW_AUXDATA, sizeof(aux), &aux);
+
+	if (flags & MSG_TRUNC)
+		copied = skb->len;
+out_free:
+	skb_free_datagram(sk, skb);
+out:
+	return err ? : copied;
+}
+
+static int dect_raw_sendmsg(struct kiocb *iocb, struct sock *sk,
+			    struct msghdr *msg, size_t len)
+{
+	struct sockaddr_dect *addr = msg->msg_name;
+	struct dect_raw_auxdata *aux = NULL;
+	struct dect_cell *cell;
+	struct sk_buff *skb;
+	struct cmsghdr *cmsg;
+	size_t size;
+	int index;
+	int err;
+
+	if (msg->msg_namelen) {
+		if (addr->dect_family != AF_DECT)
+			return -EINVAL;
+		index = addr->dect_index;
+	} else
+		index = sk->sk_bound_dev_if;
+
+	cell = dect_cell_get_by_index(index);
+	if (cell == NULL)
+		return -ENODEV;
+
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (!CMSG_OK(msg, cmsg))
+			return -EINVAL;
+		if (cmsg->cmsg_level != SOL_DECT)
+			continue;
+
+		switch (cmsg->cmsg_type) {
+		case DECT_RAW_AUXDATA:
+			if (cmsg->cmsg_len != CMSG_LEN(sizeof(*aux)))
+				return -EINVAL;
+			aux = (struct dect_raw_auxdata *)CMSG_DATA(cmsg);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (aux == NULL)
+		return -EINVAL;
+
+	size = DECT_PREAMBLE_SIZE + len;
+	skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto err1;
+
+	/* Reserve space for preamble */
+	skb_reset_mac_header(skb);
+	skb_reserve(skb, DECT_PREAMBLE_SIZE);
+
+	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+	if (err < 0)
+		goto err2;
+
+	DECT_TRX_CB(skb)->mfn   = aux->mfn;
+	DECT_TRX_CB(skb)->frame = aux->frame;
+	DECT_TRX_CB(skb)->slot  = aux->slot;
+
+	skb_queue_tail(&cell->raw_tx_queue, skb);
+	return len;
+
+err2:
+	kfree_skb(skb);
+err1:
+	return err;
+}
+
+static struct dect_proto dect_raw_proto = {
+	.type		= SOCK_RAW,
+	.protocol	= DECT_RAW,
+	.capability	= CAP_NET_RAW,
+	.ops		= &dect_dgram_ops,
+	.proto.name	= "DECT_RAW",
+	.proto.owner	= THIS_MODULE,
+	.proto.obj_size	= sizeof(struct dect_raw_sk),
+	.proto.close	= dect_raw_close,
+	.proto.bind	= dect_raw_bind,
+	.proto.unhash	= dect_raw_unhash,
+	.proto.recvmsg	= dect_raw_recvmsg,
+	.proto.sendmsg	= dect_raw_sendmsg,
+	.getname	= dect_raw_getname,
+};
+
+static int __init dect_raw_init(void)
+{
+	int err;
+
+	BUILD_BUG_ON(sizeof(struct sockaddr_dect) >
+		     sizeof(struct sockaddr));
+	err = dect_proto_register(&dect_raw_proto);
+	if (err < 0)
+		return err;
+	rcu_assign_pointer(dect_raw_rcv_hook, __dect_raw_rcv);
+	return 0;
+}
+
+static void __exit dect_raw_exit(void)
+{
+	rcu_assign_pointer(dect_raw_rcv_hook, NULL);
+	synchronize_rcu();
+	dect_proto_unregister(&dect_raw_proto);
+}
+
+module_init(dect_raw_init);
+module_exit(dect_raw_exit);
+
+MODULE_AUTHOR("Patrick McHardy <kaber at trash.net>");
+MODULE_DESCRIPTION("DECT RAW sockets");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_NET_PF_PROTO(PF_DECT, DECT_RAW);
diff --git a/target/linux/generic/files/net/dect/transceiver.c b/target/linux/generic/files/net/dect/transceiver.c
new file mode 100644
index 0000000..84b71a0
--- /dev/null
+++ b/target/linux/generic/files/net/dect/transceiver.c
@@ -0,0 +1,1031 @@
+/*
+ * DECT transceiver and transceiver group functions
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber at trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+//#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <net/dect/dect.h>
+#include <net/dect/mac_csf.h>
+#include <net/dect/transceiver.h>
+
+static LIST_HEAD(dect_transceiver_list);
+static int dect_transceiver_notify(struct dect_transceiver *trx,
+				   unsigned long event);
+
+#define trx_debug(trx, fmt, args...) \
+	pr_debug("%s: " fmt, trx->name, ## args)
+
+static const struct dect_band *dect_band[DECT_BAND_NUM];
+static const u8 dect_pkt_size[] = {
+	[DECT_PACKET_P00]	= DECT_P00_SIZE,
+	[DECT_PACKET_P08]	= DECT_P08_SIZE,
+	[DECT_PACKET_P32]	= DECT_P32_SIZE,
+	[DECT_PACKET_P640j]	= DECT_P640j_SIZE,
+	[DECT_PACKET_P672j]	= DECT_P672j_SIZE,
+	[DECT_PACKET_P80]	= DECT_P80_SIZE,
+};
+
+/**
+ * dect_transceiver_alloc_skb - allocate a transceiver RX skb
+ *
+ * @trx:	transceiver
+ * @slot:	slot number
+ *
+ * Allocate a skb according to the receiving channel characteristics.
+ */
+struct sk_buff *dect_transceiver_alloc_skb(struct dect_transceiver *trx, u8 slot)
+{
+	const struct dect_transceiver_slot *ts = &trx->slots[slot];
+	unsigned int align;
+	struct sk_buff *skb;
+
+	align = __alignof__(u64) - DECT_PREAMBLE_SIZE;
+
+	skb = alloc_skb(dect_pkt_size[ts->chd.pkt] + align, GFP_ATOMIC);
+	if (skb == NULL)
+		return NULL;
+	DECT_TRX_CB(skb)->trx = trx;
+	DECT_TRX_CB(skb)->slot = ts->chd.slot;
+	/* Reserve room for preamble and set up adjacent packet data pointer */
+	skb_reserve(skb, DECT_PREAMBLE_SIZE + align);
+	skb_put(skb, dect_pkt_size[ts->chd.pkt] - DECT_PREAMBLE_SIZE);
+	return skb;
+}
+EXPORT_SYMBOL_GPL(dect_transceiver_alloc_skb);
+
+/* Transceiver virtual clock maintenance:
+ *
+ * The transceiver layer processes the received frames from some slots in
+ * the past while the transceiver is transceiving on the following slots.
+ * Additionally the transceiver needs to be maintained for the upcoming
+ * slots. Therefore there are three different reference frames of time:
+ *
+ * 		[  RX  ][  TRX ][  TX  ]
+ * slot_0		   ->			slot_23
+ *
+ * - Real time, which is only known to the transceiver
+ *
+ * - RX time, which is a virtual clock following real time by at least one
+ *   slot and advancing as received slots are processed. A transceiver must
+ *   generate enough events so that the RX time never lags behind the TRX
+ *   time for more than one TDMA half frame.
+ *
+ * - TX time, which is a virtual clock leading RX time by a usually constant
+ *   amount of slots large enough so that packets queued to the transceiver
+ *   will reach the transceiver in time to be sent. It always leads real time,
+ *   but must never have a distance greater than one TDMA half frame from RX
+ *   time, resulting in a maximal distance of 11 to real time.
+ *
+ * Transceivers periodically notify the transceiver layer of an elapsed amount
+ * of time and the frames that were received during that period. The events
+ * batches generated by multiple transceivers contained in a group are merged
+ * and processed as a single chronological event stream.
+ *
+ * The following steps are performed during processing (for every slot):
+ *
+ * - Received packets are passed to the MAC layer
+ * - A RX tick is generated, ending the last RX slot
+ * - A TX tick with some offset in the future is generated, beginning the
+ *   next TX slot
+ *
+ * The RX and TX ticks are used by the MAC layer to maintain two timer bases
+ * for performing maintenance operations after a slot was received or before
+ * a slot will be transmitted.
+ */
+
+/**
+ * dect_transceiver_queue_event - queue a transceiver event for BH processing
+ *
+ * @trx:	DECT transceiver
+ * @event:	Transceiver event
+ */
+void dect_transceiver_queue_event(struct dect_transceiver *trx,
+				  struct dect_transceiver_event *event)
+{
+	struct dect_transceiver_group *grp = &trx->cell->trg;
+
+	spin_lock(&grp->lock);
+	list_add_tail(&event->list, &grp->events);
+	spin_unlock(&grp->lock);
+
+	tasklet_hi_schedule(&grp->tasklet);
+}
+EXPORT_SYMBOL_GPL(dect_transceiver_queue_event);
+
+static struct dect_transceiver_event *
+dect_dequeue_event(struct dect_transceiver_group *grp)
+{
+	struct dect_transceiver_event *event;
+	unsigned long flags;
+
+	event = NULL;
+	spin_lock_irqsave(&grp->lock, flags);
+	if (!list_empty(&grp->events)) {
+		event = list_first_entry(&grp->events,
+					 struct dect_transceiver_event,
+					 list);
+		list_del(&event->list);
+	}
+	spin_unlock_irqrestore(&grp->lock, flags);
+	return event;
+}
+
+static void dect_tg_merge_events(struct dect_transceiver_group *grp,
+				 struct dect_transceiver *trx,
+				 struct dect_transceiver_event *event)
+{
+	struct sk_buff *skb;
+	u8 slot, idx, i;
+
+	/* Transfer the packets to the slot input queues and mark the
+	 * slot events.
+	 */
+	trx_debug(trx, "merge %u events pos %u rssi_mask %x\n",
+		  trx->ops->eventrate, event->slotpos, event->rssi_mask);
+
+	for (i = 0; i < trx->ops->eventrate; i++) {
+		slot = event->slotpos + i;
+		idx = slot % ARRAY_SIZE(grp->slots);
+
+		skb = skb_peek(&event->rx_queue);
+		if (skb != NULL && DECT_TRX_CB(skb)->slot == slot) {
+			__skb_unlink(skb, &event->rx_queue);
+			__skb_queue_tail(&grp->slots[idx].queue, skb);
+		} else if (event->rssi_mask & (1 << i))
+			grp->slots[idx].rssi[trx->index] = event->rssi[i];
+
+		grp->slots[idx].mask |= 1 << trx->index;
+	}
+}
+
+static bool seqno_before(u32 seq1, u32 seq2)
+{
+	return (s32)(seq2 - seq1) > 0;
+}
+
+static bool seqno_after(u32 seq1, u32 seq2)
+{
+	return seqno_before(seq2, seq1);
+}
+
+static void dect_tg_process_events(struct dect_transceiver_group *grp)
+{
+	struct dect_transceiver *trx;
+	struct dect_transceiver_slot *ts;
+	struct sk_buff *skb;
+	u8 idx, d, i;
+	u16 late;
+
+	pr_debug("process events slot_low=%u slot_high=%u distance=%u\n",
+		 grp->slot_low, grp->slot_high,
+		 dect_slot_distance(grp->slot_low, grp->slot_high));
+
+	while (grp->slot_low != grp->slot_high) {
+		/*
+		 * If more than one half frame is missing, only forward the
+		 * clock since the slot positions refer to slots in the
+		 * following half frame.
+		 */
+		d = dect_slot_distance(grp->slot_low, grp->slot_high);
+		if (d > ARRAY_SIZE(grp->slots))
+			goto tick;
+
+		idx = grp->slot_low % ARRAY_SIZE(grp->slots);
+
+		/* Check for transceivers which are lagging by more than their
+		 * event rate window and mark the current window entirely as
+		 * lost.
+		 */
+		late = grp->slots[idx].mask ^ grp->trxmask;
+		while (late != 0) {
+			trx = grp->trx[ffs(late) - 1];
+			late &= ~(1 << trx->index);
+
+			if (!seqno_before(trx->seqno + trx->ops->eventrate,
+					  grp->seqno) &&
+			    d <= trx->ops->eventrate)
+				continue;
+
+			trx_debug(trx, "late for window %u\n", grp->slot_low);
+			for (i = 0; i < trx->ops->eventrate; i++)
+				grp->slots[(idx + i) % 12].mask |= 1 << trx->index;
+			trx->stats.event_late++;
+		}
+
+		if (grp->slots[idx].mask != grp->trxmask) {
+			pr_debug("slot %u incomplete: mask %x trx %x\n",
+				grp->slot_low, grp->slots[idx].mask, grp->trxmask);
+			break;
+		}
+
+		while ((skb = __skb_dequeue(&grp->slots[idx].queue))) {
+			trx = DECT_TRX_CB(skb)->trx;
+			ts = &trx->slots[DECT_TRX_CB(skb)->slot];
+			dect_mac_rcv(trx, ts, skb);
+		}
+
+		for (i = 0; i < ARRAY_SIZE(grp->slots[idx].rssi); i++) {
+			if (grp->slots[idx].rssi[i] == 0)
+				continue;
+			trx = grp->trx[i];
+			ts = &trx->slots[grp->slot_low];
+			dect_mac_report_rssi(trx, ts, grp->slots[idx].rssi[i]);
+			grp->slots[idx].rssi[i] = 0;
+		}
+
+		grp->slots[idx].mask = 0;
+tick:
+		dect_mac_rx_tick(grp, dect_next_slotnum(grp->slot_low));
+		dect_mac_tx_tick(grp, dect_slot_add(grp->slot_low,
+						    2 * grp->latency));
+
+		grp->slot_low = dect_next_slotnum(grp->slot_low);
+	}
+}
+
+/*
+ * Softirq transceiver group event processing
+ */
+static void dect_transceiver_tasklet(unsigned long data)
+{
+	struct dect_transceiver_group *grp = (struct dect_transceiver_group *)data;
+	struct dect_transceiver *trx;
+	struct dect_transceiver_event *event;
+	struct sk_buff *skb;
+
+again:
+	event = dect_dequeue_event(grp);
+	if (event == NULL) {
+		dect_tg_process_events(grp);
+		return;
+	}
+
+	trx = event->trx;
+
+	trx_debug(trx, "event handler: trx: seq %u pos %u grp: seq %u pos %u\n",
+		  trx->seqno, event->slotpos, grp->seqno, grp->slot_low);
+
+	/* Before a transceiver is locked, its timing might vary and isn't
+	 * synchronized to the remaining group. The MAC layer handles this
+	 * manually.
+	 */
+	if (trx->state != DECT_TRANSCEIVER_LOCKED) {
+		skb = __skb_dequeue(&event->rx_queue);
+		if (skb != NULL)
+			dect_mac_irc_rcv(trx, skb);
+		dect_mac_irc_tick(trx);
+		goto out;
+	}
+
+	/* If a secondary transceiver enters locked state or a tranceiver missed
+	 * a previous window, its sequence number is out of sync. Resync it once
+	 * it starts reporting events in the current window.
+	 *
+	 * FIXME: driver should ignore frames with missed interrupts completely
+	 */
+	if (seqno_before(trx->seqno + trx->ops->eventrate, grp->seqno)) {
+		if (event->slotpos != grp->slot_low) {
+			trx_debug(trx, "unsynchronized\n");
+			__skb_queue_purge(&event->rx_queue);
+			goto out;
+		}
+
+		trx->seqno = grp->seqno;
+		if (grp->slot_high != grp->slot_low)
+			trx->seqno -= trx->ops->eventrate;
+
+		trx_debug(trx, "synchronized to seqno %u\n", trx->seqno);
+	}
+
+	/* Merge the events and update the sequence number. The transceiver
+	 * with the highest sequence number determines the slot position for
+	 * the entire group.
+	 */
+	dect_tg_merge_events(grp, trx, event);
+
+	trx->seqno += trx->ops->eventrate;
+	if (seqno_after(trx->seqno, grp->seqno)) {
+		grp->seqno = trx->seqno;
+		grp->slot_high =
+			dect_slot_add(event->slotpos, trx->ops->eventrate);
+	}
+
+out:
+	dect_release_transceiver_event(event);
+	goto again;
+}
+
+int dect_transceiver_group_add(struct dect_transceiver_group *grp,
+			       struct dect_transceiver *trx)
+{
+	u8 index;
+
+	index = ffz(grp->trxmask);
+	if (index >= ARRAY_SIZE(grp->trx))
+		return -EMFILE;
+
+	trx->index = index;
+	grp->trx[index] = trx;
+	if (trx->ops->latency > grp->latency)
+		grp->latency = trx->ops->latency;
+
+	grp->trxmask |= 1 << index;
+	return 0;
+}
+
+void dect_transceiver_group_remove(struct dect_transceiver_group *grp,
+				   struct dect_transceiver *trx)
+{
+	grp->trxmask &= ~(1 << trx->index);
+	/* Synchronize with interrupt and softirq processing */
+	synchronize_rcu();
+	grp->trx[trx->index] = NULL;
+}
+
+void dect_transceiver_group_init(struct dect_transceiver_group *grp)
+{
+	unsigned int i;
+
+	spin_lock_init(&grp->lock);
+	INIT_LIST_HEAD(&grp->events);
+	for (i = 0; i < ARRAY_SIZE(grp->slots); i++)
+		__skb_queue_head_init(&grp->slots[i].queue);
+
+	tasklet_init(&grp->tasklet, dect_transceiver_tasklet,
+		     (unsigned long)grp);
+}
+
+void dect_transceiver_disable(struct dect_transceiver *trx)
+{
+	trx->ops->disable(trx);
+	trx->state = DECT_TRANSCEIVER_STOPPED;
+}
+
+void dect_transceiver_enable(struct dect_transceiver *trx)
+{
+	if (trx->mode == DECT_TRANSCEIVER_MASTER)
+		trx->state = DECT_TRANSCEIVER_LOCKED;
+	else
+		trx->state = DECT_TRANSCEIVER_UNLOCKED;
+
+	trx->ops->enable(trx);
+}
+
+void dect_transceiver_confirm(struct dect_transceiver *trx)
+{
+	trx_debug(trx, "confirm\n");
+	trx->state = DECT_TRANSCEIVER_LOCK_PENDING;
+	trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_RX;
+	trx->ops->confirm(trx);
+}
+
+void dect_transceiver_unlock(struct dect_transceiver *trx)
+{
+	trx_debug(trx, "unlock\n");
+	trx->ops->unlock(trx);
+	trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_SCANNING;
+	trx->state = DECT_TRANSCEIVER_UNLOCKED;
+}
+
+int dect_transceiver_set_band(struct dect_transceiver *trx, u8 bandnum)
+{
+	const struct dect_band *band;
+
+	band = dect_band[bandnum];
+	if (band == NULL)
+		return -ENOENT;
+	trx->carriers = trx->ops->set_band(trx, band);
+	trx->band = band;
+	return 0;
+}
+
+void dect_transceiver_lock(struct dect_transceiver *trx, u8 slot)
+{
+	trx_debug(trx, "lock to slot %u\n", slot);
+	trx->slots[DECT_SCAN_SLOT].state = DECT_SLOT_IDLE;
+	trx->state = DECT_TRANSCEIVER_LOCKED;
+	trx->ops->lock(trx, slot);
+}
+
+static void dect_transceiver_set_blind(struct dect_transceiver *trx, u8 slot)
+{
+	u8 n = DECT_FRAME_SIZE - 1 - slot;
+
+	trx->slots[slot].blinded++;
+	trx->blind_full_slots |= 1 << n;
+}
+
+static void dect_transceiver_set_visible(struct dect_transceiver *trx, u8 slot)
+{
+	u8 n = DECT_FRAME_SIZE - 1 - slot;
+
+	if (--trx->slots[slot].blinded == 0)
+		trx->blind_full_slots &= ~(1 << n);
+}
+
+static bool dect_transceiver_slot_blind(const struct dect_transceiver *trx, u8 slot)
+{
+	u8 n = DECT_FRAME_SIZE - 1 - slot;
+
+	return trx->blind_full_slots & (1 << n);
+}
+
+bool dect_transceiver_channel_available(const struct dect_transceiver *trx,
+				        const struct dect_channel_desc *chd)
+{
+	u8 slot = chd->slot, prev, next;
+
+	if (trx->slots[slot].state == DECT_SLOT_RX ||
+	    trx->slots[slot].state == DECT_SLOT_TX)
+		return false;
+
+	switch ((int)chd->pkt) {
+	case DECT_PACKET_P80:
+	case DECT_PACKET_P640j:
+	case DECT_PACKET_P672j:
+		if (dect_transceiver_slot_blind(trx, slot + 1))
+			return false;
+	case DECT_PACKET_P32:
+	case DECT_PACKET_P08:
+	case DECT_PACKET_P00:
+		if (dect_transceiver_slot_blind(trx, slot))
+			return false;
+		break;
+	}
+
+	/* In case of slow hopping transceivers the adjacent slots must be
+	 * available as well. Scanning slots are not blind, so they must be
+	 * checked for explicitly.
+	 */
+	if (trx->ops->features & DECT_TRANSCEIVER_SLOW_HOPPING) {
+		prev = dect_prev_slotnum(slot);
+		if (trx->slots[prev].state == DECT_SLOT_SCANNING)
+			return false;
+		next = dect_next_slotnum(slot);
+		if (trx->slots[next].state == DECT_SLOT_SCANNING)
+			return false;
+	}
+
+	return true;
+}
+
+static bool dect_tg_update_blind_full_slots(struct dect_transceiver_group *trg)
+{
+	const struct dect_transceiver *trx;
+	u32 blind_full_slots;
+
+	blind_full_slots = (~0U) & DECT_SLOT_MASK;
+	dect_foreach_transceiver(trx, trg)
+		blind_full_slots &= trx->blind_full_slots;
+
+	if (trg->blind_full_slots != blind_full_slots) {
+		trg->blind_full_slots = blind_full_slots;
+		return true;
+	} else
+		return false;
+}
+
+/**
+ * dect_transceiver_reserve - reserve transceiver resources for a physical channel
+ *
+ * Reserve the slot positions necessary to estabish the specified physical
+ * channel. The chosen transceivers and the global groups blind full slot
+ * masks are updated.
+ *
+ * Returns true when the global visibility state has changed.
+ */
+bool dect_transceiver_reserve(struct dect_transceiver_group *trg,
+			      struct dect_transceiver *trx,
+			      const struct dect_channel_desc *chd)
+{
+	u8 slot = chd->slot;
+
+	switch ((int)chd->pkt) {
+	case DECT_PACKET_P80:
+	case DECT_PACKET_P640j:
+	case DECT_PACKET_P672j:
+		dect_transceiver_set_blind(trx, slot + 1);
+	case DECT_PACKET_P32:
+	case DECT_PACKET_P08:
+	case DECT_PACKET_P00:
+		dect_transceiver_set_blind(trx, slot);
+		break;
+	}
+
+	/* Set adjacent slots blind if the transceiver is slow hopping */
+	if (trx->ops->features & DECT_TRANSCEIVER_SLOW_HOPPING) {
+		dect_transceiver_set_blind(trx, dect_prev_slotnum(slot));
+		dect_transceiver_set_blind(trx, dect_next_slotnum(slot));
+	}
+
+	return dect_tg_update_blind_full_slots(trg);
+}
+
+/**
+ * dect_transceiver_release - release transceiver resources of a phyiscal channel
+ *
+ * Release the slot positions used by the specified physical channel. The
+ * transceiver and the global group blind full slot masks are updated.
+ *
+ * Returns true when the global visibility state has changed.
+ */
+bool dect_transceiver_release(struct dect_transceiver_group *trg,
+			      struct dect_transceiver *trx,
+			      const struct dect_channel_desc *chd)
+{
+	u8 slot = chd->slot;
+
+	switch ((int)chd->pkt) {
+	case DECT_PACKET_P80:
+	case DECT_PACKET_P640j:
+	case DECT_PACKET_P672j:
+		dect_transceiver_set_visible(trx, slot + 1);
+	case DECT_PACKET_P32:
+	case DECT_PACKET_P08:
+	case DECT_PACKET_P00:
+		dect_transceiver_set_visible(trx, slot);
+		break;
+	}
+
+	/* Set adjacent slots unblind if the transceiver is slow hopping */
+	if (trx->ops->features & DECT_TRANSCEIVER_SLOW_HOPPING) {
+		dect_transceiver_set_visible(trx, dect_next_slotnum(slot));
+		dect_transceiver_set_visible(trx, dect_prev_slotnum(slot));
+	}
+
+	return dect_tg_update_blind_full_slots(trg);
+}
+
+struct dect_transceiver *dect_transceiver_alloc(const struct dect_transceiver_ops *ops,
+						unsigned int priv)
+{
+	struct dect_transceiver *trx;
+	unsigned int nevents, size, i;
+
+	/* Allocate enough event structures for one TDMA half frame */
+	nevents = DECT_HALF_FRAME_SIZE / ops->eventrate;
+	size = nevents * sizeof(trx->event[0]) + priv;
+
+	trx = kzalloc(sizeof(*trx) + size, GFP_KERNEL);
+	if (trx == NULL)
+		return NULL;
+
+	trx->state = DECT_TRANSCEIVER_STOPPED;
+	trx->ops = ops;
+	for (i = 0; i < DECT_FRAME_SIZE; i++)
+		trx->slots[i].chd.slot = i;
+
+	for (i = 0; i < nevents; i++) {
+		skb_queue_head_init(&trx->event[i].rx_queue);
+		trx->event[i].trx = trx;
+	}
+	return trx;
+}
+EXPORT_SYMBOL_GPL(dect_transceiver_alloc);
+
+void dect_transceiver_free(struct dect_transceiver *trx)
+{
+	kfree(trx);
+}
+EXPORT_SYMBOL_GPL(dect_transceiver_free);
+
+static int dect_transceiver_alloc_name(struct dect_transceiver *trx)
+{
+	struct dect_transceiver *t;
+	unsigned long *inuse;
+	int i;
+
+	inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
+	if (inuse == NULL)
+		return -ENOMEM;
+
+	list_for_each_entry(t, &dect_transceiver_list, list) {
+		if (!sscanf(t->name, "trx%d", &i))
+			continue;
+		if (i > BITS_PER_BYTE * PAGE_SIZE)
+			continue;
+		set_bit(i, inuse);
+	}
+
+	i = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
+	free_page((unsigned long)inuse);
+	if (i == BITS_PER_BYTE * PAGE_SIZE)
+		return -ENFILE;
+
+	snprintf(trx->name, sizeof(trx->name), "trx%d", i);
+	return 0;
+}
+
+int dect_register_transceiver(struct dect_transceiver *trx)
+{
+	int err;
+
+	dect_lock();
+	err = dect_transceiver_alloc_name(trx);
+	if (err < 0)
+		goto out;
+
+	err = dect_transceiver_set_band(trx, DECT_DEFAULT_BAND);
+	if (err < 0)
+		goto out;
+
+	list_add_tail(&trx->list, &dect_transceiver_list);
+	dect_transceiver_notify(trx, DECT_TRANSCEIVER_REGISTER);
+out:
+	dect_unlock();
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dect_register_transceiver);
+
+void dect_unregister_transceiver(struct dect_transceiver *trx)
+{
+	dect_lock();
+	list_del(&trx->list);
+	dect_transceiver_notify(trx, DECT_TRANSCEIVER_UNREGISTER);
+	dect_unlock();
+
+	synchronize_rcu();
+	trx->ops->destructor(trx);
+}
+EXPORT_SYMBOL_GPL(dect_unregister_transceiver);
+
+/*
+ * Transceiver netlink interface
+ */
+
+static struct dect_transceiver *dect_transceiver_get_by_name(const struct nlattr *nla)
+{
+	struct dect_transceiver *trx;
+
+	list_for_each_entry(trx, &dect_transceiver_list, list) {
+		if (!nla_strcmp(nla, trx->name))
+			return trx;
+	}
+	return NULL;
+}
+
+static int dect_fill_slot(struct sk_buff *skb,
+			  const struct dect_transceiver *trx, u8 slot)
+{
+	const struct dect_transceiver_slot *ts = &trx->slots[slot];
+
+	nla_put_u8(skb, DECTA_SLOT_NUM, slot);
+	nla_put_u8(skb, DECTA_SLOT_STATE, ts->state);
+	nla_put_u32(skb, DECTA_SLOT_FLAGS, ts->flags);
+	if (ts->state != DECT_SLOT_IDLE) {
+		nla_put_u8(skb, DECTA_SLOT_PACKET, ts->chd.pkt);
+		nla_put_u8(skb, DECTA_SLOT_CARRIER, ts->chd.carrier);
+		nla_put_u32(skb, DECTA_SLOT_FREQUENCY, trx->band->frequency[ts->chd.carrier]);
+	}
+	if (ts->state == DECT_SLOT_RX) {
+		nla_put_u32(skb, DECTA_SLOT_PHASEOFF, ts->phaseoff);
+		nla_put_u8(skb, DECTA_SLOT_RSSI,
+			   ts->rssi >> DECT_RSSI_AVG_SCALE);
+	}
+	nla_put_u32(skb, DECTA_SLOT_RX_BYTES, ts->rx_bytes);
+	nla_put_u32(skb, DECTA_SLOT_RX_PACKETS, ts->rx_packets);
+	nla_put_u32(skb, DECTA_SLOT_RX_A_CRC_ERRORS, ts->rx_a_crc_errors);
+	nla_put_u32(skb, DECTA_SLOT_RX_X_CRC_ERRORS, ts->rx_x_crc_errors);
+	nla_put_u32(skb, DECTA_SLOT_RX_Z_CRC_ERRORS, ts->rx_z_crc_errors);
+	nla_put_u32(skb, DECTA_SLOT_TX_BYTES, ts->tx_bytes);
+	nla_put_u32(skb, DECTA_SLOT_TX_PACKETS, ts->tx_packets);
+	return 0;
+}
+
+static int dect_fill_transceiver(struct sk_buff *skb,
+				 const struct dect_transceiver *trx,
+				 u16 type, u32 pid, u32 seq, u16 flags)
+{
+	const struct dect_transceiver_stats *stats = &trx->stats;
+	struct nlattr *nest, *chan;
+	struct nlmsghdr *nlh;
+	struct dectmsg *dm;
+	u8 slot;
+
+	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*dm), flags);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	dm = nlmsg_data(nlh);
+
+	nla_put_string(skb, DECTA_TRANSCEIVER_NAME, trx->name);
+	nla_put_string(skb, DECTA_TRANSCEIVER_TYPE, trx->ops->name);
+	nla_put_u32(skb, DECTA_TRANSCEIVER_FEATURES, trx->ops->features);
+	if (trx->cell != NULL)
+		nla_put_u8(skb, DECTA_TRANSCEIVER_LINK, trx->cell->index);
+
+	nest = nla_nest_start(skb, DECTA_TRANSCEIVER_STATS);
+	if (nest == NULL)
+		goto nla_put_failure;
+	nla_put_u32(skb, DECTA_TRANSCEIVER_STATS_EVENT_BUSY, stats->event_busy);
+	nla_put_u32(skb, DECTA_TRANSCEIVER_STATS_EVENT_LATE, stats->event_late);
+	nla_nest_end(skb, nest);
+
+	nla_put_u8(skb, DECTA_TRANSCEIVER_BAND, trx->band->band);
+
+	nest = nla_nest_start(skb, DECTA_TRANSCEIVER_SLOTS);
+	if (nest == NULL)
+		goto nla_put_failure;
+	for (slot = 0; slot < DECT_FRAME_SIZE; slot++) {
+		chan = nla_nest_start(skb, DECTA_LIST_ELEM);
+		if (chan == NULL)
+			goto nla_put_failure;
+		if (dect_fill_slot(skb, trx, slot) < 0)
+			goto nla_put_failure;
+		nla_nest_end(skb, chan);
+	}
+	nla_nest_end(skb, nest);
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static const struct nla_policy dect_transceiver_policy[DECTA_TRANSCEIVER_MAX + 1] = {
+	[DECTA_TRANSCEIVER_NAME]	= { .type = NLA_STRING, .len = DECTNAMSIZ },
+	[DECTA_TRANSCEIVER_LINK]	= { .type = NLA_U8 },
+};
+
+static int dect_new_transceiver(const struct sk_buff *in_skb,
+				const struct nlmsghdr *nlh,
+				const struct nlattr *tb[DECTA_TRANSCEIVER_MAX + 1])
+{
+	struct dect_transceiver *trx;
+	struct dect_cell *cell;
+	struct dectmsg *dm;
+	int index;
+
+	dm = nlmsg_data(nlh);
+
+	if (tb[DECTA_TRANSCEIVER_NAME] == NULL)
+		return -EINVAL;
+
+	trx = dect_transceiver_get_by_name(tb[DECTA_TRANSCEIVER_NAME]);
+	if (trx == NULL) {
+		if (nlh->nlmsg_flags & NLM_F_CREATE)
+			return -EOPNOTSUPP;
+		return -ENOENT;
+	}
+	if (nlh->nlmsg_flags & NLM_F_EXCL)
+		return -EEXIST;
+
+	if (tb[DECTA_TRANSCEIVER_LINK] != NULL) {
+		index = nla_get_u8(tb[DECTA_TRANSCEIVER_LINK]);
+		if (index == -1)
+			dect_cell_detach_transceiver(trx->cell, trx);
+		else {
+			cell = dect_cell_get_by_index(index);
+			if (cell == NULL)
+				return -ENOENT;
+			return dect_cell_attach_transceiver(cell, trx);
+		}
+	}
+	return 0;
+}
+
+static int dect_get_transceiver(const struct sk_buff *in_skb,
+				const struct nlmsghdr *nlh,
+				const struct nlattr *tb[DECTA_TRANSCEIVER_MAX + 1])
+{
+	u32 pid = NETLINK_CB(in_skb).portid;
+	const struct dect_transceiver *trx;
+	struct sk_buff *skb;
+	int err;
+
+	if (tb[DECTA_TRANSCEIVER_NAME] == NULL)
+		return -EINVAL;
+
+	trx = dect_transceiver_get_by_name(tb[DECTA_TRANSCEIVER_NAME]);
+	if (trx == NULL)
+		return -ENOENT;
+
+	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOMEM;
+	err = dect_fill_transceiver(skb, trx, DECT_NEW_TRANSCEIVER, pid,
+				    nlh->nlmsg_seq, NLMSG_DONE);
+	if (err < 0)
+		goto err1;
+	return nlmsg_unicast(dect_nlsk, skb, pid);
+
+err1:
+	kfree_skb(skb);
+	return err;
+}
+
+static int dect_dump_transceiver(struct sk_buff *skb,
+				 struct netlink_callback *cb)
+{
+	const struct dect_transceiver *trx;
+	unsigned int idx, s_idx;
+
+	s_idx = cb->args[0];
+	idx = 0;
+	list_for_each_entry(trx, &dect_transceiver_list, list) {
+		if (idx < s_idx)
+			goto cont;
+		if (dect_fill_transceiver(skb, trx, DECT_NEW_TRANSCEIVER,
+					  NETLINK_CB(cb->skb).portid,
+					  cb->nlh->nlmsg_seq, NLM_F_MULTI) <= 0)
+			break;
+cont:
+		idx++;
+	}
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
+static void dect_notify_transceiver(u16 event, const struct dect_transceiver *trx,
+				    const struct nlmsghdr *nlh, u32 pid)
+{
+	struct sk_buff *skb;
+	bool report = nlh ? nlmsg_report(nlh) : 0;
+	u32 seq = nlh ? nlh->nlmsg_seq : 0;
+	int err = -ENOBUFS;
+
+	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		goto err;
+
+	err = dect_fill_transceiver(skb, trx, event, pid, seq, NLMSG_DONE);
+	if (err < 0) {
+		WARN_ON(err == -EMSGSIZE);
+		kfree_skb(skb);
+		goto err;
+	}
+	nlmsg_notify(dect_nlsk, skb, pid, DECTNLGRP_TRANSCEIVER, report, GFP_KERNEL);
+err:
+	if (err < 0)
+		netlink_set_err(dect_nlsk, pid, DECTNLGRP_TRANSCEIVER, err);
+}
+
+static int dect_transceiver_notify(struct dect_transceiver *trx,
+				   unsigned long event)
+{
+	struct dect_cell *cell = trx->cell;
+
+	switch (event) {
+	case DECT_TRANSCEIVER_REGISTER:
+		dect_notify_transceiver(DECT_NEW_TRANSCEIVER, trx, NULL, 0);
+		break;
+	case DECT_TRANSCEIVER_UNREGISTER:
+		if (cell != NULL)
+			dect_cell_detach_transceiver(cell, trx);
+		dect_notify_transceiver(DECT_DEL_TRANSCEIVER, trx, NULL, 0);
+		break;
+	}
+	return 0;
+};
+
+
+static const struct dect_netlink_handler dect_transceiver_handlers[] = {
+	{
+		/* DECT_NEW_TRANSCEIVER */
+		.policy		= dect_transceiver_policy,
+		.maxtype	= DECTA_TRANSCEIVER_MAX,
+		.doit		= dect_new_transceiver,
+	},
+	{
+		/* DECT_DEL_TRANSCEIVER */
+	},
+	{
+		/* DECT_GET_TRANSCEIVER */
+		.policy		= dect_transceiver_policy,
+		.maxtype	= DECTA_TRANSCEIVER_MAX,
+		.doit		= dect_get_transceiver,
+		.dump		= dect_dump_transceiver,
+	},
+};
+
+/*
+ * RF-bands:
+ */
+
+struct dect_band_desc {
+	u8	band;
+	u8	carriers;
+	s16	c1_off;
+	u8	c2;
+	s16	c2_off;
+};
+
+static const struct dect_band_desc dect_band_desc[] __initdata = {
+	/* 1880 MHz to 1900 MHz RF band 00000 */
+	{ 0, 10, 0, -1,  0, },
+	/* 1880 MHz to 1978 MHz and 2010 MHz to 2025 MHz RF band 00001 */
+	{ 1, 64, 0, 56, 19, },
+	/* 1880 MHz to 1925 MHz and 2010 MHz to 2025 MHz RF band 00010 */
+	{ 2, 33, 0, 25, 50, },
+	/* 1880 MHz to 1900 MHz, 1915 MHz to 1940 MHz and 2010 MHz to
+	 * 2025 MHz RF band 00011 */
+	{ 3, 33, 10, 25, 50, },
+	/* 1880 MHz to 1900 MHz, 1935 MHz to 1960 MHz and 2010 MHz to
+	 * 2025 MHz RF band 00100 */
+	{ 4, 33, 22, 25, 50, },
+	/* 1880 MHz to 1900 MHZ, 1955 MHz to 1980 MHz and 2010 MHz to
+	 * 2025 MHz RF band 00101 */
+	{ 5, 33, 34, 25, 50, },
+	/* 902 MHz to 928 MHz RF band 01000 */
+	{ 8, 24, -576, -1, 0, },
+	/* 2400 MHz to 2483,5 MHz RF band 01001 */
+	{ 9, 55, 292, -1, 0, },
+};
+
+/*
+ * A nominal DECT RF carrier is one whose center frequency is generated by
+ * the formula:
+ *
+ * Fg = F0 - g × 1,728 MHz, where g is any integer
+ */
+static u32 __init dect_calc_frequency(s16 g)
+{
+	if (g >= 0 && g < 10)
+		return DECT_FREQUENCY_F0 - g * DECT_CARRIER_WIDTH;
+	else
+		return DECT_FREQUENCY_F0 + (g - 9) * DECT_CARRIER_WIDTH;
+}
+
+static int __init dect_init_band(const struct dect_band_desc *desc)
+{
+	struct dect_band *band;
+	unsigned int size;
+	u8 carrier;
+
+	size = sizeof(*band) + desc->carriers * sizeof(band->frequency[0]);
+	band = kmalloc(size, GFP_KERNEL);
+	if (band == NULL)
+		return -ENOMEM;
+
+	band->band     = desc->band;
+	band->carriers = desc->carriers;
+
+	for (carrier = 0; carrier < 10; carrier++)
+		band->frequency[carrier] =
+			dect_calc_frequency(carrier);
+
+	for (; carrier < min(desc->carriers, desc->c2); carrier++)
+		band->frequency[carrier] =
+			dect_calc_frequency(carrier + desc->c1_off);
+
+	for (; carrier < desc->carriers; carrier++)
+		band->frequency[carrier] =
+			dect_calc_frequency(carrier + desc->c2_off);
+
+	printk("RF-band %u:\n", band->band);
+	for (carrier = 0; carrier < band->carriers; carrier++) {
+		printk("  carrier %u: %u.%03uMHz\n", carrier,
+		       band->frequency[carrier] / 1000,
+		       band->frequency[carrier] % 1000);
+	}
+	printk("\n");
+
+	dect_band[band->band] = band;
+	return 0;
+}
+
+int __init dect_transceiver_module_init(void)
+{
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(dect_band_desc); i++) {
+		err = dect_init_band(&dect_band_desc[i]);
+		if (err < 0)
+			goto err1;
+	}
+
+	dect_netlink_register_handlers(dect_transceiver_handlers,
+				       DECT_NEW_TRANSCEIVER,
+				       ARRAY_SIZE(dect_transceiver_handlers));
+	return 0;
+
+err1:
+	dect_transceiver_module_exit();
+	return err;
+}
+
+void dect_transceiver_module_exit(void)
+{
+	u8 band;
+
+	dect_netlink_unregister_handlers(DECT_NEW_TRANSCEIVER,
+				         ARRAY_SIZE(dect_transceiver_handlers));
+
+	for (band = 0; band < ARRAY_SIZE(dect_band); band++)
+		kfree(dect_band[band]);
+}
diff --git a/target/linux/generic/patches-3.10/780-dect-support.patch b/target/linux/generic/patches-3.10/780-dect-support.patch
new file mode 100644
index 0000000..6524db5
--- /dev/null
+++ b/target/linux/generic/patches-3.10/780-dect-support.patch
@@ -0,0 +1,155 @@
+From 18236ecd381ea60b4112b4ac1d33d95443c9f421 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel at makrotopia.org>
+Date: Thu, 26 Jun 2014 09:23:07 +0200
+Subject: [PATCH] add DECT stack
+
+Signed-off-by: Daniel Golle <daniel at makrotopia.org>
+---
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index ad7e322..0513edd 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -2472,6 +2472,14 @@ S:	Orphan
+ F:	Documentation/networking/decnet.txt
+ F:	net/decnet/
+ 
++DECT NETWORK PROTOCOL
++M:	Patrick McHardy <kaber at trash.net>
++S:	Maintained
++F:	net/dect
++F:	drivers/dect
++F:	include/net/dect
++F:	include/linux/dect*.h
++
+ DEFXX FDDI NETWORK DRIVER
+ M:	"Maciej W. Rozycki" <macro at linux-mips.org>
+ S:	Maintained
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index 9953a42..379993b 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -42,6 +42,8 @@ source "drivers/net/Kconfig"
+ 
+ source "drivers/isdn/Kconfig"
+ 
++source "drivers/dect/Kconfig"
++
+ # input before char - char/joystick depends on it. As does USB.
+ 
+ source "drivers/input/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 130abc1..3ef597d 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -103,6 +103,7 @@ obj-$(CONFIG_MD)		+= md/
+ obj-$(CONFIG_BT)		+= bluetooth/
+ obj-$(CONFIG_ACCESSIBILITY)	+= accessibility/
+ obj-$(CONFIG_ISDN)		+= isdn/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_EDAC)		+= edac/
+ obj-$(CONFIG_EISA)		+= eisa/
+ obj-y				+= lguest/
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index b10ce4b..55a7e0f 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -179,7 +179,8 @@ struct ucred {
+ #define AF_ALG		38	/* Algorithm sockets		*/
+ #define AF_NFC		39	/* NFC sockets			*/
+ #define AF_VSOCK	40	/* vSockets			*/
+-#define AF_MAX		41	/* For now.. */
++#define AF_DECT		41	/* DECT sockets			*/
++#define AF_MAX		42	/* For now.. */
+ 
+ /* Protocol families, same as address families. */
+ #define PF_UNSPEC	AF_UNSPEC
+@@ -223,6 +224,7 @@ struct ucred {
+ #define PF_ALG		AF_ALG
+ #define PF_NFC		AF_NFC
+ #define PF_VSOCK	AF_VSOCK
++#define PF_DECT		AF_DECT
+ #define PF_MAX		AF_MAX
+ 
+ /* Maximum queue length specifiable by listen.  */
+@@ -299,6 +301,7 @@ struct ucred {
+ #define SOL_CAIF	278
+ #define SOL_ALG		279
+ #define SOL_NFC		280
++#define SOL_DECT	281
+ 
+ /* IPX options */
+ #define IPX_TYPE	1
+diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
+index 1a85940..80b39e6 100644
+--- a/include/uapi/linux/netlink.h
++++ b/include/uapi/linux/netlink.h
+@@ -27,6 +27,7 @@
+ #define NETLINK_ECRYPTFS	19
+ #define NETLINK_RDMA		20
+ #define NETLINK_CRYPTO		21	/* Crypto layer */
++#define NETLINK_DECT		22	/* DECT */
+ 
+ #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
+ 
+diff --git a/net/Kconfig b/net/Kconfig
+index 2ddc904..ba6c8dc 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -312,6 +312,7 @@ source "net/ax25/Kconfig"
+ source "net/can/Kconfig"
+ source "net/irda/Kconfig"
+ source "net/bluetooth/Kconfig"
++source "net/dect/Kconfig"
+ source "net/rxrpc/Kconfig"
+ 
+ config FIB_RULES
+diff --git a/net/Makefile b/net/Makefile
+index 091e7b04..1761799 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_AX25)		+= ax25/
+ obj-$(CONFIG_CAN)		+= can/
+ obj-$(CONFIG_IRDA)		+= irda/
+ obj-$(CONFIG_BT)		+= bluetooth/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_SUNRPC)		+= sunrpc/
+ obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
+ obj-$(CONFIG_ATM)		+= atm/
+diff --git a/net/core/sock.c b/net/core/sock.c
+index d6d024c..f5f2b91 100644
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -210,7 +210,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
+   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
+   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
+   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
+-  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_MAX"
++  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_DECT"     ,
++  "sk_lock-AF_MAX"
+ };
+ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
+@@ -226,7 +227,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
+   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
+   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
+-  "slock-AF_NFC"   , "slock-AF_VSOCK"    ,"slock-AF_MAX"
++  "slock-AF_NFC"   , "slock-AF_VSOCK"    , "slock-AF_DECT"     ,
++  "slock-AF_MAX"
+ };
+ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
+@@ -242,7 +244,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
+   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
+   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
+-  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_MAX"
++  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_DECT"     ,
++  "clock-AF_MAX"
+ };
+ 
+ /*
+-- 
+2.0.0
+
diff --git a/target/linux/generic/patches-3.12/780-dect-support.patch b/target/linux/generic/patches-3.12/780-dect-support.patch
new file mode 100644
index 0000000..c59aa4a
--- /dev/null
+++ b/target/linux/generic/patches-3.12/780-dect-support.patch
@@ -0,0 +1,155 @@
+From 59337837fd79a39ad49f66d66c5e2f197066082e Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel at makrotopia.org>
+Date: Thu, 26 Jun 2014 09:23:07 +0200
+Subject: [PATCH] add DECT stack
+
+Signed-off-by: Daniel Golle <daniel at makrotopia.org>
+---
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index ffcaf97..bb0edf9 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -2587,6 +2587,14 @@ S:	Orphan
+ F:	Documentation/networking/decnet.txt
+ F:	net/decnet/
+ 
++DECT NETWORK PROTOCOL
++M:	Patrick McHardy <kaber at trash.net>
++S:	Maintained
++F:	net/dect
++F:	drivers/dect
++F:	include/net/dect
++F:	include/linux/dect*.h
++
+ DEFXX FDDI NETWORK DRIVER
+ M:	"Maciej W. Rozycki" <macro at linux-mips.org>
+ S:	Maintained
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index aa43b91..4fd728b 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -42,6 +42,8 @@ source "drivers/net/Kconfig"
+ 
+ source "drivers/isdn/Kconfig"
+ 
++source "drivers/dect/Kconfig"
++
+ # input before char - char/joystick depends on it. As does USB.
+ 
+ source "drivers/input/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index ab93de8..3f8c2ae 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -103,6 +103,7 @@ obj-$(CONFIG_MD)		+= md/
+ obj-$(CONFIG_BT)		+= bluetooth/
+ obj-$(CONFIG_ACCESSIBILITY)	+= accessibility/
+ obj-$(CONFIG_ISDN)		+= isdn/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_EDAC)		+= edac/
+ obj-$(CONFIG_EISA)		+= eisa/
+ obj-y				+= lguest/
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index 445ef75..e058944 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -180,7 +180,8 @@ struct ucred {
+ #define AF_ALG		38	/* Algorithm sockets		*/
+ #define AF_NFC		39	/* NFC sockets			*/
+ #define AF_VSOCK	40	/* vSockets			*/
+-#define AF_MAX		41	/* For now.. */
++#define AF_DECT		41	/* DECT sockets			*/
++#define AF_MAX		42	/* For now.. */
+ 
+ /* Protocol families, same as address families. */
+ #define PF_UNSPEC	AF_UNSPEC
+@@ -225,6 +226,7 @@ struct ucred {
+ #define PF_ALG		AF_ALG
+ #define PF_NFC		AF_NFC
+ #define PF_VSOCK	AF_VSOCK
++#define PF_DECT		AF_DECT
+ #define PF_MAX		AF_MAX
+ 
+ /* Maximum queue length specifiable by listen.  */
+@@ -301,6 +303,7 @@ struct ucred {
+ #define SOL_CAIF	278
+ #define SOL_ALG		279
+ #define SOL_NFC		280
++#define SOL_DECT	281
+ 
+ /* IPX options */
+ #define IPX_TYPE	1
+diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
+index 1a85940..80b39e6 100644
+--- a/include/uapi/linux/netlink.h
++++ b/include/uapi/linux/netlink.h
+@@ -27,6 +27,7 @@
+ #define NETLINK_ECRYPTFS	19
+ #define NETLINK_RDMA		20
+ #define NETLINK_CRYPTO		21	/* Crypto layer */
++#define NETLINK_DECT		22	/* DECT */
+ 
+ #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
+ 
+diff --git a/net/Kconfig b/net/Kconfig
+index b50dacc..2fb79bf 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -330,6 +330,7 @@ source "net/ax25/Kconfig"
+ source "net/can/Kconfig"
+ source "net/irda/Kconfig"
+ source "net/bluetooth/Kconfig"
++source "net/dect/Kconfig"
+ source "net/rxrpc/Kconfig"
+ 
+ config FIB_RULES
+diff --git a/net/Makefile b/net/Makefile
+index 9492e8c..3dfccc5 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_AX25)		+= ax25/
+ obj-$(CONFIG_CAN)		+= can/
+ obj-$(CONFIG_IRDA)		+= irda/
+ obj-$(CONFIG_BT)		+= bluetooth/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_SUNRPC)		+= sunrpc/
+ obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
+ obj-$(CONFIG_ATM)		+= atm/
+diff --git a/net/core/sock.c b/net/core/sock.c
+index 0b39e7a..b41feea 100644
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -213,7 +213,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
+   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
+   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
+   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
+-  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_MAX"
++  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_DECT"     ,
++  "sk_lock-AF_MAX"
+ };
+ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
+@@ -229,7 +230,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
+   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
+   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
+-  "slock-AF_NFC"   , "slock-AF_VSOCK"    ,"slock-AF_MAX"
++  "slock-AF_NFC"   , "slock-AF_VSOCK"    , "slock-AF_DECT"     ,
++  "slock-AF_MAX"
+ };
+ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
+@@ -245,7 +247,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
+   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
+   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
+-  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_MAX"
++  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_DECT"     ,
++  "clock-AF_MAX"
+ };
+ 
+ /*
+-- 
+2.0.0
+
diff --git a/target/linux/generic/patches-3.13/780-dect-support.patch b/target/linux/generic/patches-3.13/780-dect-support.patch
new file mode 100644
index 0000000..8a3c7e8
--- /dev/null
+++ b/target/linux/generic/patches-3.13/780-dect-support.patch
@@ -0,0 +1,155 @@
+From 431a43db2f6efc56a5866e44e49bddcb78da9345 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel at makrotopia.org>
+Date: Thu, 26 Jun 2014 09:23:07 +0200
+Subject: [PATCH] add DECT stack
+
+Signed-off-by: Daniel Golle <daniel at makrotopia.org>
+---
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 6a6e4ac..74fdca4 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -2607,6 +2607,14 @@ S:	Orphan
+ F:	Documentation/networking/decnet.txt
+ F:	net/decnet/
+ 
++DECT NETWORK PROTOCOL
++M:	Patrick McHardy <kaber at trash.net>
++S:	Maintained
++F:	net/dect
++F:	drivers/dect
++F:	include/net/dect
++F:	include/linux/dect*.h
++
+ DEFXX FDDI NETWORK DRIVER
+ M:	"Maciej W. Rozycki" <macro at linux-mips.org>
+ S:	Maintained
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index b3138fb..ff8bc73 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -42,6 +42,8 @@ source "drivers/net/Kconfig"
+ 
+ source "drivers/isdn/Kconfig"
+ 
++source "drivers/dect/Kconfig"
++
+ # input before char - char/joystick depends on it. As does USB.
+ 
+ source "drivers/input/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 3cc8214..d457a9f 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -105,6 +105,7 @@ obj-$(CONFIG_MD)		+= md/
+ obj-$(CONFIG_BT)		+= bluetooth/
+ obj-$(CONFIG_ACCESSIBILITY)	+= accessibility/
+ obj-$(CONFIG_ISDN)		+= isdn/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_EDAC)		+= edac/
+ obj-$(CONFIG_EISA)		+= eisa/
+ obj-y				+= lguest/
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index 445ef75..e058944 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -180,7 +180,8 @@ struct ucred {
+ #define AF_ALG		38	/* Algorithm sockets		*/
+ #define AF_NFC		39	/* NFC sockets			*/
+ #define AF_VSOCK	40	/* vSockets			*/
+-#define AF_MAX		41	/* For now.. */
++#define AF_DECT		41	/* DECT sockets			*/
++#define AF_MAX		42	/* For now.. */
+ 
+ /* Protocol families, same as address families. */
+ #define PF_UNSPEC	AF_UNSPEC
+@@ -225,6 +226,7 @@ struct ucred {
+ #define PF_ALG		AF_ALG
+ #define PF_NFC		AF_NFC
+ #define PF_VSOCK	AF_VSOCK
++#define PF_DECT		AF_DECT
+ #define PF_MAX		AF_MAX
+ 
+ /* Maximum queue length specifiable by listen.  */
+@@ -301,6 +303,7 @@ struct ucred {
+ #define SOL_CAIF	278
+ #define SOL_ALG		279
+ #define SOL_NFC		280
++#define SOL_DECT	281
+ 
+ /* IPX options */
+ #define IPX_TYPE	1
+diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
+index 1a85940..80b39e6 100644
+--- a/include/uapi/linux/netlink.h
++++ b/include/uapi/linux/netlink.h
+@@ -27,6 +27,7 @@
+ #define NETLINK_ECRYPTFS	19
+ #define NETLINK_RDMA		20
+ #define NETLINK_CRYPTO		21	/* Crypto layer */
++#define NETLINK_DECT		22	/* DECT */
+ 
+ #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
+ 
+diff --git a/net/Kconfig b/net/Kconfig
+index d334678..4a774b7 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -331,6 +331,7 @@ source "net/ax25/Kconfig"
+ source "net/can/Kconfig"
+ source "net/irda/Kconfig"
+ source "net/bluetooth/Kconfig"
++source "net/dect/Kconfig"
+ source "net/rxrpc/Kconfig"
+ 
+ config FIB_RULES
+diff --git a/net/Makefile b/net/Makefile
+index 8fa2f91..fc9e943 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_AX25)		+= ax25/
+ obj-$(CONFIG_CAN)		+= can/
+ obj-$(CONFIG_IRDA)		+= irda/
+ obj-$(CONFIG_BT)		+= bluetooth/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_SUNRPC)		+= sunrpc/
+ obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
+ obj-$(CONFIG_ATM)		+= atm/
+diff --git a/net/core/sock.c b/net/core/sock.c
+index 5393b4b..935f682 100644
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -213,7 +213,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
+   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
+   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
+   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
+-  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_MAX"
++  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_DECT"     ,
++  "sk_lock-AF_MAX"
+ };
+ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
+@@ -229,7 +230,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
+   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
+   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
+-  "slock-AF_NFC"   , "slock-AF_VSOCK"    ,"slock-AF_MAX"
++  "slock-AF_NFC"   , "slock-AF_VSOCK"    , "slock-AF_DECT"     ,
++  "slock-AF_MAX"
+ };
+ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
+@@ -245,7 +247,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
+   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
+   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
+-  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_MAX"
++  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_DECT"     ,
++  "clock-AF_MAX"
+ };
+ 
+ /*
+-- 
+2.0.0
+
diff --git a/target/linux/generic/patches-3.14/780-dect-support.patch b/target/linux/generic/patches-3.14/780-dect-support.patch
new file mode 100644
index 0000000..677b5e7
--- /dev/null
+++ b/target/linux/generic/patches-3.14/780-dect-support.patch
@@ -0,0 +1,155 @@
+From 1bbf9f45334914da7ebbb9fc7d3288dcf4adcf95 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel at makrotopia.org>
+Date: Thu, 26 Jun 2014 09:23:07 +0200
+Subject: [PATCH] add DECT stack
+
+Signed-off-by: Daniel Golle <daniel at makrotopia.org>
+---
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 900d98e..1204df4 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -2637,6 +2637,14 @@ S:	Orphan
+ F:	Documentation/networking/decnet.txt
+ F:	net/decnet/
+ 
++DECT NETWORK PROTOCOL
++M:	Patrick McHardy <kaber at trash.net>
++S:	Maintained
++F:	net/dect
++F:	drivers/dect
++F:	include/net/dect
++F:	include/linux/dect*.h
++
+ DEFXX FDDI NETWORK DRIVER
+ M:	"Maciej W. Rozycki" <macro at linux-mips.org>
+ S:	Maintained
+diff --git a/drivers/Kconfig b/drivers/Kconfig
+index b3138fb..ff8bc73 100644
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -42,6 +42,8 @@ source "drivers/net/Kconfig"
+ 
+ source "drivers/isdn/Kconfig"
+ 
++source "drivers/dect/Kconfig"
++
+ # input before char - char/joystick depends on it. As does USB.
+ 
+ source "drivers/input/Kconfig"
+diff --git a/drivers/Makefile b/drivers/Makefile
+index 8e3b8b0..9923ad6 100644
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -105,6 +105,7 @@ obj-$(CONFIG_MD)		+= md/
+ obj-$(CONFIG_BT)		+= bluetooth/
+ obj-$(CONFIG_ACCESSIBILITY)	+= accessibility/
+ obj-$(CONFIG_ISDN)		+= isdn/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_EDAC)		+= edac/
+ obj-$(CONFIG_EISA)		+= eisa/
+ obj-y				+= lguest/
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index 8e98297..82542d5 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -180,7 +180,8 @@ struct ucred {
+ #define AF_ALG		38	/* Algorithm sockets		*/
+ #define AF_NFC		39	/* NFC sockets			*/
+ #define AF_VSOCK	40	/* vSockets			*/
+-#define AF_MAX		41	/* For now.. */
++#define AF_DECT		41	/* DECT sockets			*/
++#define AF_MAX		42	/* For now.. */
+ 
+ /* Protocol families, same as address families. */
+ #define PF_UNSPEC	AF_UNSPEC
+@@ -225,6 +226,7 @@ struct ucred {
+ #define PF_ALG		AF_ALG
+ #define PF_NFC		AF_NFC
+ #define PF_VSOCK	AF_VSOCK
++#define PF_DECT		AF_DECT
+ #define PF_MAX		AF_MAX
+ 
+ /* Maximum queue length specifiable by listen.  */
+@@ -301,6 +303,7 @@ struct ucred {
+ #define SOL_CAIF	278
+ #define SOL_ALG		279
+ #define SOL_NFC		280
++#define SOL_DECT	281
+ 
+ /* IPX options */
+ #define IPX_TYPE	1
+diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
+index 1a85940..80b39e6 100644
+--- a/include/uapi/linux/netlink.h
++++ b/include/uapi/linux/netlink.h
+@@ -27,6 +27,7 @@
+ #define NETLINK_ECRYPTFS	19
+ #define NETLINK_RDMA		20
+ #define NETLINK_CRYPTO		21	/* Crypto layer */
++#define NETLINK_DECT		22	/* DECT */
+ 
+ #define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG
+ 
+diff --git a/net/Kconfig b/net/Kconfig
+index e411046..0fe96c29 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -338,6 +338,7 @@ source "net/ax25/Kconfig"
+ source "net/can/Kconfig"
+ source "net/irda/Kconfig"
+ source "net/bluetooth/Kconfig"
++source "net/dect/Kconfig"
+ source "net/rxrpc/Kconfig"
+ 
+ config FIB_RULES
+diff --git a/net/Makefile b/net/Makefile
+index cbbbe6d..5950cc1 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_AX25)		+= ax25/
+ obj-$(CONFIG_CAN)		+= can/
+ obj-$(CONFIG_IRDA)		+= irda/
+ obj-$(CONFIG_BT)		+= bluetooth/
++obj-$(CONFIG_DECT)		+= dect/
+ obj-$(CONFIG_SUNRPC)		+= sunrpc/
+ obj-$(CONFIG_AF_RXRPC)		+= rxrpc/
+ obj-$(CONFIG_ATM)		+= atm/
+diff --git a/net/core/sock.c b/net/core/sock.c
+index c0fc6bd..d139e7b 100644
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -213,7 +213,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
+   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
+   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
+   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
+-  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_MAX"
++  "sk_lock-AF_NFC"   , "sk_lock-AF_VSOCK"    , "sk_lock-AF_DECT"     ,
++  "sk_lock-AF_MAX"
+ };
+ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
+@@ -229,7 +230,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
+   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
+   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
+   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
+-  "slock-AF_NFC"   , "slock-AF_VSOCK"    ,"slock-AF_MAX"
++  "slock-AF_NFC"   , "slock-AF_VSOCK"    , "slock-AF_DECT"     ,
++  "slock-AF_MAX"
+ };
+ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
+@@ -245,7 +247,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
+   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
+   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
+   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
+-  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_MAX"
++  "clock-AF_NFC"   , "clock-AF_VSOCK"    , "clock-AF_DECT"     ,
++  "clock-AF_MAX"
+ };
+ 
+ /*
+-- 
+2.0.0
+
-- 
2.0.0

-------------- next part --------------
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list