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

Florian Fainelli florian at openwrt.org
Thu Jun 26 13:11:38 EDT 2014


2014-06-26 9:27 GMT-07:00 Daniel Golle <daniel at makrotopia.org>:
> 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

Please wait until there is a better way to get these drivers
delivered, the diffstat is just ridiculous, and we already carry a lot
of out of tree code (e.g: ocf-crypto). At least, make that code a
tarball for now and ask us to host it on downloads.openwrt.org if you
want to.

Thanks!

>
> 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 @@
>