aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/dect/Kconfig11
-rw-r--r--drivers/dect/Makefile2
-rw-r--r--drivers/dect/coa/.gitignore4
-rw-r--r--drivers/dect/coa/Kconfig45
-rw-r--r--drivers/dect/coa/Makefile44
-rw-r--r--drivers/dect/coa/bin2c.c57
-rw-r--r--drivers/dect/coa/com_on_air.h99
-rw-r--r--drivers/dect/coa/com_on_air_cs.c271
-rw-r--r--drivers/dect/coa/com_on_air_pci.c164
-rw-r--r--drivers/dect/coa/dip_opcodes.h157
-rw-r--r--drivers/dect/coa/radio_lmx3161.c84
-rw-r--r--drivers/dect/coa/radio_u2785.c261
-rw-r--r--drivers/dect/coa/sc1442x.c1032
-rw-r--r--drivers/dect/coa/sc1442x_firmware.asm429
-rw-r--r--drivers/dect/coa/sc1442x_firmware.c73
-rw-r--r--drivers/dect/coa/sc1442x_firmware.h70
-rw-r--r--drivers/dect/vtrx/Kconfig5
-rw-r--r--drivers/dect/vtrx/Makefile2
-rw-r--r--drivers/dect/vtrx/mw_to_dbm.c164
-rw-r--r--drivers/dect/vtrx/vtrx-sysfs.c230
-rw-r--r--drivers/dect/vtrx/vtrx.c397
-rw-r--r--drivers/dect/vtrx/vtrx.h42
-rw-r--r--include/linux/Kbuild415
-rw-r--r--include/linux/dect.h206
-rw-r--r--include/linux/dect_netlink.h397
-rw-r--r--include/linux/netlink.h1
-rw-r--r--include/linux/socket.h5
-rw-r--r--include/net/dect/ccp.h110
-rw-r--r--include/net/dect/dect.h319
-rw-r--r--include/net/dect/dlc.h463
-rw-r--r--include/net/dect/dsc.h12
-rw-r--r--include/net/dect/identities.h194
-rw-r--r--include/net/dect/mac.h837
-rw-r--r--include/net/dect/mac_ccf.h251
-rw-r--r--include/net/dect/mac_csf.h604
-rw-r--r--include/net/dect/transceiver.h711
-rw-r--r--include/uapi/linux/netlink.h1
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/core/sock.c6
-rw-r--r--net/dect/Kconfig66
-rw-r--r--net/dect/Makefile17
-rw-r--r--net/dect/af_dect.c456
-rw-r--r--net/dect/ccp.c906
-rw-r--r--net/dect/core.c183
-rw-r--r--net/dect/dect_netlink.c152
-rw-r--r--net/dect/dlc.c282
-rw-r--r--net/dect/dlc_b_sap.c278
-rw-r--r--net/dect/dlc_cplane.c981
-rw-r--r--net/dect/dlc_lu1_sap.c475
-rw-r--r--net/dect/dlc_s_sap.c729
-rw-r--r--net/dect/dlc_uplane.c86
-rw-r--r--net/dect/dsc.c141
-rw-r--r--net/dect/identities.c222
-rw-r--r--net/dect/mac_ccf.c2103
-rw-r--r--net/dect/mac_csf.c5356
-rw-r--r--net/dect/raw.c268
-rw-r--r--net/dect/transceiver.c1062
61 files changed, 21946 insertions, 5 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3b955649c32..932e447f8d3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2347,6 +2347,14 @@ S: Orphan
F: Documentation/networking/decnet.txt
F: net/decnet/
+DECT NETWORK PROTOCOL
+M: Patrick McHardy <kaber@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@linux-mips.org>
S: Maintained
diff --git a/drivers/Kconfig b/drivers/Kconfig
index f5fb0722a63..88abb851d9b 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 7863b9fee50..315c638a98e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -100,6 +100,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/drivers/dect/Kconfig b/drivers/dect/Kconfig
new file mode 100644
index 00000000000..baba4c10412
--- /dev/null
+++ b/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/drivers/dect/Makefile b/drivers/dect/Makefile
new file mode 100644
index 00000000000..c58af58e2b8
--- /dev/null
+++ b/drivers/dect/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DECT_VTRX) += vtrx/
+obj-$(CONFIG_DECT_COA) += coa/
diff --git a/drivers/dect/coa/.gitignore b/drivers/dect/coa/.gitignore
new file mode 100644
index 00000000000..7890f992f86
--- /dev/null
+++ b/drivers/dect/coa/.gitignore
@@ -0,0 +1,4 @@
+bin2c
+*.p
+*.h.tmp
+*.bin
diff --git a/drivers/dect/coa/Kconfig b/drivers/dect/coa/Kconfig
new file mode 100644
index 00000000000..207193fed80
--- /dev/null
+++ b/drivers/dect/coa/Kconfig
@@ -0,0 +1,45 @@
+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.
+
+config DECT_COA_P64
+ depends on DECT_COA_PCI && !DECT_COA_CS && DECT_COA_FIRMWARE
+ bool "Enable P640 (wideband) support"
+ help
+ This option enables support for P640j packets, which are used for
+ wideband audio. This does not work with the PCMCIA devices.
diff --git a/drivers/dect/coa/Makefile b/drivers/dect/coa/Makefile
new file mode 100644
index 00000000000..2b5ac0ddd85
--- /dev/null
+++ b/drivers/dect/coa/Makefile
@@ -0,0 +1,44 @@
+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)
+ifeq ($(CONFIG_DECT_COA_P64),y)
+ASL_FLAGS = -D ENABLE_P64
+endif
+
+ASL = asl
+P2BIN = p2bin
+BIN2C = $(obj)/bin2c
+
+quiet_cmd_asl = ASL $<
+ cmd_asl = $(ASL) -q -c $< -o $(<:.asm=.p) $(ASL_FLAGS) -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/drivers/dect/coa/bin2c.c b/drivers/dect/coa/bin2c.c
new file mode 100644
index 00000000000..bab08fa5831
--- /dev/null
+++ b/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/drivers/dect/coa/com_on_air.h b/drivers/dect/coa/com_on_air.h
new file mode 100644
index 00000000000..37c2b0a077e
--- /dev/null
+++ b/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@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/drivers/dect/coa/com_on_air_cs.c b/drivers/dect/coa/com_on_air_cs.c
new file mode 100644
index 00000000000..e1f22318061
--- /dev/null
+++ b/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@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/drivers/dect/coa/com_on_air_pci.c b/drivers/dect/coa/com_on_air_pci.c
new file mode 100644
index 00000000000..609214c0036
--- /dev/null
+++ b/drivers/dect/coa/com_on_air_pci.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/drivers/dect/coa/dip_opcodes.h b/drivers/dect/coa/dip_opcodes.h
new file mode 100644
index 00000000000..bd50056d03d
--- /dev/null
+++ b/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/drivers/dect/coa/radio_lmx3161.c b/drivers/dect/coa/radio_lmx3161.c
new file mode 100644
index 00000000000..9f6d5aef1b1
--- /dev/null
+++ b/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@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/drivers/dect/coa/radio_u2785.c b/drivers/dect/coa/radio_u2785.c
new file mode 100644
index 00000000000..dab53a01011
--- /dev/null
+++ b/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@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/drivers/dect/coa/sc1442x.c b/drivers/dect/coa/sc1442x.c
new file mode 100644
index 00000000000..73b98693ea0
--- /dev/null
+++ b/drivers/dect/coa/sc1442x.c
@@ -0,0 +1,1032 @@
+/*
+ * 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@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,
+ [DECT_PACKET_P640j][DECT_B_UNPROTECTED][0][0] = RX_P640j,
+ [DECT_PACKET_P640j][DECT_B_UNPROTECTED][1][0] = RX_P640j_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,
+ [DECT_PACKET_P640j][DECT_B_UNPROTECTED][0] = TX_P640j,
+ [DECT_PACKET_P640j][DECT_B_UNPROTECTED][1] = TX_P640j_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);
+ sc1442x_write_cmd(dev, TX_P640j_Enc, JMP, LoadEncKey);
+ sc1442x_write_cmd(dev, RX_P640j_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);
+ sc1442x_write_cmd(dev, RX_P640j_Enc, JMP, LoadEncKey);
+ sc1442x_write_cmd(dev, TX_P640j_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 |
+#ifdef CONFIG_DECT_COA_P64
+ DECT_TRANSCEIVER_PACKET_P64 |
+#endif
+ 0,
+ .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/drivers/dect/coa/sc1442x_firmware.asm b/drivers/dect/coa/sc1442x_firmware.asm
new file mode 100644
index 00000000000..c8097f888c3
--- /dev/null
+++ b/drivers/dect/coa/sc1442x_firmware.asm
@@ -0,0 +1,429 @@
+ 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
+
+RX_P640j_Enc: JMP LoadEncKey
+RX_P640j: JMP Receive
+ B_BR SD_B_FIELD_OFF
+ JMP Transfer_P640j
+ WT 14 ; 15 - 1 (RTN)
+ B_XR
+ JMP ReceiveEnd
+ 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 ;
+
+TX_P640j_Enc: JMP LoadEncKey
+TX_P640j: JMP Transmit
+ B_BT SD_B_FIELD_OFF
+ WT 3 ; B_BT has 3 bits of latency
+ JMP Transfer_P640j
+ WT 11 ; 15 - 1 (RTN) - 3 (latency)
+ B_XT
+ WT 13 ; 8 (X/Z-Field) + 5
+ B_RST
+ JMP TransmitEnd
+ BR label_58
+
+Transfer_B: WT 45 ; 47 - 2 (JMP/JMP, JMP/RTN)
+ B_XON
+ WT 15
+ B_XOFF
+ RTN
+
+Transfer_P640j: JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ JMP Transfer_B
+ WT 46 ; 47 - 1 (RTN)
+ B_XON
+ RTN
+
+;-------------------------------------------------------------------------------
+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
+
+
+ReceiveInit: 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 7 ; | p: -33--26
+ RTN
+
+;-------------------------------------------------------------------------------
+; Enable the receiver, receive the S-field and the first 61 bits of the D-field
+; (93 bits total)
+;
+Receive: JMP ReceiveInit
+ 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: JMP ReceiveInit
+ 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
+ IFNDEF ENABLE_P64
+ MEN2
+ WT 182
+ MEN2N
+ WT 16
+ ENDIF
+ 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,RX_P640j,RX_P640j_Enc
+ SHARED TX_P00,TX_P32U,TX_P32U_Enc,TX_P640j,TX_P640j_Enc
+
+ SHARED DCS_IV,DCS_CK,DCS_STATE,DCS_STATE_SIZE
+ SHARED LoadEncKey,LoadEncState
diff --git a/drivers/dect/coa/sc1442x_firmware.c b/drivers/dect/coa/sc1442x_firmware.c
new file mode 100644
index 00000000000..d1fea604a31
--- /dev/null
+++ b/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, 0xef, 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, 0x84, 0x2d, 0x0e, 0x02, 0xa2, 0x01, 0x6f,
+ 0x02, 0x92, 0x01, 0x39, 0x02, 0xc7, 0x02, 0x84,
+ 0x2d, 0x0e, 0x02, 0xa0, 0x01, 0x76, 0x02, 0xc7,
+ 0x02, 0x84, 0x3c, 0x0e, 0x02, 0x63, 0x09, 0x0e,
+ 0x2b, 0x00, 0x02, 0xa2, 0x01, 0x76, 0x02, 0xa5,
+ 0x02, 0xb4, 0x01, 0x71, 0x02, 0xc7, 0x02, 0xa5,
+ 0x25, 0x0e, 0x02, 0xb1, 0x01, 0x72, 0x02, 0xc7,
+ 0x02, 0xa5, 0x34, 0x0e, 0x09, 0x03, 0x02, 0x63,
+ 0x09, 0x0b, 0x24, 0x00, 0x09, 0x0d, 0x20, 0x00,
+ 0x02, 0xb4, 0x01, 0x78, 0x09, 0x2d, 0x27, 0x00,
+ 0x09, 0x0f, 0x26, 0x00, 0x04, 0x00, 0x02, 0x5e,
+ 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e,
+ 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e, 0x02, 0x5e,
+ 0x09, 0x2e, 0x27, 0x00, 0x04, 0x00, 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, 0x07, 0x04, 0x00,
+ 0x02, 0x7b, 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, 0x02, 0x7b, 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, 0xcf, 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, 0xcf,
+ 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, 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, 0xb8, 0x09, 0xfa,
+ 0xea, 0x20, 0xed, 0x42, 0x28, 0x00, 0x09, 0x40,
+ 0x26, 0x00, 0x29, 0x00, 0x08, 0x14, 0x03, 0xe8,
+ 0x20, 0x00, 0x6b, 0x00, 0x08, 0x17, 0x01, 0xda,
+ 0x08, 0x17, 0xea, 0x00, 0x02, 0xb8, 0x02, 0x38,
+ 0x61, 0x00, 0x08, 0x16, 0x01, 0xda, 0x0f, 0x00,
+ 0xfa, 0x10, 0x09, 0x0a, 0xee, 0x00, 0x09, 0x0a,
+ 0xe9, 0x00, 0xe8, 0x04, 0x62, 0x00, 0x0b, 0x00,
+ 0x01, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
diff --git a/drivers/dect/coa/sc1442x_firmware.h b/drivers/dect/coa/sc1442x_firmware.h
new file mode 100644
index 00000000000..8d615bd10bb
--- /dev/null
+++ b/drivers/dect/coa/sc1442x_firmware.h
@@ -0,0 +1,70 @@
+#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 0xF8
+#define RFInit 0xB8
+#define SyncInit 0xD9
+#define Sync 0xDA
+#define SyncLock 0xEA
+#define SyncLoop 0xEE
+#define ClockSyncOn 0x86
+#define ClockSyncOff 0x8E
+#define ClockAdjust 0x8A
+#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 RX_P640j 0x44
+#define RX_P640j_Enc 0x43
+#define TX_P00 0x4B
+#define TX_P32U 0x4F
+#define TX_P32U_Enc 0x4E
+#define TX_P640j 0x54
+#define TX_P640j_Enc 0x53
+#define DCS_IV 0x70
+#define DCS_CK 0x78
+#define DCS_STATE 0x70
+#define DCS_STATE_SIZE 0xB
+#define LoadEncKey 0xC7
+#define LoadEncState 0xD4
+
+#endif /* SC1442X_FIRMWARE */
diff --git a/drivers/dect/vtrx/Kconfig b/drivers/dect/vtrx/Kconfig
new file mode 100644
index 00000000000..fb86eeefa8a
--- /dev/null
+++ b/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/drivers/dect/vtrx/Makefile b/drivers/dect/vtrx/Makefile
new file mode 100644
index 00000000000..08abbf52c91
--- /dev/null
+++ b/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/drivers/dect/vtrx/mw_to_dbm.c b/drivers/dect/vtrx/mw_to_dbm.c
new file mode 100644
index 00000000000..55122a88cd2
--- /dev/null
+++ b/drivers/dect/vtrx/mw_to_dbm.c
@@ -0,0 +1,164 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber@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/drivers/dect/vtrx/vtrx-sysfs.c b/drivers/dect/vtrx/vtrx-sysfs.c
new file mode 100644
index 00000000000..c89c85a0010
--- /dev/null
+++ b/drivers/dect/vtrx/vtrx-sysfs.c
@@ -0,0 +1,230 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber@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 <linux/export.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/drivers/dect/vtrx/vtrx.c b/drivers/dect/vtrx/vtrx.c
new file mode 100644
index 00000000000..d6a9084a3e1
--- /dev/null
+++ b/drivers/dect/vtrx/vtrx.c
@@ -0,0 +1,397 @@
+/*
+ * DECT virtual transceiver
+ *
+ * Copyright (c) 2010 Patrick McHardy <kaber@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/drivers/dect/vtrx/vtrx.h b/drivers/dect/vtrx/vtrx.h
new file mode 100644
index 00000000000..67aaba67bc2
--- /dev/null
+++ b/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/include/linux/Kbuild b/include/linux/Kbuild
new file mode 100644
index 00000000000..3d74fced165
--- /dev/null
+++ b/include/linux/Kbuild
@@ -0,0 +1,415 @@
+header-y += byteorder/
+header-y += can/
+header-y += caif/
+header-y += dvb/
+header-y += hdlc/
+header-y += hsi/
+header-y += isdn/
+header-y += mmc/
+header-y += nfsd/
+header-y += raid/
+header-y += spi/
+header-y += sunrpc/
+header-y += tc_act/
+header-y += tc_ematch/
+header-y += netfilter/
+header-y += netfilter_arp/
+header-y += netfilter_bridge/
+header-y += netfilter_ipv4/
+header-y += netfilter_ipv6/
+header-y += usb/
+header-y += wimax/
+
+objhdr-y += version.h
+
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/a.out.h \
+ $(srctree)/include/asm-$(SRCARCH)/a.out.h \
+ $(INSTALL_HDR_PATH)/include/asm-*/a.out.h),)
+header-y += a.out.h
+endif
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h \
+ $(srctree)/include/asm-$(SRCARCH)/kvm.h \
+ $(INSTALL_HDR_PATH)/include/asm-*/kvm.h),)
+header-y += kvm.h
+endif
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm_para.h \
+ $(srctree)/include/asm-$(SRCARCH)/kvm_para.h \
+ $(INSTALL_HDR_PATH)/include/asm-*/kvm_para.h),)
+header-y += kvm_para.h
+endif
+
+header-y += acct.h
+header-y += adb.h
+header-y += adfs_fs.h
+header-y += affs_hardblocks.h
+header-y += agpgart.h
+header-y += aio_abi.h
+header-y += apm_bios.h
+header-y += arcfb.h
+header-y += atalk.h
+header-y += atm.h
+header-y += atm_eni.h
+header-y += atm_he.h
+header-y += atm_idt77105.h
+header-y += atm_nicstar.h
+header-y += atm_tcp.h
+header-y += atm_zatm.h
+header-y += atmapi.h
+header-y += atmarp.h
+header-y += atmbr2684.h
+header-y += atmclip.h
+header-y += atmdev.h
+header-y += atmioc.h
+header-y += atmlec.h
+header-y += atmmpc.h
+header-y += atmppp.h
+header-y += atmsap.h
+header-y += atmsvc.h
+header-y += audit.h
+header-y += auto_fs.h
+header-y += auto_fs4.h
+header-y += auxvec.h
+header-y += ax25.h
+header-y += b1lli.h
+header-y += baycom.h
+header-y += bfs_fs.h
+header-y += binfmts.h
+header-y += blk_types.h
+header-y += blkpg.h
+header-y += blktrace_api.h
+header-y += bpqether.h
+header-y += bsg.h
+header-y += can.h
+header-y += capability.h
+header-y += capi.h
+header-y += cciss_defs.h
+header-y += cciss_ioctl.h
+header-y += cdk.h
+header-y += cdrom.h
+header-y += cgroupstats.h
+header-y += chio.h
+header-y += cm4000_cs.h
+header-y += cn_proc.h
+header-y += coda.h
+header-y += coda_psdev.h
+header-y += coff.h
+header-y += comstats.h
+header-y += connector.h
+header-y += const.h
+header-y += cramfs_fs.h
+header-y += cuda.h
+header-y += cyclades.h
+header-y += cycx_cfm.h
+header-y += dcbnl.h
+header-y += dccp.h
+header-y += dect.h
+header-y += dect_netlink.h
+header-y += dlm.h
+header-y += dlm_device.h
+header-y += dlm_netlink.h
+header-y += dlm_plock.h
+header-y += dlmconstants.h
+header-y += dm-ioctl.h
+header-y += dm-log-userspace.h
+header-y += dn.h
+header-y += dqblk_xfs.h
+header-y += edd.h
+header-y += efs_fs_sb.h
+header-y += elf-em.h
+header-y += elf-fdpic.h
+header-y += elf.h
+header-y += elfcore.h
+header-y += errno.h
+header-y += errqueue.h
+header-y += ethtool.h
+header-y += eventpoll.h
+header-y += fadvise.h
+header-y += falloc.h
+header-y += fanotify.h
+header-y += fb.h
+header-y += fcntl.h
+header-y += fd.h
+header-y += fdreg.h
+header-y += fib_rules.h
+header-y += fiemap.h
+header-y += filter.h
+header-y += firewire-cdev.h
+header-y += firewire-constants.h
+header-y += flat.h
+header-y += fs.h
+header-y += fsl_hypervisor.h
+header-y += fuse.h
+header-y += futex.h
+header-y += gameport.h
+header-y += gen_stats.h
+header-y += generic_serial.h
+header-y += genetlink.h
+header-y += gfs2_ondisk.h
+header-y += gigaset_dev.h
+header-y += hdlc.h
+header-y += hdlcdrv.h
+header-y += hdreg.h
+header-y += hid.h
+header-y += hiddev.h
+header-y += hidraw.h
+header-y += hpet.h
+header-y += hysdn_if.h
+header-y += i2c-dev.h
+header-y += i2c.h
+header-y += i2o-dev.h
+header-y += i8k.h
+header-y += icmp.h
+header-y += icmpv6.h
+header-y += if.h
+header-y += if_addr.h
+header-y += if_addrlabel.h
+header-y += if_alg.h
+header-y += if_arcnet.h
+header-y += if_arp.h
+header-y += if_bonding.h
+header-y += if_bridge.h
+header-y += if_cablemodem.h
+header-y += if_eql.h
+header-y += if_ether.h
+header-y += if_fc.h
+header-y += if_fddi.h
+header-y += if_frad.h
+header-y += if_hippi.h
+header-y += if_infiniband.h
+header-y += if_link.h
+header-y += if_ltalk.h
+header-y += if_packet.h
+header-y += if_phonet.h
+header-y += if_plip.h
+header-y += if_ppp.h
+header-y += if_pppol2tp.h
+header-y += if_pppox.h
+header-y += if_slip.h
+header-y += if_strip.h
+header-y += if_team.h
+header-y += if_tun.h
+header-y += if_tunnel.h
+header-y += if_vlan.h
+header-y += if_x25.h
+header-y += igmp.h
+header-y += in.h
+header-y += in6.h
+header-y += in_route.h
+header-y += sock_diag.h
+header-y += inet_diag.h
+header-y += unix_diag.h
+header-y += inotify.h
+header-y += input.h
+header-y += ioctl.h
+header-y += ip.h
+header-y += ip6_tunnel.h
+header-y += ip_vs.h
+header-y += ipc.h
+header-y += ipmi.h
+header-y += ipmi_msgdefs.h
+header-y += ipsec.h
+header-y += ipv6.h
+header-y += ipv6_route.h
+header-y += ipx.h
+header-y += irda.h
+header-y += irqnr.h
+header-y += isdn.h
+header-y += isdn_divertif.h
+header-y += isdn_ppp.h
+header-y += isdnif.h
+header-y += iso_fs.h
+header-y += ivtv.h
+header-y += ivtvfb.h
+header-y += ixjuser.h
+header-y += jffs2.h
+header-y += joystick.h
+header-y += kd.h
+header-y += kdev_t.h
+header-y += kernel.h
+header-y += kernelcapi.h
+header-y += kernel-page-flags.h
+header-y += kexec.h
+header-y += keyboard.h
+header-y += keyctl.h
+header-y += l2tp.h
+header-y += limits.h
+header-y += llc.h
+header-y += loop.h
+header-y += lp.h
+header-y += magic.h
+header-y += major.h
+header-y += map_to_7segment.h
+header-y += matroxfb.h
+header-y += mdio.h
+header-y += media.h
+header-y += mei.h
+header-y += mempolicy.h
+header-y += meye.h
+header-y += mii.h
+header-y += minix_fs.h
+header-y += mman.h
+header-y += mmtimer.h
+header-y += mqueue.h
+header-y += mroute.h
+header-y += mroute6.h
+header-y += msdos_fs.h
+header-y += msg.h
+header-y += mtio.h
+header-y += n_r3964.h
+header-y += nbd.h
+header-y += ncp.h
+header-y += ncp_fs.h
+header-y += ncp_mount.h
+header-y += ncp_no.h
+header-y += neighbour.h
+header-y += net.h
+header-y += net_dropmon.h
+header-y += net_tstamp.h
+header-y += netdevice.h
+header-y += netfilter.h
+header-y += netfilter_arp.h
+header-y += netfilter_bridge.h
+header-y += netfilter_decnet.h
+header-y += netfilter_ipv4.h
+header-y += netfilter_ipv6.h
+header-y += netlink.h
+header-y += netrom.h
+header-y += nfc.h
+header-y += nfs.h
+header-y += nfs2.h
+header-y += nfs3.h
+header-y += nfs4.h
+header-y += nfs4_mount.h
+header-y += nfs_fs.h
+header-y += nfs_idmap.h
+header-y += nfs_mount.h
+header-y += nfsacl.h
+header-y += nl80211.h
+header-y += nubus.h
+header-y += nvram.h
+header-y += omap3isp.h
+header-y += omapfb.h
+header-y += oom.h
+header-y += param.h
+header-y += parport.h
+header-y += patchkey.h
+header-y += pci.h
+header-y += pci_regs.h
+header-y += perf_event.h
+header-y += personality.h
+header-y += pfkeyv2.h
+header-y += pg.h
+header-y += phantom.h
+header-y += phonet.h
+header-y += pkt_cls.h
+header-y += pkt_sched.h
+header-y += pktcdvd.h
+header-y += pmu.h
+header-y += poll.h
+header-y += posix_types.h
+header-y += ppdev.h
+header-y += ppp-comp.h
+header-y += ppp-ioctl.h
+header-y += ppp_defs.h
+header-y += pps.h
+header-y += prctl.h
+header-y += ptp_clock.h
+header-y += ptrace.h
+header-y += qnx4_fs.h
+header-y += qnxtypes.h
+header-y += quota.h
+header-y += radeonfb.h
+header-y += random.h
+header-y += raw.h
+header-y += rds.h
+header-y += reboot.h
+header-y += reiserfs_fs.h
+header-y += reiserfs_xattr.h
+header-y += resource.h
+header-y += rfkill.h
+header-y += romfs_fs.h
+header-y += rose.h
+header-y += route.h
+header-y += rtc.h
+header-y += rtnetlink.h
+header-y += scc.h
+header-y += sched.h
+header-y += screen_info.h
+header-y += sdla.h
+header-y += seccomp.h
+header-y += securebits.h
+header-y += selinux_netlink.h
+header-y += sem.h
+header-y += serial.h
+header-y += serial_core.h
+header-y += serial_reg.h
+header-y += serio.h
+header-y += shm.h
+header-y += signal.h
+header-y += signalfd.h
+header-y += snmp.h
+header-y += socket.h
+header-y += sockios.h
+header-y += som.h
+header-y += sonet.h
+header-y += sonypi.h
+header-y += sound.h
+header-y += soundcard.h
+header-y += stat.h
+header-y += stddef.h
+header-y += string.h
+header-y += suspend_ioctls.h
+header-y += swab.h
+header-y += synclink.h
+header-y += sysctl.h
+header-y += sysinfo.h
+header-y += taskstats.h
+header-y += tcp.h
+header-y += telephony.h
+header-y += termios.h
+header-y += time.h
+header-y += times.h
+header-y += timex.h
+header-y += tiocl.h
+header-y += tipc.h
+header-y += tipc_config.h
+header-y += toshiba.h
+header-y += tty.h
+header-y += types.h
+header-y += udf_fs_i.h
+header-y += udp.h
+header-y += uinput.h
+header-y += uio.h
+header-y += ultrasound.h
+header-y += un.h
+header-y += unistd.h
+header-y += usbdevice_fs.h
+header-y += utime.h
+header-y += utsname.h
+header-y += uuid.h
+header-y += uvcvideo.h
+header-y += v4l2-dv-timings.h
+header-y += v4l2-mediabus.h
+header-y += v4l2-subdev.h
+header-y += veth.h
+header-y += vhost.h
+header-y += videodev2.h
+header-y += virtio_9p.h
+header-y += virtio_balloon.h
+header-y += virtio_blk.h
+header-y += virtio_config.h
+header-y += virtio_console.h
+header-y += virtio_ids.h
+header-y += virtio_net.h
+header-y += virtio_pci.h
+header-y += virtio_ring.h
+header-y += virtio_rng.h
+header-y += vt.h
+header-y += wait.h
+header-y += wanrouter.h
+header-y += watchdog.h
+header-y += wimax.h
+header-y += wireless.h
+header-y += x25.h
+header-y += xattr.h
+header-y += xfrm.h
diff --git a/include/linux/dect.h b/include/linux/dect.h
new file mode 100644
index 00000000000..e458a9c944e
--- /dev/null
+++ b/include/linux/dect.h
@@ -0,0 +1,206 @@
+#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
+#define DECT_DL_MAC_CONN_PARAMS 3
+
+enum dect_cipher_states {
+ DECT_CIPHER_DISABLED,
+ DECT_CIPHER_ENABLED,
+};
+
+/**
+ * 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,
+};
+
+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,
+};
+
+/**
+ * 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,
+};
+
+struct dect_mac_conn_params {
+ enum dect_mac_connection_types type;
+ enum dect_mac_service_types service;
+ enum dect_slot_types slot;
+};
+
+/**
+ * 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/include/linux/dect_netlink.h b/include/linux/dect_netlink.h
new file mode 100644
index 00000000000..7c01acabf39
--- /dev/null
+++ b/include/linux/dect_netlink.h
@@ -0,0 +1,397 @@
+#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
+ * @DECT_TRANSCEIVER_PACKET_P64: transceiver supports packet P640j
+ */
+enum dect_transceiver_features {
+ DECT_TRANSCEIVER_SLOW_HOPPING = 0x1,
+ DECT_TRANSCEIVER_PACKET_P64 = 0x2,
+};
+
+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/include/linux/netlink.h b/include/linux/netlink.h
index e0f746b7b95..87103292ce4 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -1,7 +1,6 @@
#ifndef __LINUX_NETLINK_H
#define __LINUX_NETLINK_H
-
#include <linux/capability.h>
#include <linux/skbuff.h>
#include <linux/export.h>
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 9a546ff853d..73c31074f95 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -178,7 +178,8 @@ struct ucred {
#define AF_CAIF 37 /* CAIF sockets */
#define AF_ALG 38 /* Algorithm sockets */
#define AF_NFC 39 /* NFC sockets */
-#define AF_MAX 40 /* For now.. */
+#define AF_DECT 40 /* DECT sockets */
+#define AF_MAX 41 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -221,6 +222,7 @@ struct ucred {
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
#define PF_NFC AF_NFC
+#define PF_DECT AF_DECT
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -296,6 +298,7 @@ struct ucred {
#define SOL_IUCV 277
#define SOL_CAIF 278
#define SOL_ALG 279
+#define SOL_DECT 280
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/net/dect/ccp.h b/include/net/dect/ccp.h
new file mode 100644
index 00000000000..5234c7d2d4f
--- /dev/null
+++ b/include/net/dect/ccp.h
@@ -0,0 +1,110 @@
+/*
+ * DECT MAC Layer - Cell Control Protocol (CCP)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/include/net/dect/dect.h b/include/net/dect/dect.h
new file mode 100644
index 00000000000..5e245c864a9
--- /dev/null
+++ b/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
+ * @nlportid: netlink socket port id
+ */
+struct dect_llme_req {
+ struct nlmsghdr nlh;
+ u32 nlportid;
+};
+
+#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/include/net/dect/dlc.h b/include/net/dect/dlc.h
new file mode 100644
index 00000000000..84d442c43c3
--- /dev/null
+++ b/include/net/dect/dlc.h
@@ -0,0 +1,463 @@
+/*
+ * DECT DLC Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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;
+ struct dect_mac_conn_params mcp;
+ enum dect_mac_conn_states state;
+ 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,
+ const struct dect_mac_conn_params *mcp);
+
+extern int dect_mac_con_cfm(struct dect_cluster *cl, u32 mcei,
+ const struct dect_mac_conn_params *mcp);
+extern int dect_mac_con_ind(struct dect_cluster *cl,
+ const struct dect_mbc_id *id,
+ const struct dect_mac_conn_params *mcp);
+
+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/include/net/dect/dsc.h b/include/net/dect/dsc.h
new file mode 100644
index 00000000000..423a646df27
--- /dev/null
+++ b/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/include/net/dect/identities.h b/include/net/dect/identities.h
new file mode 100644
index 00000000000..a924d358c29
--- /dev/null
+++ b/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/include/net/dect/mac.h b/include/net/dect/mac.h
new file mode 100644
index 00000000000..74c0a9ec9f4
--- /dev/null
+++ b/include/net/dect/mac.h
@@ -0,0 +1,837 @@
+/*
+ * DECT MAC Layer - Header and global definitions
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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;
+ u16 fmid;
+ u32 pmid;
+ union {
+ struct {
+ u8 lbn;
+ u8 ecn;
+ u8 type;
+ u8 service;
+ u8 slot;
+ bool cf;
+ u8 a_mod;
+ u8 bz_mod;
+ u8 bz_ext_mod;
+ u8 acr;
+ } attr;
+ struct {
+ u8 lbn;
+ u8 reason;
+ } rel;
+ };
+};
+
+/* 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)
+
+/**
+ * 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
+ * @ari: FT identifier
+ * @pmid: Portable MAC Identity
+ * @ecn: Exchanged Connection Number
+ * @service: Service type
+ */
+struct dect_mbc_id {
+ u32 mcei;
+ struct dect_ari ari;
+ struct dect_pmid pmid;
+ u8 ecn;
+};
+
+#endif /* _NET_DECT_MAC_H */
diff --git a/include/net/dect/mac_ccf.h b/include/net/dect/mac_ccf.h
new file mode 100644
index 00000000000..31ba6ab0f1e
--- /dev/null
+++ b/include/net/dect/mac_ccf.h
@@ -0,0 +1,251 @@
+/*
+ * DECT MAC Layer - Cluster Control Functions (CCF)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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
+ * @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;
+
+ struct dect_mbc_id id;
+ struct dect_mac_conn_params mcp;
+ enum dect_mbc_state state;
+
+ 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,
+ const struct dect_mac_conn_params *mcp);
+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);
+
+extern u8 dect_b_field_size(enum dect_slot_types slot);
+
+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 *,
+ const struct dect_mac_conn_params *, 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/include/net/dect/mac_csf.h b/include/net/dect/mac_csf.h
new file mode 100644
index 00000000000..301fdc1efba
--- /dev/null
+++ b/include/net/dect/mac_csf.h
@@ -0,0 +1,604 @@
+/*
+ * DECT MAC Layer - Cell Site Functions (CSF)
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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_ATTRIBUTES_SENT: Initiator: attributes-T 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_ATTRIBUTES_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_STOP_REQ_RCVD: Stop request received (FP)
+ * @DECT_TBC_ENC_STOP_REQ_SENT: Stop request sent (PP)
+ * @DECT_TBC_ENC_STOP_CFM_RCVD: Stop confirm received (PP)
+ * @DECT_TBC_ENC_STOP_CFM_SENT: Stop 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_STOP_REQ_RCVD,
+ DECT_TBC_ENC_STOP_REQ_SENT,
+ DECT_TBC_ENC_STOP_CFM_RCVD,
+ DECT_TBC_ENC_STOP_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;
+ struct dect_mac_conn_params mcp;
+ 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_mac_conn_params *, 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/include/net/dect/transceiver.h b/include/net/dect/transceiver.h
new file mode 100644
index 00000000000..65dfa4c8ea5
--- /dev/null
+++ b/include/net/dect/transceiver.h
@@ -0,0 +1,711 @@
+/*
+ * DECT Transceiver Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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_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,
+ const struct dect_channel_desc *chd,
+ u64 ck)
+{
+ trx->slots[chd->slot].ck = ck;
+ dect_set_flags(trx, chd->slot, DECT_SLOT_CIPHER);
+}
+
+static inline void dect_disable_cipher(struct dect_transceiver *trx,
+ const struct dect_channel_desc *chd)
+{
+ dect_clear_flags(trx, chd->slot, DECT_SLOT_CIPHER);
+ trx->slots[chd->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
+ * @features: Combined features 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 features;
+ 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/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 78d5b8a546d..084fd1c0358 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -26,6 +26,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 30b48f52313..a86094eb425 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 4f4ee083064..a049519bb21 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -35,6 +35,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 bc131d41968..d27ed2c0ff6 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -208,7 +208,7 @@ 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_MAX"
+ "sk_lock-AF_NFC" , "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" ,
@@ -224,7 +224,7 @@ 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_MAX"
+ "slock-AF_NFC" , "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" ,
@@ -240,7 +240,7 @@ 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_MAX"
+ "clock-AF_NFC" , "clock-AF_DECT" , "clock-AF_MAX"
};
/*
diff --git a/net/dect/Kconfig b/net/dect/Kconfig
new file mode 100644
index 00000000000..873500d57b1
--- /dev/null
+++ b/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
+ 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/net/dect/Makefile b/net/dect/Makefile
new file mode 100644
index 00000000000..7d7bb1439d8
--- /dev/null
+++ b/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/net/dect/af_dect.c b/net/dect/af_dect.c
new file mode 100644
index 00000000000..452df7f60ce
--- /dev/null
+++ b/net/dect/af_dect.c
@@ -0,0 +1,456 @@
+/*
+ * DECT sockets
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/net/dect/ccp.c b/net/dect/ccp.c
new file mode 100644
index 00000000000..d423a966e32
--- /dev/null
+++ b/net/dect/ccp.c
@@ -0,0 +1,906 @@
+/*
+ * DECT Cell Control Protocol
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/net/dect/core.c b/net/dect/core.c
new file mode 100644
index 00000000000..80a7dd89742
--- /dev/null
+++ b/net/dect/core.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber@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@trash.net>");
+MODULE_DESCRIPTION("DECT protocol stack");
+MODULE_LICENSE("GPL");
diff --git a/net/dect/dect_netlink.c b/net/dect/dect_netlink.c
new file mode 100644
index 00000000000..80527fb5e27
--- /dev/null
+++ b/net/dect/dect_netlink.c
@@ -0,0 +1,152 @@
+/*
+ * DECT netlink control interface
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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 <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();
+}
+
+static struct netlink_kernel_cfg dect_netlink_cfg = {
+ .groups = DECTNLGRP_MAX,
+ .input = dect_netlink_rcv,
+};
+
+int __init dect_netlink_module_init(void)
+{
+ struct sock *sk;
+
+ sk = netlink_kernel_create(&init_net, NETLINK_DECT, &dect_netlink_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/net/dect/dlc.c b/net/dect/dlc.c
new file mode 100644
index 00000000000..c2d287d922a
--- /dev/null
+++ b/net/dect/dlc.c
@@ -0,0 +1,282 @@
+/*
+ * DECT DLC Layer
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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,
+ const struct dect_mac_conn_params *mcp)
+{
+ struct dect_mbc_id mid = {
+ .mcei = mc->mcei,
+ .ari = mc->mci.ari,
+ .pmid = mc->mci.pmid,
+ .ecn = mc->mci.lcn,
+ };
+ int err;
+
+ err = dect_mac_con_req(mc->cl, &mid, mcp);
+ 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,
+ const struct dect_mac_conn_params *mcp)
+{
+ struct dect_mac_conn *mc;
+
+ mc = dect_mac_conn_get_by_mcei(cl, mcei);
+ if (WARN_ON(mc == NULL))
+ return -ENOENT;
+ mc->mcp = *mcp;
+
+ 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,
+ const struct dect_mac_conn_params *mcp)
+{
+ 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->mcp = *mcp;
+
+ 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/net/dect/dlc_b_sap.c b/net/dect/dlc_b_sap.c
new file mode 100644
index 00000000000..aaabeaefc78
--- /dev/null
+++ b/net/dect/dlc_b_sap.c
@@ -0,0 +1,278 @@
+/*
+ * DECT DLC B SAP sockets - DLC C-plane broadcast service access
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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 hlist_node *node;
+ struct sk_buff *skb2;
+ struct sock *sk, *prev = NULL;
+
+ spin_lock(&dect_bsap_lock);
+ sk_for_each(sk, node, &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@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/net/dect/dlc_cplane.c b/net/dect/dlc_cplane.c
new file mode 100644
index 00000000000..9365b9d5ca3
--- /dev/null
+++ b/net/dect/dlc_cplane.c
@@ -0,0 +1,981 @@
+/*
+ * DECT DLC C-plane
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/net/dect/dlc_lu1_sap.c b/net/dect/dlc_lu1_sap.c
new file mode 100644
index 00000000000..4ec946a695b
--- /dev/null
+++ b/net/dect/dlc_lu1_sap.c
@@ -0,0 +1,475 @@
+/*
+ * DECT DLC LU1 SAP sockets
+ *
+ * Copyright (c) 2009-2011 Patrick McHardy <kaber@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 need = dect_b_field_size(lu1->mc->mcp.slot);
+ u8 frame, slot, off, last_off;
+
+ /* 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[lu1->mc->mcp.slot][lu1->slot];
+ off = slot_offset_tbl[lu1->mc->mcp.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@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/net/dect/dlc_s_sap.c b/net/dect/dlc_s_sap.c
new file mode 100644
index 00000000000..396242a8d60
--- /dev/null
+++ b/net/dect/dlc_s_sap.c
@@ -0,0 +1,729 @@
+/*
+ * DECT DLC S SAP sockets - DLC C-plane data link service access
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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;
+ struct dect_mac_conn_params mcp;
+};
+
+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 hlist_node *n;
+ struct sock *sk;
+
+ // FIXME: wildcards
+ sk_for_each(sk, n, &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 hlist_node *n;
+ struct sock *sk;
+
+ pr_debug("lookup listener: lln %u sapi %u\n", dli->lln, sapi);
+ sk_for_each_bound(sk, n, &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);
+out:
+ spin_unlock(&dect_ssap_lock);
+ return lapc;
+
+err1:
+ sk_free(newsk);
+ goto out;
+}
+
+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, &ssap->mcp);
+ 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_mac_conn_params mcp;
+ struct dect_dl_encrypt dle;
+ int err;
+ u64 ck;
+
+ switch (optname) {
+ case DECT_DL_MAC_CONN_PARAMS:
+ if (optlen != sizeof(mcp))
+ return -EINVAL;
+ if (sk->sk_state != DECT_SK_RELEASED)
+ return -EISCONN;
+ if (copy_from_user(&mcp, optval, sizeof(mcp)))
+ return -EFAULT;
+
+ switch (mcp.service) {
+ case DECT_SERVICE_IN_MIN_DELAY:
+ case DECT_SERVICE_IPX_ENCODED_PROTECTED:
+ case DECT_SERVICE_IN_NORMAL_DELAY:
+ case DECT_SERVICE_UNKNOWN:
+ case DECT_SERVICE_C_CHANNEL_ONLY:
+ case DECT_SERVICE_IP_ERROR_DETECTION:
+ case DECT_SERVICE_IPQ_ERROR_DETECTION:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mcp.slot) {
+ case DECT_FULL_SLOT:
+ case DECT_HALF_SLOT:
+ case DECT_DOUBLE_SLOT:
+ case DECT_LONG_SLOT_640:
+ case DECT_LONG_SLOT_672:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssap->mcp = mcp;
+ err = 0;
+ break;
+ 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_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct dect_ssap *ssap = dect_ssap(sk);
+ struct dect_mac_conn_params *mcp;
+ int len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
+ switch (optname) {
+ case DECT_DL_MAC_CONN_PARAMS:
+ if (sk->sk_state != DECT_SK_ESTABLISH_PENDING &&
+ sk->sk_state != DECT_SK_ESTABLISHED)
+ return -ENOTCONN;
+
+ mcp = &ssap->lapc->lc->mc->mcp;
+ len = min_t(unsigned int, len, sizeof(*mcp));
+ if (copy_to_user(optval, mcp, len))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ return 0;
+}
+
+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.getsockopt = dect_ssap_getsockopt,
+ .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/net/dect/dlc_uplane.c b/net/dect/dlc_uplane.c
new file mode 100644
index 00000000000..6d8d3188e70
--- /dev/null
+++ b/net/dect/dlc_uplane.c
@@ -0,0 +1,86 @@
+/*
+ * DECT DLC U-plane
+ *
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/net/dect/dsc.c b/net/dect/dsc.c
new file mode 100644
index 00000000000..d3f597a0a8f
--- /dev/null
+++ b/net/dect/dsc.c
@@ -0,0 +1,141 @@
+/*
+ * DECT Standard Cipher
+ *
+ * Copyright (c) 2010 Erik Tews <e_tews@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/net/dect/identities.c b/net/dect/identities.c
new file mode 100644
index 00000000000..28fe80d346b
--- /dev/null
+++ b/net/dect/identities.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2009 Patrick McHardy <kaber@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/export.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/net/dect/mac_ccf.c b/net/dect/mac_ccf.c
new file mode 100644
index 00000000000..5ad9b026e5c
--- /dev/null
+++ b/net/dect/mac_ccf.c
@@ -0,0 +1,2103 @@
+/*
+ * DECT MAC Cluster Control Functions
+ *
+ * Copyright (c) 2009-2011 Patrick McHardy <kaber@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>
+
+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);
+
+u8 dect_b_field_size(enum dect_slot_types slot)
+{
+ static const u8 b_field_size[] = {
+ [DECT_FULL_SLOT] = 40,
+ [DECT_DOUBLE_SLOT] = 100,
+ [DECT_LONG_SLOT_640] = 80,
+ };
+ return b_field_size[slot];
+}
+EXPORT_SYMBOL_GPL(dect_b_field_size);
+
+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->mcp.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->mcp.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;
+ int err;
+
+ err = ch->ops->tbc_establish_req(ch, &tb->id, &mbc->mcp,
+ 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,
+ const struct dect_mac_conn_params *mcp)
+{
+ 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->mcp = *mcp;
+ if (mcp->service != DECT_SERVICE_IN_MIN_DELAY ||
+ mcp->slot != DECT_FULL_SLOT)
+ mbc->mcp.type = DECT_MAC_CONN_ADVANCED;
+ 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,
+ const struct dect_mac_conn_params *mcp,
+ 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.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->mcp = *mcp;
+ } 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->mcp);
+ 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->mcp);
+ 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 |
+ DECT_FPC_EXTENDED_FP_INFO;
+ si->efpc.fpc = DECT_EFPC_EXTENDED_FP_INFO2;
+ si->efpc2.fpc = DECT_EFPC2_LONG_SLOT_J640;
+ 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->nlportid = 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;
+
+ if (nla_put_u8(skb, DECTA_ARI_CLASS, ari->arc) ||
+ nla_put_u32(skb, DECTA_ARI_FPN, ari->fpn))
+ goto nla_put_failure;
+
+ switch (ari->arc) {
+ case DECT_ARC_A:
+ if (nla_put_u16(skb, DECTA_ARI_EMC, ari->emc))
+ goto nla_put_failure;
+ break;
+ case DECT_ARC_B:
+ if (nla_put_u16(skb, DECTA_ARI_EIC, ari->eic) ||
+ nla_put_u32(skb, DECTA_ARI_FPS, ari->fps))
+ goto nla_put_failure;
+ break;
+ case DECT_ARC_C:
+ if (nla_put_u16(skb, DECTA_ARI_POC, ari->poc) ||
+ nla_put_u32(skb, DECTA_ARI_FPS, ari->fps))
+ goto nla_put_failure;
+ break;
+ case DECT_ARC_D:
+ if (nla_put_u32(skb, DECTA_ARI_GOP, ari->gop))
+ goto nla_put_failure;
+ break;
+ case DECT_ARC_E:
+ if (nla_put_u16(skb, DECTA_ARI_FIL, ari->fil))
+ goto nla_put_failure;
+ 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))
+ goto nla_put_failure;
+ if (sari->tari &&
+ nla_put_flag(skb, DECTA_SARI_TARI))
+ goto nla_put_failure;
+ 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);
+ }
+
+ if (nla_put_u8(skb, DECTA_MAC_INFO_RPN, cl->rpn))
+ goto nla_put_failure;
+
+ if (si->mask & (1 << DECT_TM_TYPE_FPC)) {
+ if (nla_put_u32(skb, DECTA_MAC_INFO_FPC, si->fpc.fpc) ||
+ nla_put_u16(skb, DECTA_MAC_INFO_HLC, si->fpc.hlc))
+ goto nla_put_failure;
+ }
+
+ if (si->mask & (1 << DECT_TM_TYPE_EFPC)) {
+ if (nla_put_u16(skb, DECTA_MAC_INFO_EFPC, si->efpc.fpc) ||
+ nla_put_u32(skb, DECTA_MAC_INFO_EHLC, si->efpc.hlc))
+ goto nla_put_failure;
+ }
+
+ if (si->mask & (1 << DECT_TM_TYPE_EFPC2)) {
+ if (nla_put_u16(skb, DECTA_MAC_INFO_EFPC2, si->efpc2.fpc) ||
+ nla_put_u32(skb, DECTA_MAC_INFO_EHLC2, si->efpc2.hlc))
+ goto nla_put_failure;
+ }
+
+ 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;
+
+ if (nla_put_u8(skb, DECTA_MAC_INFO_RSSI,
+ res->rssi >> DECT_RSSI_AVG_SCALE) ||
+ dect_fill_ari(skb, &idi->pari, DECTA_MAC_INFO_PARI) < 0 ||
+ nla_put_u8(skb, DECTA_MAC_INFO_RPN, idi->rpn))
+ goto nla_put_failure;
+
+ 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 portid = res->lreq.nlportid;
+ 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, portid, DECTNLGRP_LLME, 1, GFP_ATOMIC);
+err:
+ if (err < 0)
+ netlink_set_err(dect_nlsk, portid, 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.nlportid);
+}
+
+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;
+
+ if (nla_put_u8(skb, DECTA_LLME_OP, op) ||
+ nla_put_u8(skb, DECTA_LLME_TYPE, type))
+ goto nla_put_failure;
+
+ 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;
+ if (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))
+ goto nla_put_failure;
+ 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;
+ if (nla_put_u32(skb, DECTA_MBC_MCEI, mbc->id.mcei) ||
+ nla_put_u8(skb, DECTA_MBC_SERVICE, mbc->mcp.service) ||
+ nla_put_u8(skb, DECTA_MBC_STATE, mbc->state) ||
+ nla_put_u8(skb, DECTA_MBC_CIPHER_STATE, mbc->cipher_state))
+ goto nla_put_failure;
+
+ stats = nla_nest_start(skb, DECTA_MBC_STATS);
+ if (stats == NULL)
+ goto nla_put_failure;
+ if (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))
+ goto nla_put_failure;
+ 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 portid, 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, portid, seq, type, sizeof(*dm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ dm = nlmsg_data(nlh);
+ dm->dm_index = cl->index;
+ if (nla_put_string(skb, DECTA_CLUSTER_NAME, cl->name) ||
+ nla_put_u8(skb, DECTA_CLUSTER_MODE, cl->mode) ||
+ 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)
+ if (nla_put_u8(skb, DECTA_LIST_ELEM, ch->rpn))
+ goto nla_put_failure;
+ 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 portid)
+{
+ 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, portid, seq, NLMSG_DONE);
+ if (err < 0) {
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto err;
+ }
+ nlmsg_notify(dect_nlsk, skb, portid, DECTNLGRP_CLUSTER, report,
+ GFP_KERNEL);
+err:
+ if (err < 0)
+ netlink_set_err(dect_nlsk, portid, 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 portid = 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, portid,
+ nlh->nlmsg_seq, NLMSG_DONE);
+ if (err < 0)
+ goto err1;
+ return nlmsg_unicast(dect_nlsk, skb, portid);
+
+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);
+MODULE_LICENSE("GPL");
diff --git a/net/dect/mac_csf.c b/net/dect/mac_csf.c
new file mode 100644
index 00000000000..ed6bef1dfb5
--- /dev/null
+++ b/net/dect/mac_csf.c
@@ -0,0 +1,5356 @@
+/*
+ * DECT MAC Cell Site Functions
+ *
+ * Copyright (c) 2009-2011 Patrick McHardy <kaber@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
+
+static void dect_notify_cell(u16 event, const struct dect_cell *cell,
+ const struct nlmsghdr *nlh, u32 portid);
+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_pkt_b_field_sizes[] = {
+ [DECT_PACKET_P00] = 0,
+ [DECT_PACKET_P32] = 40,
+ [DECT_PACKET_P640j] = 80,
+ [DECT_PACKET_P80] = 100,
+};
+
+static u8 dect_pkt_b_field_size(const struct dect_channel_desc *chd)
+{
+ return dect_pkt_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)->pkt == DECT_PACKET_P640j ? "P640j" : \
+ (chl)->pkt == DECT_PACKET_P672j ? "P672j" : "?", \
+ (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 %03llx rfcars %03x 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);
+}
+
+static void dect_chl_flush(struct dect_cell *cell)
+{
+ struct dect_channel_list *chl, *next;
+
+ list_for_each_entry_safe(chl, next, &cell->chanlists, list) {
+ list_del(&chl->list);
+ dect_chl_release(chl);
+ }
+}
+
+/**
+ * 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);
+ /* 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->attr.ecn = (t & DECT_CCTRL_ATTR_ECN_MASK) >> DECT_CCTRL_ATTR_ECN_SHIFT;
+ cctl->attr.lbn = (t & DECT_CCTRL_ATTR_LBN_MASK) >> DECT_CCTRL_ATTR_LBN_SHIFT;
+ cctl->attr.type = (t & DECT_CCTRL_ATTR_TYPE_MASK) >> DECT_CCTRL_ATTR_TYPE_SHIFT;
+ cctl->attr.service = (t & DECT_CCTRL_ATTR_SERVICE_MASK) >> DECT_CCTRL_ATTR_SERVICE_SHIFT;
+ cctl->attr.slot = (t & DECT_CCTRL_ATTR_SLOT_MASK) >> DECT_CCTRL_ATTR_SLOT_SHIFT;
+ cctl->attr.cf = (t & DECT_CCTRL_ATTR_CF_FLAG);
+ cctl->attr.a_mod = (t & DECT_CCTRL_ATTR_A_MOD_MASK) >> DECT_CCTRL_ATTR_A_MOD_SHIFT;
+ cctl->attr.bz_mod = (t & DECT_CCTRL_ATTR_BZ_MOD_MASK) >> DECT_CCTRL_ATTR_BZ_MOD_SHIFT;
+ cctl->attr.bz_ext_mod = (t & DECT_CCTRL_ATTR_BZ_EXT_MOD_MASK) >> DECT_CCTRL_ATTR_BZ_EXT_MOD_SHIFT;
+ cctl->attr.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->attr.ecn, cctl->attr.lbn,
+ cctl->attr.type, cctl->attr.service, cctl->attr.slot,
+ cctl->attr.cf, cctl->attr.a_mod, cctl->attr.bz_mod,
+ cctl->attr.bz_ext_mod, cctl->attr.acr);
+ return 0;
+}
+
+static u64 dect_build_cctrl_attr(const struct dect_cctrl *cctl)
+{
+ u64 t = 0;
+
+ t |= cctl->cmd;
+ t |= (u64)cctl->attr.ecn << DECT_CCTRL_ATTR_ECN_SHIFT;
+ t |= (u64)cctl->attr.lbn << DECT_CCTRL_ATTR_LBN_SHIFT;
+ t |= (u64)cctl->attr.type << DECT_CCTRL_ATTR_TYPE_SHIFT;
+ t |= (u64)cctl->attr.service << DECT_CCTRL_ATTR_SERVICE_SHIFT;
+ t |= (u64)cctl->attr.slot << DECT_CCTRL_ATTR_SLOT_SHIFT;
+ t |= cctl->attr.cf ? DECT_CCTRL_ATTR_CF_FLAG : 0;
+ t |= (u64)cctl->attr.a_mod << DECT_CCTRL_ATTR_A_MOD_SHIFT;
+ t |= (u64)cctl->attr.bz_mod << DECT_CCTRL_ATTR_BZ_MOD_SHIFT;
+ t |= (u64)cctl->attr.bz_ext_mod << DECT_CCTRL_ATTR_BZ_EXT_MOD_SHIFT;
+ t |= (u64)cctl->attr.acr << DECT_CCTRL_ATTR_ACR_SHIFT;
+ return t;
+}
+
+static int dect_parse_cctrl_release(struct dect_cctrl *cctl, u64 t)
+{
+ cctl->rel.lbn = (t & DECT_CCTRL_RELEASE_LBN_MASK) >>
+ DECT_CCTRL_RELEASE_LBN_SHIFT;
+ cctl->rel.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->rel.lbn, cctl->rel.reason);
+ return 0;
+}
+
+static u64 dect_build_cctrl_release(const struct dect_cctrl *cctl)
+{
+ u64 t = 0;
+
+ t |= cctl->cmd;
+ t |= (u64)cctl->rel.lbn << DECT_CCTRL_RELEASE_LBN_SHIFT;
+ t |= (u64)cctl->rel.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);
+
+ 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_ATTRIBUTES_SENT] = "ATTRIBUTES_SENT",
+ [DECT_TBC_OTHER_WAIT] = "OTHER_WAIT",
+ [DECT_TBC_ESTABLISHED] = "ESTABLISHED",
+ [DECT_TBC_RELEASING] = "RELEASING",
+ [DECT_TBC_RELEASED] = "RELEASED",
+};
+
+static struct sk_buff *dect_b_skb_alloc(const struct dect_channel_desc *chd)
+{
+ unsigned int b_field_size;
+ struct sk_buff *skb;
+
+ b_field_size = dect_pkt_b_field_size(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);
+ return skb;
+}
+
+static enum dect_b_identifications dect_pkt_to_bi(enum dect_packet_types pkt)
+{
+ switch (pkt) {
+ case DECT_PACKET_P08:
+ return DECT_BI_HALF_SLOT_REQUIRED;
+ case DECT_PACKET_P80:
+ return DECT_BI_DOUBLE_SLOT_REQUIRED;
+ case DECT_PACKET_P640j:
+ return DECT_BI_LONG_SLOT_640_REQUIRED;
+ case DECT_PACKET_P672j:
+ return DECT_BI_LONG_SLOT_672_REQUIRED;
+ default:
+ return DECT_BI_NONE;
+ }
+}
+
+static enum dect_packet_types dect_bi_to_pkt(enum dect_b_identifications b_id)
+{
+ switch (b_id) {
+ case DECT_BI_DOUBLE_SLOT_REQUIRED:
+ return DECT_PACKET_P80;
+ case DECT_BI_HALF_SLOT_REQUIRED:
+ return DECT_PACKET_P08;
+ case DECT_BI_LONG_SLOT_672_REQUIRED:
+ return DECT_PACKET_P672j;
+ case DECT_BI_LONG_SLOT_640_REQUIRED:
+ return DECT_PACKET_P640j;
+ default:
+ return DECT_PACKET_P32;
+ }
+}
+
+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->mcp.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(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 -ENOMEM;
+
+ cctl.cmd = cmd;
+ cctl.attr.ecn = tbc->id.ecn;
+ cctl.attr.lbn = tbc->id.lbn;
+ cctl.attr.type = DECT_CCTRL_TYPE_SYMETRIC_BEARER;
+ cctl.attr.service = tbc->mcp.service;
+ cctl.attr.cf = false;
+
+ cctl.attr.slot = tbc->mcp.slot;
+ cctl.attr.a_mod = DECT_MODULATION_2_LEVEL;
+ cctl.attr.bz_mod = DECT_MODULATION_2_LEVEL;
+ cctl.attr.bz_ext_mod = 7;
+ cctl.attr.acr = 0;
+
+ if (tbc->mcp.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_attributes_confirm(struct dect_tbc *tbc)
+{
+ tbc_debug(tbc, "TX ATTRIBUTES_T_CONFIRM\n");
+ return dect_tbc_send_attributes(tbc, DECT_CCTRL_ATTRIBUTES_T_CONFIRM);
+}
+
+static int dect_tbc_send_attributes_request(struct dect_tbc *tbc)
+{
+ tbc_debug(tbc, "TX ATTRIBUTES_T_REQUEST\n");
+ return dect_tbc_send_attributes(tbc, DECT_CCTRL_ATTRIBUTES_T_REQUEST);
+}
+
+static int dect_tbc_send_wait(struct dect_tbc *tbc)
+{
+ struct sk_buff *skb;
+
+ skb = dect_tbc_build_cctrl(tbc, DECT_CCTRL_WAIT);
+ if (skb == NULL)
+ return -ENOMEM;
+ 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.rel.reason = reason;
+
+ if (tbc->mcp.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->attr.ecn;
+ tbc->id.lbn = cctl->attr.lbn;
+ tbc->mcp.service = cctl->attr.service;
+ tbc->mcp.slot = cctl->attr.slot;
+
+ if (clh->ops->tbc_establish_ind(clh, &cell->handle, &tbc->id,
+ &tbc->mcp, 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;
+ 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->mcp.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;
+
+ if (dect_tbc_send_wait(tbc) < 0)
+ goto release;
+ 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;
+ if (dect_tbc_send_wait(tbc) < 0)
+ goto release;
+ dect_tbc_state_change(tbc, DECT_TBC_WAIT_RCVD);
+ } else {
+ tbc_debug(tbc, "Confirmed\n");
+ if (tbc->mcp.type == DECT_MAC_CONN_BASIC) {
+ if (dect_tbc_send_wait(tbc) < 0)
+ goto release;
+ dect_tbc_state_change(tbc, DECT_TBC_OTHER_WAIT);
+ } else {
+ if (dect_tbc_send_attributes_request(tbc) < 0)
+ goto release;
+ dect_tbc_state_change(tbc, DECT_TBC_ATTRIBUTES_SENT);
+ }
+ }
+ break;
+
+ case DECT_TBC_ATTRIBUTES_SENT:
+ if (cctl->cmd != DECT_CCTRL_ATTRIBUTES_T_CONFIRM) {
+ if (cctl->cmd != DECT_CCTRL_WAIT)
+ goto release;
+ if (dect_tbc_send_wait(tbc) < 0)
+ goto release;
+ } else {
+ if (dect_tbc_send_wait(tbc) < 0)
+ goto release;
+ 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, 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;
+ case DECT_TBC_ENC_STOP_REQ_RCVD:
+ tbc_debug(tbc, "TX encryption disabled\n");
+ dect_disable_cipher(tbc->txb.trx, &tbc->txb.chd);
+ /* fall through */
+ case DECT_TBC_ENC_STOP_CFM_SENT:
+ tbc->enc_state = DECT_TBC_ENC_STOP_CFM_SENT;
+ cmd = DECT_ENCCTRL_STOP_CONFIRM;
+ break;
+ case DECT_TBC_ENC_STOP_REQ_SENT:
+ cmd = DECT_ENCCTRL_STOP_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, 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, tbc->ck);
+ dect_tbc_event(tbc, DECT_TBC_CIPHER_ENABLED);
+ break;
+
+ case DECT_ENCCTRL_STOP_REQUEST:
+ if (cell->mode != DECT_MODE_FP)
+ break;
+
+ tbc->enc_state = DECT_TBC_ENC_STOP_REQ_RCVD;
+ tbc->enc_msg_cnt = 0;
+
+ dect_bearer_timer_add(cell, &tbc->txb, &tbc->enc_timer, 0);
+ break;
+ case DECT_ENCCTRL_STOP_CONFIRM:
+ if (tbc->enc_state == DECT_TBC_ENC_STOP_REQ_SENT) {
+ tbc->enc_state = DECT_TBC_ENC_STOP_CFM_RCVD;
+ tbc->enc_msg_cnt = 0;
+
+ dect_timer_del(&tbc->enc_timer);
+ tbc_debug(tbc, "TX encryption disabled\n");
+ dect_disable_cipher(tbc->txb.trx, &tbc->txb.chd);
+ }
+ if (tbc->enc_state == DECT_TBC_ENC_STOP_CFM_RCVD) {
+ skb = dect_tbc_build_encctrl(tbc, DECT_ENCCTRL_STOP_GRANT);
+ if (skb != NULL)
+ dect_tbc_queue_mac_control(tbc, skb);
+ }
+ break;
+ case DECT_ENCCTRL_STOP_GRANT:
+ if (tbc->enc_state != DECT_TBC_ENC_STOP_CFM_SENT)
+ break;
+ tbc->enc_state = DECT_TBC_CIPHER_DISABLED;
+
+ dect_timer_del(&tbc->enc_timer);
+ tbc_debug(tbc, "RX encryption disabled\n");
+ dect_disable_cipher(tbc->txb.trx, &tbc->txb.chd);
+ dect_tbc_event(tbc, DECT_TBC_CIPHER_DISABLED);
+ 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_pkt_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, tbc->ck);
+ dect_enable_cipher(tbc->txb.trx, &tbc->txb.chd, 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);
+ if (status == DECT_CIPHER_ENABLED) {
+ 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_debug(tbc, "RX encryption enabled\n");
+ dect_enable_cipher(tbc->rxb.trx, &tbc->rxb.chd, tbc->ck);
+ } else {
+ skb = dect_tbc_build_encctrl(tbc, DECT_ENCCTRL_STOP_REQUEST);
+ if (skb != NULL)
+ dect_tbc_queue_mac_control(tbc, skb);
+ tbc->enc_state = DECT_TBC_ENC_STOP_REQ_SENT;
+ }
+
+ tbc->enc_msg_cnt = 0;
+ dect_bearer_timer_add(cell, &tbc->txb, &tbc->enc_timer, 0);
+ 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, *b_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;
+
+ if (tbc->mcp.type == DECT_MAC_CONN_ADVANCED) {
+ b_skb = dect_b_skb_alloc(&tbc->txb.chd);
+ if (b_skb == NULL)
+ goto err1;
+ DECT_B_CB(b_skb)->id = dect_pkt_to_bi(tbc->txb.chd.pkt);
+ tbc->b_tx_skb = b_skb;
+ }
+
+ 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);
+ return;
+
+err1:
+ kfree_skb(skb);
+ //FIXME: indicate error
+}
+
+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_map_params(struct dect_channel_desc *chd,
+ const struct dect_mac_conn_params *mcp)
+{
+ switch (mcp->service) {
+ case DECT_SERVICE_IN_MIN_DELAY:
+ case DECT_SERVICE_IN_NORMAL_DELAY:
+ case DECT_SERVICE_UNKNOWN:
+ chd->b_fmt = DECT_B_UNPROTECTED;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ switch (mcp->slot) {
+ case DECT_FULL_SLOT:
+ chd->pkt = DECT_PACKET_P32;
+ break;
+ case DECT_DOUBLE_SLOT:
+ chd->pkt = DECT_PACKET_P80;
+ break;
+ case DECT_LONG_SLOT_640:
+ chd->pkt = DECT_PACKET_P640j;
+ break;
+ case DECT_LONG_SLOT_672:
+ chd->pkt = DECT_PACKET_P672j;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int dect_tbc_establish_req(const struct dect_cell_handle *ch,
+ const struct dect_tbc_id *id,
+ const struct dect_mac_conn_params *mcp,
+ 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;
+
+ memset(&tchd, 0, sizeof(tchd));
+ err = dect_tbc_map_params(&tchd, mcp);
+ if (err < 0)
+ goto err1;
+
+ /* Select TDD slot pair and reserve transceiver resources */
+ 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->mcp = *mcp;
+ 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 &&
+ tbc->state != DECT_TBC_RESPONSE_SENT);
+
+ /* Stop wait timer and send CONFIRM */
+ dect_timer_del(&tbc->wait_timer);
+ if (tbc->mcp.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;
+ enum dect_b_identifications b_id;
+ enum dect_packet_types pkt;
+ struct sk_buff *b_skb;
+ 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;
+ }
+
+ if (tm->type == DECT_TM_TYPE_BCCTRL)
+ pkt = DECT_PACKET_P32;
+ else
+ pkt = dect_bi_to_pkt(dect_parse_b_id(skb));
+
+ /* Select transceivers for RX/TX and reserve resources */
+ memcpy(&rchd, &ts->chd, sizeof(rchd));
+ rchd.pkt = pkt;
+ 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->mcp.type = DECT_MAC_CONN_BASIC;
+ tbc->mcp.service = DECT_SERVICE_IN_MIN_DELAY;
+ tbc->mcp.slot = DECT_FULL_SLOT;
+ } else {
+ /* Service is unknown at this time */
+ tbc->mcp.type = DECT_MAC_CONN_ADVANCED;
+ tbc->mcp.service = DECT_SERVICE_UNKNOWN;
+
+ b_skb = dect_b_skb_alloc(&tchd);
+ if (b_skb == NULL)
+ goto err4;
+ DECT_B_CB(b_skb)->id = b_id;
+ tbc->b_tx_skb = b_skb;
+ }
+
+ dect_tbc_state_change(tbc, DECT_TBC_REQ_RCVD);
+ tbc_debug(tbc, "RCV ACCESS_REQUEST: pkt: %u\n", pkt);
+
+ /* 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->mcp.type == DECT_MAC_CONN_BASIC) {
+ if (clh->ops->tbc_establish_ind(clh, &cell->handle, &tbc->id,
+ &tbc->mcp, 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;
+
+ dect_transceiver_unlock(trx);
+ memset(&irc->si, 0, sizeof(irc->si));
+ 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_channel_reserve(cell, trx, &chd);
+ 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;
+
+ if (bearer->ops->state == DECT_TRAFFIC_BEARER) {
+ tbc = bearer->tbc;
+ skb = tbc->b_tx_skb;
+ tbc->b_tx_skb = NULL;
+ }
+
+ if (skb == NULL) {
+ skb = dect_b_skb_alloc(&bearer->chd);
+ if (skb == NULL)
+ return NULL;
+ 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);
+
+ dect_chl_flush(cell);
+ /* 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);
+ if (cell->trg.features & DECT_TRANSCEIVER_PACKET_P64 &&
+ cell->si.efpc2.fpc & DECT_EFPC2_LONG_SLOT_J640)
+ dect_chl_schedule_update(cell, DECT_PACKET_P640j);
+ }
+
+ 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)
+{