diff options
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) +{ |