[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 @@
> +From 1bbf9f45334914da7ebbb9fc7d3288dcf4adcf95 Mon Sep 17 00:00:00 2001
> +From: Daniel Golle <daniel at makrotopia.org>
> +Date: Thu, 26 Jun 2014 09:23:07 +0200
> +Subject: [PATCH] add DECT stack
> +
> +Signed-off-by: Daniel Golle <daniel at makrotopia.org>
> +---
> +
> +diff --git a/MAINTAINERS b/MAINTAINERS
> +index 900d98e..1204df4 100644
> +--- a/MAINTAINERS
> ++++ b/MAINTAINERS
> +@@ -2637,6 +2637,14 @@ S: Orphan
> + F: Documentation/networking/decnet.txt
> + F: net/decnet/
> +
> ++DECT NETWORK PROTOCOL
> ++M: Patrick McHardy <kaber at trash.net>
> ++S: Maintained
> ++F: net/dect
> ++F: drivers/dect
> ++F: include/net/dect
> ++F: include/linux/dect*.h
> ++
> + DEFXX FDDI NETWORK DRIVER
> + M: "Maciej W. Rozycki" <macro at linux-mips.org>
> + S: Maintained
> +diff --git a/drivers/Kconfig b/drivers/Kconfig
> +index b3138fb..ff8bc73 100644
> +--- a/drivers/Kconfig
> ++++ b/drivers/Kconfig
> +@@ -42,6 +42,8 @@ source "drivers/net/Kconfig"
> +
> + source "drivers/isdn/Kconfig"
> +
> ++source "drivers/dect/Kconfig"
> ++
> + # input before char - char/joystick depends on it. As does USB.
> +
> + source "drivers/input/Kconfig"
> +diff --git a/drivers/Makefile b/drivers/Makefile
> +index 8e3b8b0..9923ad6 100644
> +--- a/drivers/Makefile
> ++++ b/drivers/Makefile
> +@@ -105,6 +105,7 @@ obj-$(CONFIG_MD) += md/
> + obj-$(CONFIG_BT) += bluetooth/
> + obj-$(CONFIG_ACCESSIBILITY) += accessibility/
> + obj-$(CONFIG_ISDN) += isdn/
> ++obj-$(CONFIG_DECT) += dect/
> + obj-$(CONFIG_EDAC) += edac/
> + obj-$(CONFIG_EISA) += eisa/
> + obj-y += lguest/
> +diff --git a/include/linux/socket.h b/include/linux/socket.h
> +index 8e98297..82542d5 100644
> +--- a/include/linux/socket.h
> ++++ b/include/linux/socket.h
> +@@ -180,7 +180,8 @@ struct ucred {
> + #define AF_ALG 38 /* Algorithm sockets */
> + #define AF_NFC 39 /* NFC sockets */
> + #define AF_VSOCK 40 /* vSockets */
> +-#define AF_MAX 41 /* For now.. */
> ++#define AF_DECT 41 /* DECT sockets */
> ++#define AF_MAX 42 /* For now.. */
> +
> + /* Protocol families, same as address families. */
> + #define PF_UNSPEC AF_UNSPEC
> +@@ -225,6 +226,7 @@ struct ucred {
> + #define PF_ALG AF_ALG
> + #define PF_NFC AF_NFC
> + #define PF_VSOCK AF_VSOCK
> ++#define PF_DECT AF_DECT
> + #define PF_MAX AF_MAX
> +
> + /* Maximum queue length specifiable by listen. */
> +@@ -301,6 +303,7 @@ struct ucred {
> + #define SOL_CAIF 278
> + #define SOL_ALG 279
> + #define SOL_NFC 280
> ++#define SOL_DECT 281
> +
> + /* IPX options */
> + #define IPX_TYPE 1
> +diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
> +index 1a85940..80b39e6 100644
> +--- a/include/uapi/linux/netlink.h
> ++++ b/include/uapi/linux/netlink.h
> +@@ -27,6 +27,7 @@
> + #define NETLINK_ECRYPTFS 19
> + #define NETLINK_RDMA 20
> + #define NETLINK_CRYPTO 21 /* Crypto layer */
> ++#define NETLINK_DECT 22 /* DECT */
> +
> + #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
> +
> +diff --git a/net/Kconfig b/net/Kconfig
> +index e411046..0fe96c29 100644
> +--- a/net/Kconfig
> ++++ b/net/Kconfig
> +@@ -338,6 +338,7 @@ source "net/ax25/Kconfig"
> + source "net/can/Kconfig"
> + source "net/irda/Kconfig"
> + source "net/bluetooth/Kconfig"
> ++source "net/dect/Kconfig"
> + source "net/rxrpc/Kconfig"
> +
> + config FIB_RULES
> +diff --git a/net/Makefile b/net/Makefile
> +index cbbbe6d..5950cc1 100644
> +--- a/net/Makefile
> ++++ b/net/Makefile
> +@@ -34,6 +34,7 @@ obj-$(CONFIG_AX25) += ax25/
> + obj-$(CONFIG_CAN) += can/
> + obj-$(CONFIG_IRDA) += irda/
> + obj-$(CONFIG_BT) += bluetooth/
> ++obj-$(CONFIG_DECT) += dect/
> + obj-$(CONFIG_SUNRPC) += sunrpc/
> + obj-$(CONFIG_AF_RXRPC) += rxrpc/
> + obj-$(CONFIG_ATM) += atm/
> +diff --git a/net/core/sock.c b/net/core/sock.c
> +index c0fc6bd..d139e7b 100644
> +--- a/net/core/sock.c
> ++++ b/net/core/sock.c
> +@@ -213,7 +213,8 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
> + "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
> + "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
> + "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
> +- "sk_lock-AF_NFC" , "sk_lock-AF_VSOCK" , "sk_lock-AF_MAX"
> ++ "sk_lock-AF_NFC" , "sk_lock-AF_VSOCK" , "sk_lock-AF_DECT" ,
> ++ "sk_lock-AF_MAX"
> + };
> + static const char *const af_family_slock_key_strings[AF_MAX+1] = {
> + "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
> +@@ -229,7 +230,8 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
> + "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
> + "slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
> + "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
> +- "slock-AF_NFC" , "slock-AF_VSOCK" ,"slock-AF_MAX"
> ++ "slock-AF_NFC" , "slock-AF_VSOCK" , "slock-AF_DECT" ,
> ++ "slock-AF_MAX"
> + };
> + static const char *const af_family_clock_key_strings[AF_MAX+1] = {
> + "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
> +@@ -245,7 +247,8 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
> + "clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
> + "clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
> + "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
> +- "clock-AF_NFC" , "clock-AF_VSOCK" , "clock-AF_MAX"
> ++ "clock-AF_NFC" , "clock-AF_VSOCK" , "clock-AF_DECT" ,
> ++ "clock-AF_MAX"
> + };
> +
> + /*
> +--
> +2.0.0
> +
> --
> 2.0.0
>
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel at lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>
--
Florian
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list