diff --git a/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt new file mode 100644 index 000000000000..7c4a0cc370cf --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt @@ -0,0 +1,29 @@ +* Marvell International Ltd. NCI NFC Controller + +Required properties: +- compatible: Should be "mrvl,nfc-uart". + +Optional SoC specific properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. +- reset-n-io: Output GPIO pin used to reset the chip (active low). +- hci-muxed: Specifies that the chip is muxing NCI over HCI frames. + +Optional UART-based chip specific properties: +- flow-control: Specifies that the chip is using RTS/CTS. +- break-control: Specifies that the chip needs specific break management. + +Example (for ARM-based BeagleBoard Black with 88W8887 on UART5): + +&uart5 { + status = "okay"; + + nfcmrvluart: nfcmrvluart@5 { + compatible = "mrvl,nfc-uart"; + + reset-n-io = <&gpio3 16 0>; + + hci-muxed; + flow-control; + } +}; diff --git a/Documentation/devicetree/bindings/net/nfc/st21nfcb.txt b/Documentation/devicetree/bindings/net/nfc/st-nci.txt similarity index 87% rename from Documentation/devicetree/bindings/net/nfc/st21nfcb.txt rename to Documentation/devicetree/bindings/net/nfc/st-nci.txt index bb237072dbe9..d707588ed734 100644 --- a/Documentation/devicetree/bindings/net/nfc/st21nfcb.txt +++ b/Documentation/devicetree/bindings/net/nfc/st-nci.txt @@ -1,7 +1,7 @@ -* STMicroelectronics SAS. ST21NFCB NFC Controller +* STMicroelectronics SAS. ST NCI NFC Controller Required properties: -- compatible: Should be "st,st21nfcb-i2c". +- compatible: Should be "st,st21nfcb-i2c" or "st,st21nfcc-i2c". - clock-frequency: I²C work frequency. - reg: address on the bus - interrupt-parent: phandle for the interrupt gpio controller diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt index 7c89ca290ced..32b35a07abe4 100644 --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt @@ -18,6 +18,9 @@ Optional SoC Specific Properties: "IRQ Status Read" erratum. - en2-rf-quirk: Specify that the trf7970a being used has the "EN2 RF" erratum. +- t5t-rmb-extra-byte-quirk: Specify that the trf7970a has the erratum + where an extra byte is returned by Read Multiple Block commands issued + to Type 5 tags. Example (for ARM-based BeagleBone with TRF7970A on SPI1): @@ -39,6 +42,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1): autosuspend-delay = <30000>; irq-status-read-quirk; en2-rf-quirk; + t5t-rmb-extra-byte-quirk; status = "okay"; }; }; diff --git a/Documentation/nfc/nfc-hci.txt b/Documentation/nfc/nfc-hci.txt index 0686c9e211c2..0dc078cab972 100644 --- a/Documentation/nfc/nfc-hci.txt +++ b/Documentation/nfc/nfc-hci.txt @@ -122,7 +122,7 @@ This must be done from a context that can sleep. PHY Management -------------- -The physical link (i2c, ...) management is defined by the following struture: +The physical link (i2c, ...) management is defined by the following structure: struct nfc_phy_ops { int (*write)(void *dev_id, struct sk_buff *skb); diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 107714e4405f..722673cb785b 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -72,6 +72,6 @@ source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" source "drivers/nfc/st21nfca/Kconfig" -source "drivers/nfc/st21nfcb/Kconfig" +source "drivers/nfc/st-nci/Kconfig" source "drivers/nfc/nxp-nci/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index a4292d790f9b..368b6dfe71b3 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -12,7 +12,5 @@ obj-$(CONFIG_NFC_PORT100) += port100.o obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ -obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ +obj-$(CONFIG_NFC_ST_NCI) += st-nci/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ - -ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index 661e2c8143c4..daf352597ef8 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -211,7 +211,6 @@ flush: static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id) { struct microread_i2c_phy *phy = phy_id; - struct i2c_client *client; struct sk_buff *skb = NULL; int r; @@ -220,8 +219,6 @@ static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id) return IRQ_NONE; } - client = phy->i2c_dev; - if (phy->hard_fault != 0) return IRQ_HANDLED; diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig index 5e18afd9abe2..796be2411440 100644 --- a/drivers/nfc/nfcmrvl/Kconfig +++ b/drivers/nfc/nfcmrvl/Kconfig @@ -21,3 +21,14 @@ config NFC_MRVL_USB Say Y here to compile support for Marvell NFC-over-USB driver into the kernel or say M to compile it as module. + +config NFC_MRVL_UART + tristate "Marvell NFC-over-UART driver" + depends on NFC_MRVL && NFC_NCI_UART + help + Marvell NFC-over-UART driver. + + This driver provides support for Marvell NFC-over-UART devices + + Say Y here to compile support for Marvell NFC-over-UART driver + into the kernel or say M to compile it as module. diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile index 97a0de72dc01..775196274d1f 100644 --- a/drivers/nfc/nfcmrvl/Makefile +++ b/drivers/nfc/nfcmrvl/Makefile @@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o nfcmrvl_usb-y += usb.o obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o + +nfcmrvl_uart-y += uart.o +obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index ad4933cefbd1..4a8866d62941 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -17,6 +17,9 @@ */ #include +#include +#include +#include #include #include #include @@ -63,20 +66,25 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) return -EBUSY; + if (priv->config.hci_muxed) { + unsigned char *hdr; + unsigned char len = skb->len; + + hdr = (char *) skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); + hdr[0] = NFCMRVL_HCI_COMMAND_CODE; + hdr[1] = NFCMRVL_HCI_OGF; + hdr[2] = NFCMRVL_HCI_OCF; + hdr[3] = len; + } + return priv->if_ops->nci_send(priv, skb); } static int nfcmrvl_nci_setup(struct nci_dev *ndev) { - __u8 val; - - val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED; - nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val); - val = NFCMRVL_GPIO_PIN_NFC_ACTIVE; - nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val); - val = NFCMRVL_EXT_COEX_ENABLE; - nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val); + __u8 val = 1; + nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val); return 0; } @@ -88,11 +96,13 @@ static struct nci_ops nfcmrvl_nci_ops = { }; struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, - struct nfcmrvl_if_ops *ops, - struct device *dev) + struct nfcmrvl_if_ops *ops, + struct device *dev, + struct nfcmrvl_platform_data *pdata) { struct nfcmrvl_private *priv; int rc; + int headroom = 0; u32 protocols; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -103,13 +113,30 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->if_ops = ops; priv->dev = dev; + memcpy(&priv->config, pdata, sizeof(*pdata)); + + if (priv->config.reset_n_io) { + rc = devm_gpio_request_one(dev, + priv->config.reset_n_io, + GPIOF_OUT_INIT_LOW, + "nfcmrvl_reset_n"); + if (rc < 0) + nfc_err(dev, "failed to request reset_n io\n"); + } + + if (priv->config.hci_muxed) + headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE; + protocols = NFC_PROTO_JEWEL_MASK - | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_MIFARE_MASK + | NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_ISO15693_MASK | NFC_PROTO_NFC_DEP_MASK; - priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); + priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, + headroom, 0); if (!priv->ndev) { nfc_err(dev, "nci_allocate_device failed\n"); rc = -ENOMEM; @@ -118,6 +145,8 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, nci_set_drvdata(priv->ndev, priv); + nfcmrvl_chip_reset(priv); + rc = nci_register_device(priv->ndev); if (rc) { nfc_err(dev, "nci_register_device failed %d\n", rc); @@ -144,21 +173,84 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) } EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); -int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) { - struct sk_buff *skb; + if (priv->config.hci_muxed) { + if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE && + skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) { + /* Data packet, let's extract NCI payload */ + skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); + } else { + /* Skip this packet */ + kfree_skb(skb); + return 0; + } + } - skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC); - if (!skb) - return -ENOMEM; + if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + nci_recv_frame(priv->ndev, skb); + else { + /* Drop this packet since nobody wants it */ + kfree_skb(skb); + return 0; + } - memcpy(skb_put(skb, count), data, count); - nci_recv_frame(priv->ndev, skb); - - return count; + return 0; } EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); +void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) +{ + /* + * This function does not take care if someone is using the device. + * To be improved. + */ + + if (priv->config.reset_n_io) { + nfc_info(priv->dev, "reset the chip\n"); + gpio_set_value(priv->config.reset_n_io, 0); + usleep_range(5000, 10000); + gpio_set_value(priv->config.reset_n_io, 1); + } else + nfc_info(priv->dev, "no reset available on this interface\n"); +} + +#ifdef CONFIG_OF + +int nfcmrvl_parse_dt(struct device_node *node, + struct nfcmrvl_platform_data *pdata) +{ + int reset_n_io; + + reset_n_io = of_get_named_gpio(node, "reset-n-io", 0); + if (reset_n_io < 0) { + pr_info("no reset-n-io config\n"); + reset_n_io = 0; + } else if (!gpio_is_valid(reset_n_io)) { + pr_err("invalid reset-n-io GPIO\n"); + return reset_n_io; + } + pdata->reset_n_io = reset_n_io; + + if (of_find_property(node, "hci-muxed", NULL)) + pdata->hci_muxed = 1; + else + pdata->hci_muxed = 0; + + return 0; +} + +#else + +int nfcmrvl_parse_dt(struct device_node *node, + struct nfcmrvl_platform_data *pdata) +{ + return -ENODEV; +} + +#endif +EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt); + MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 54c4a956bd45..e5a7e5464f2e 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -16,6 +16,11 @@ * this warranty disclaimer. **/ +#ifndef _NFCMRVL_H_ +#define _NFCMRVL_H_ + +#include + /* Define private flags: */ #define NFCMRVL_NCI_RUNNING 1 @@ -27,11 +32,49 @@ #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB #define NFCMRVL_NCI_MAX_EVENT_SIZE 260 +/* +** NCI FW Parmaters +*/ + +#define NFCMRVL_PB_BAIL_OUT 0x11 + +/* +** HCI defines +*/ + +#define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04 +#define NFCMRVL_HCI_EVENT_CODE 0x04 +#define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF +#define NFCMRVL_HCI_COMMAND_CODE 0x01 +#define NFCMRVL_HCI_OGF 0x81 +#define NFCMRVL_HCI_OCF 0xFE + +enum nfcmrvl_phy { + NFCMRVL_PHY_USB = 0, + NFCMRVL_PHY_UART = 1, +}; + + struct nfcmrvl_private { - struct nci_dev *ndev; + unsigned long flags; + + /* Platform configuration */ + struct nfcmrvl_platform_data config; + + struct nci_dev *ndev; + + /* + ** PHY related information + */ + + /* PHY driver context */ void *drv_data; + /* PHY device */ struct device *dev; + /* PHY type */ + enum nfcmrvl_phy phy; + /* Low level driver ops */ struct nfcmrvl_if_ops *if_ops; }; @@ -42,7 +85,16 @@ struct nfcmrvl_if_ops { }; void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); -int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count); +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb); struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, - struct nfcmrvl_if_ops *ops, - struct device *dev); + struct nfcmrvl_if_ops *ops, + struct device *dev, + struct nfcmrvl_platform_data *pdata); + + +void nfcmrvl_chip_reset(struct nfcmrvl_private *priv); + +int nfcmrvl_parse_dt(struct device_node *node, + struct nfcmrvl_platform_data *pdata); + +#endif diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c new file mode 100644 index 000000000000..61442d6528a6 --- /dev/null +++ b/drivers/nfc/nfcmrvl/uart.c @@ -0,0 +1,225 @@ +/** + * Marvell NFC-over-UART driver + * + * Copyright (C) 2015, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include +#include +#include +#include +#include +#include "nfcmrvl.h" + +static unsigned int hci_muxed; +static unsigned int flow_control; +static unsigned int break_control; +static unsigned int reset_n_io; + +/* +** NFCMRVL NCI OPS +*/ + +static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv) +{ + return 0; +} + +static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv) +{ + return 0; +} + +static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv, + struct sk_buff *skb) +{ + struct nci_uart *nu = priv->drv_data; + + return nu->ops.send(nu, skb); +} + +static struct nfcmrvl_if_ops uart_ops = { + .nci_open = nfcmrvl_uart_nci_open, + .nci_close = nfcmrvl_uart_nci_close, + .nci_send = nfcmrvl_uart_nci_send, +}; + +#ifdef CONFIG_OF + +static int nfcmrvl_uart_parse_dt(struct device_node *node, + struct nfcmrvl_platform_data *pdata) +{ + struct device_node *matched_node; + int ret; + + matched_node = of_find_compatible_node(node, NULL, "mrvl,nfc-uart"); + if (!matched_node) + return -ENODEV; + + ret = nfcmrvl_parse_dt(matched_node, pdata); + if (ret < 0) { + pr_err("Failed to get generic entries\n"); + return ret; + } + + if (of_find_property(matched_node, "flow-control", NULL)) + pdata->flow_control = 1; + else + pdata->flow_control = 0; + + if (of_find_property(matched_node, "break-control", NULL)) + pdata->break_control = 1; + else + pdata->break_control = 0; + + return 0; +} + +#else + +static int nfcmrvl_uart_parse_dt(struct device_node *node, + struct nfcmrvl_platform_data *pdata) +{ + return -ENODEV; +} + +#endif + +/* +** NCI UART OPS +*/ + +static int nfcmrvl_nci_uart_open(struct nci_uart *nu) +{ + struct nfcmrvl_private *priv; + struct nfcmrvl_platform_data *pdata = NULL; + struct nfcmrvl_platform_data config; + + /* + * Platform data cannot be used here since usually it is already used + * by low level serial driver. We can try to retrieve serial device + * and check if DT entries were added. + */ + + if (nu->tty->dev->parent && nu->tty->dev->parent->of_node) + if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node, + &config) == 0) + pdata = &config; + + if (!pdata) { + pr_info("No platform data / DT -> fallback to module params\n"); + config.hci_muxed = hci_muxed; + config.reset_n_io = reset_n_io; + config.flow_control = flow_control; + config.break_control = break_control; + pdata = &config; + } + + priv = nfcmrvl_nci_register_dev(nu, &uart_ops, nu->tty->dev, pdata); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + priv->phy = NFCMRVL_PHY_UART; + + nu->drv_data = priv; + nu->ndev = priv->ndev; + + /* Set BREAK */ + if (priv->config.break_control && nu->tty->ops->break_ctl) + nu->tty->ops->break_ctl(nu->tty, -1); + + return 0; +} + +static void nfcmrvl_nci_uart_close(struct nci_uart *nu) +{ + nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data); +} + +static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb) +{ + return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data, + skb); +} + +static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu) +{ + struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; + + /* Remove BREAK to wake up the NFCC */ + if (priv->config.break_control && nu->tty->ops->break_ctl) { + nu->tty->ops->break_ctl(nu->tty, 0); + usleep_range(3000, 5000); + } +} + +static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu) +{ + struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; + + /* + ** To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him + ** up. we set BREAK. Once we will be ready to send again we will remove + ** it. + */ + if (priv->config.break_control && nu->tty->ops->break_ctl) + nu->tty->ops->break_ctl(nu->tty, -1); +} + +static struct nci_uart nfcmrvl_nci_uart = { + .owner = THIS_MODULE, + .name = "nfcmrvl_uart", + .driver = NCI_UART_DRIVER_MARVELL, + .ops = { + .open = nfcmrvl_nci_uart_open, + .close = nfcmrvl_nci_uart_close, + .recv = nfcmrvl_nci_uart_recv, + .tx_start = nfcmrvl_nci_uart_tx_start, + .tx_done = nfcmrvl_nci_uart_tx_done, + } +}; + +/* +** Module init +*/ + +static int nfcmrvl_uart_init_module(void) +{ + return nci_uart_register(&nfcmrvl_nci_uart); +} + +static void nfcmrvl_uart_exit_module(void) +{ + nci_uart_unregister(&nfcmrvl_nci_uart); +} + +module_init(nfcmrvl_uart_init_module); +module_exit(nfcmrvl_uart_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC-over-UART"); +MODULE_LICENSE("GPL v2"); + +module_param(flow_control, uint, 0); +MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init."); + +module_param(break_control, uint, 0); +MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal."); + +module_param(hci_muxed, uint, 0); +MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one."); + +module_param(reset_n_io, uint, 0); +MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal."); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 6cf15c1a2618..7d1fe436c9f6 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -26,7 +26,8 @@ #define VERSION "1.0" static struct usb_device_id nfcmrvl_table[] = { - { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046, + USB_CLASS_VENDOR_SPEC, 4, 1) }, { } /* Terminating entry */ }; @@ -69,18 +70,27 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) static void nfcmrvl_bulk_complete(struct urb *urb) { struct nfcmrvl_usb_drv_data *drv_data = urb->context; + struct sk_buff *skb; int err; - dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d", + dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n", urb, urb->status, urb->actual_length); if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) return; if (!urb->status) { - if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, - urb->actual_length) < 0) - nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); + skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length, + GFP_ATOMIC); + if (!skb) { + nfc_err(&drv_data->udev->dev, "failed to alloc mem\n"); + } else { + memcpy(skb_put(skb, urb->actual_length), + urb->transfer_buffer, urb->actual_length); + if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) + nfc_err(&drv_data->udev->dev, + "corrupted Rx packet\n"); + } } if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) @@ -292,6 +302,10 @@ static int nfcmrvl_probe(struct usb_interface *intf, struct nfcmrvl_private *priv; int i; struct usb_device *udev = interface_to_usbdev(intf); + struct nfcmrvl_platform_data config; + + /* No configuration for USB */ + memset(&config, 0, sizeof(config)); nfc_info(&udev->dev, "intf %p id %p\n", intf, id); @@ -329,11 +343,12 @@ static int nfcmrvl_probe(struct usb_interface *intf, init_usb_anchor(&drv_data->deferred); priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, - &drv_data->udev->dev); + &drv_data->udev->dev, &config); if (IS_ERR(priv)) return PTR_ERR(priv); drv_data->priv = priv; + drv_data->priv->phy = NFCMRVL_PHY_USB; priv->dev = &drv_data->udev->dev; usb_set_intfdata(intf, drv_data); diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile index c008be30bb18..c9ec7869dbd2 100644 --- a/drivers/nfc/nxp-nci/Makefile +++ b/drivers/nfc/nxp-nci/Makefile @@ -7,5 +7,3 @@ nxp-nci_i2c-objs = i2c.o obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o - -ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 17bd67dbebf0..2f77f1d03638 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -2,8 +2,10 @@ * I2C link layer for the NXP NCI driver * * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * Copyright (C) 2012-2015 Intel Corporation. All rights reserved. * * Authors: Clément Perrochaud + * Authors: Oleg Zhurakivskyy * * Derived from PN544 device driver: * Copyright (C) 2012 Intel Corporation. All rights reserved. @@ -23,12 +25,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include #include #include #include +#include #include #include #include @@ -48,6 +52,7 @@ struct nxp_nci_i2c_phy { unsigned int gpio_en; unsigned int gpio_fw; + unsigned int gpio_irq; int hard_fault; /* * < 0 if hardware error occurred (e.g. i2c err) @@ -308,6 +313,37 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) #endif +static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy) +{ + struct i2c_client *client = phy->i2c_dev; + struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq; + + gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2); + gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1); + gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0); + + if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) { + nfc_err(&client->dev, "No GPIOs\n"); + return -EINVAL; + } + + gpiod_direction_output(gpiod_en, 0); + gpiod_direction_output(gpiod_fw, 0); + gpiod_direction_input(gpiod_irq); + + client->irq = gpiod_to_irq(gpiod_irq); + if (client->irq < 0) { + nfc_err(&client->dev, "No IRQ\n"); + return -EINVAL; + } + + phy->gpio_en = desc_to_gpio(gpiod_en); + phy->gpio_fw = desc_to_gpio(gpiod_fw); + phy->gpio_irq = desc_to_gpio(gpiod_irq); + + return 0; +} + static int nxp_nci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -343,6 +379,11 @@ static int nxp_nci_i2c_probe(struct i2c_client *client, phy->gpio_en = pdata->gpio_en; phy->gpio_fw = pdata->gpio_fw; client->irq = pdata->irq; + } else if (ACPI_HANDLE(&client->dev)) { + r = nxp_nci_i2c_acpi_config(phy); + if (r < 0) + goto probe_exit; + goto nci_probe; } else { nfc_err(&client->dev, "No platform data\n"); r = -EINVAL; @@ -359,6 +400,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client, if (r < 0) goto probe_exit; +nci_probe: r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops, NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev); if (r < 0) @@ -397,10 +439,19 @@ static const struct of_device_id of_nxp_nci_i2c_match[] = { }; MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); +#ifdef CONFIG_ACPI +static struct acpi_device_id acpi_id[] = { + { "NXP7471" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, acpi_id); +#endif + static struct i2c_driver nxp_nci_i2c_driver = { .driver = { .name = NXP_NCI_I2C_DRIVER_NAME, .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(acpi_id), .of_match_table = of_match_ptr(of_nxp_nci_i2c_match), }, .probe = nxp_nci_i2c_probe, @@ -413,3 +464,4 @@ module_i2c_driver(nxp_nci_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers"); MODULE_AUTHOR("Clément Perrochaud "); +MODULE_AUTHOR("Oleg Zhurakivskyy "); diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 6fd986f5ac3e..fa75c53f3fa5 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -895,56 +895,35 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) return -ENODEV; /* Get EN GPIO from ACPI */ - gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1); + gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1, + GPIOD_OUT_LOW); if (IS_ERR(gpiod_en)) { - nfc_err(dev, - "Unable to get EN GPIO\n"); + nfc_err(dev, "Unable to get EN GPIO\n"); return -ENODEV; } - phy->gpio_en = desc_to_gpio(gpiod_en); - - /* Configuration EN GPIO */ - ret = gpiod_direction_output(gpiod_en, 0); - if (ret) { - nfc_err(dev, "Fail EN pin direction\n"); - return ret; - } + phy->gpio_en = desc_to_gpio(gpiod_en); /* Get FW GPIO from ACPI */ - gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2); + gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2, + GPIOD_OUT_LOW); if (IS_ERR(gpiod_fw)) { - nfc_err(dev, - "Unable to get FW GPIO\n"); + nfc_err(dev, "Unable to get FW GPIO\n"); return -ENODEV; } - phy->gpio_fw = desc_to_gpio(gpiod_fw); - - /* Configuration FW GPIO */ - ret = gpiod_direction_output(gpiod_fw, 0); - if (ret) { - nfc_err(dev, "Fail FW pin direction\n"); - return ret; - } + phy->gpio_fw = desc_to_gpio(gpiod_fw); /* Get IRQ GPIO */ - gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0); + gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0, + GPIOD_IN); if (IS_ERR(gpiod_irq)) { - nfc_err(dev, - "Unable to get IRQ GPIO\n"); + nfc_err(dev, "Unable to get IRQ GPIO\n"); return -ENODEV; } phy->gpio_irq = desc_to_gpio(gpiod_irq); - /* Configure IRQ GPIO */ - ret = gpiod_direction_input(gpiod_irq); - if (ret) { - nfc_err(dev, "Fail IRQ pin direction\n"); - return ret; - } - /* Map the pin to an IRQ */ ret = gpiod_to_irq(gpiod_irq); if (ret < 0) { diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig new file mode 100644 index 000000000000..fc3904c946ee --- /dev/null +++ b/drivers/nfc/st-nci/Kconfig @@ -0,0 +1,23 @@ +config NFC_ST_NCI + tristate "STMicroelectronics ST NCI NFC driver" + depends on NFC_NCI + default n + ---help--- + STMicroelectronics NFC NCI chips core driver. It implements the chipset + NCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st-nci. + Say N if unsure. + +config NFC_ST_NCI_I2C + tristate "NFC ST NCI i2c support" + depends on NFC_ST_NCI && I2C + ---help--- + This module adds support for an I2C interface to the + STMicroelectronics NFC NCI chips familly. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st-nci_i2c. + Say N if unsure. diff --git a/drivers/nfc/st-nci/Makefile b/drivers/nfc/st-nci/Makefile new file mode 100644 index 000000000000..0df157df3a94 --- /dev/null +++ b/drivers/nfc/st-nci/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for ST21NFCB NCI based NFC driver +# + +st-nci-objs = ndlc.o core.o st-nci_se.o +obj-$(CONFIG_NFC_ST_NCI) += st-nci.o + +st-nci_i2c-objs = i2c.o +obj-$(CONFIG_NFC_ST_NCI_I2C) += st-nci_i2c.o diff --git a/drivers/nfc/st-nci/core.c b/drivers/nfc/st-nci/core.c new file mode 100644 index 000000000000..c419d3943973 --- /dev/null +++ b/drivers/nfc/st-nci/core.c @@ -0,0 +1,179 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "st-nci.h" +#include "st-nci_se.h" + +#define DRIVER_DESC "NCI NFC driver for ST_NCI" + +#define ST_NCI1_X_PROPRIETARY_ISO15693 0x83 + +static int st_nci_init(struct nci_dev *ndev) +{ + struct nci_mode_set_cmd cmd; + + cmd.cmd_type = ST_NCI_SET_NFC_MODE; + cmd.mode = 1; + + return nci_prop_cmd(ndev, ST_NCI_CORE_PROP, + sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); +} + +static int st_nci_open(struct nci_dev *ndev) +{ + struct st_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (test_and_set_bit(ST_NCI_RUNNING, &info->flags)) + return 0; + + r = ndlc_open(info->ndlc); + if (r) + clear_bit(ST_NCI_RUNNING, &info->flags); + + return r; +} + +static int st_nci_close(struct nci_dev *ndev) +{ + struct st_nci_info *info = nci_get_drvdata(ndev); + + if (!test_bit(ST_NCI_RUNNING, &info->flags)) + return 0; + + ndlc_close(info->ndlc); + + clear_bit(ST_NCI_RUNNING, &info->flags); + + return 0; +} + +static int st_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct st_nci_info *info = nci_get_drvdata(ndev); + + skb->dev = (void *)ndev; + + if (!test_bit(ST_NCI_RUNNING, &info->flags)) + return -EBUSY; + + return ndlc_send(info->ndlc, skb); +} + +static __u32 st_nci_get_rfprotocol(struct nci_dev *ndev, + __u8 rf_protocol) +{ + return rf_protocol == ST_NCI1_X_PROPRIETARY_ISO15693 ? + NFC_PROTO_ISO15693_MASK : 0; +} + +static int st_nci_prop_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nci_req_complete(ndev, status); + return 0; +} + +static struct nci_prop_ops st_nci_prop_ops[] = { + { + .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, + ST_NCI_CORE_PROP), + .rsp = st_nci_prop_rsp_packet, + }, +}; + +static struct nci_ops st_nci_ops = { + .init = st_nci_init, + .open = st_nci_open, + .close = st_nci_close, + .send = st_nci_send, + .get_rfprotocol = st_nci_get_rfprotocol, + .discover_se = st_nci_discover_se, + .enable_se = st_nci_enable_se, + .disable_se = st_nci_disable_se, + .se_io = st_nci_se_io, + .hci_load_session = st_nci_hci_load_session, + .hci_event_received = st_nci_hci_event_received, + .hci_cmd_received = st_nci_hci_cmd_received, + .prop_ops = st_nci_prop_ops, + .n_prop_ops = ARRAY_SIZE(st_nci_prop_ops), +}; + +int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, + int phy_tailroom) +{ + struct st_nci_info *info; + int r; + u32 protocols; + + info = devm_kzalloc(ndlc->dev, + sizeof(struct st_nci_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK + | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_ISO15693_MASK + | NFC_PROTO_NFC_DEP_MASK; + + ndlc->ndev = nci_allocate_device(&st_nci_ops, protocols, + phy_headroom, phy_tailroom); + if (!ndlc->ndev) { + pr_err("Cannot allocate nfc ndev\n"); + return -ENOMEM; + } + info->ndlc = ndlc; + + nci_set_drvdata(ndlc->ndev, info); + + r = nci_register_device(ndlc->ndev); + if (r) { + pr_err("Cannot register nfc device to nci core\n"); + nci_free_device(ndlc->ndev); + return r; + } + + return st_nci_se_init(ndlc->ndev); +} +EXPORT_SYMBOL_GPL(st_nci_probe); + +void st_nci_remove(struct nci_dev *ndev) +{ + struct st_nci_info *info = nci_get_drvdata(ndev); + + ndlc_close(info->ndlc); + + nci_unregister_device(ndev); + nci_free_device(ndev); +} +EXPORT_SYMBOL_GPL(st_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st-nci/i2c.c similarity index 64% rename from drivers/nfc/st21nfcb/i2c.c rename to drivers/nfc/st-nci/i2c.c index 76a4cad41cec..06175ce769bb 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -1,6 +1,6 @@ /* - * I2C Link Layer for ST21NFCB NCI based Driver - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * I2C Link Layer for ST NCI NFC controller familly based Driver + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "ndlc.h" @@ -35,25 +35,23 @@ #define ST21NFCB_FRAME_HEADROOM 1 #define ST21NFCB_FRAME_TAILROOM 0 -#define ST21NFCB_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ -#define ST21NFCB_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */ +#define ST_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ +#define ST_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */ -#define ST21NFCB_NCI_I2C_DRIVER_NAME "st21nfcb_nci_i2c" +#define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c" -static struct i2c_device_id st21nfcb_nci_i2c_id_table[] = { - {ST21NFCB_NCI_DRIVER_NAME, 0}, +static struct i2c_device_id st_nci_i2c_id_table[] = { + {ST_NCI_DRIVER_NAME, 0}, {} }; -MODULE_DEVICE_TABLE(i2c, st21nfcb_nci_i2c_id_table); +MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); -struct st21nfcb_i2c_phy { +struct st_nci_i2c_phy { struct i2c_client *i2c_dev; struct llt_ndlc *ndlc; unsigned int gpio_reset; unsigned int irq_polarity; - - int powered; }; #define I2C_DUMP_SKB(info, skb) \ @@ -63,33 +61,26 @@ do { \ 16, 1, (skb)->data, (skb)->len, 0); \ } while (0) -static int st21nfcb_nci_i2c_enable(void *phy_id) +static int st_nci_i2c_enable(void *phy_id) { - struct st21nfcb_i2c_phy *phy = phy_id; + struct st_nci_i2c_phy *phy = phy_id; gpio_set_value(phy->gpio_reset, 0); usleep_range(10000, 15000); gpio_set_value(phy->gpio_reset, 1); - phy->powered = 1; usleep_range(80000, 85000); + if (phy->ndlc->powered == 0) + enable_irq(phy->i2c_dev->irq); + return 0; } -static void st21nfcb_nci_i2c_disable(void *phy_id) +static void st_nci_i2c_disable(void *phy_id) { - struct st21nfcb_i2c_phy *phy = phy_id; + struct st_nci_i2c_phy *phy = phy_id; - phy->powered = 0; - /* reset chip in order to flush clf */ - gpio_set_value(phy->gpio_reset, 0); - usleep_range(10000, 15000); - gpio_set_value(phy->gpio_reset, 1); -} - -static void st21nfcb_nci_remove_header(struct sk_buff *skb) -{ - skb_pull(skb, ST21NFCB_FRAME_HEADROOM); + disable_irq_nosync(phy->i2c_dev->irq); } /* @@ -97,13 +88,13 @@ static void st21nfcb_nci_remove_header(struct sk_buff *skb) * It must return either zero for success, or <0 for error. * In addition, it must not alter the skb */ -static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) +static int st_nci_i2c_write(void *phy_id, struct sk_buff *skb) { int r = -1; - struct st21nfcb_i2c_phy *phy = phy_id; + struct st_nci_i2c_phy *phy = phy_id; struct i2c_client *client = phy->i2c_dev; - I2C_DUMP_SKB("st21nfcb_nci_i2c_write", skb); + I2C_DUMP_SKB("st_nci_i2c_write", skb); if (phy->ndlc->hard_fault != 0) return phy->ndlc->hard_fault; @@ -121,8 +112,6 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) r = 0; } - st21nfcb_nci_remove_header(skb); - return r; } @@ -135,40 +124,40 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) * at end of read) * -EREMOTEIO : i2c read error (fatal) * -EBADMSG : frame was incorrect and discarded - * (value returned from st21nfcb_nci_i2c_repack) + * (value returned from st_nci_i2c_repack) * -EIO : if no ST21NFCB_SOF_EOF is found after reaching * the read length end sequence */ -static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, +static int st_nci_i2c_read(struct st_nci_i2c_phy *phy, struct sk_buff **skb) { int r; u8 len; - u8 buf[ST21NFCB_NCI_I2C_MAX_SIZE]; + u8 buf[ST_NCI_I2C_MAX_SIZE]; struct i2c_client *client = phy->i2c_dev; - r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + r = i2c_master_recv(client, buf, ST_NCI_I2C_MIN_SIZE); if (r < 0) { /* Retry, chip was in standby */ usleep_range(1000, 4000); - r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + r = i2c_master_recv(client, buf, ST_NCI_I2C_MIN_SIZE); } - if (r != ST21NFCB_NCI_I2C_MIN_SIZE) + if (r != ST_NCI_I2C_MIN_SIZE) return -EREMOTEIO; len = be16_to_cpu(*(__be16 *) (buf + 2)); - if (len > ST21NFCB_NCI_I2C_MAX_SIZE) { + if (len > ST_NCI_I2C_MAX_SIZE) { nfc_err(&client->dev, "invalid frame len\n"); return -EBADMSG; } - *skb = alloc_skb(ST21NFCB_NCI_I2C_MIN_SIZE + len, GFP_KERNEL); + *skb = alloc_skb(ST_NCI_I2C_MIN_SIZE + len, GFP_KERNEL); if (*skb == NULL) return -ENOMEM; - skb_reserve(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); - skb_put(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); - memcpy((*skb)->data, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + skb_reserve(*skb, ST_NCI_I2C_MIN_SIZE); + skb_put(*skb, ST_NCI_I2C_MIN_SIZE); + memcpy((*skb)->data, buf, ST_NCI_I2C_MIN_SIZE); if (!len) return 0; @@ -180,7 +169,7 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, } skb_put(*skb, len); - memcpy((*skb)->data + ST21NFCB_NCI_I2C_MIN_SIZE, buf, len); + memcpy((*skb)->data + ST_NCI_I2C_MIN_SIZE, buf, len); I2C_DUMP_SKB("i2c frame read", *skb); @@ -192,9 +181,9 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, * * On ST21NFCB, IRQ goes in idle state when read starts. */ -static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) +static irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id) { - struct st21nfcb_i2c_phy *phy = phy_id; + struct st_nci_i2c_phy *phy = phy_id; struct i2c_client *client; struct sk_buff *skb = NULL; int r; @@ -210,12 +199,12 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) if (phy->ndlc->hard_fault) return IRQ_HANDLED; - if (!phy->powered) { - st21nfcb_nci_i2c_disable(phy); + if (!phy->ndlc->powered) { + st_nci_i2c_disable(phy); return IRQ_HANDLED; } - r = st21nfcb_nci_i2c_read(phy, &skb); + r = st_nci_i2c_read(phy, &skb); if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG) return IRQ_HANDLED; @@ -225,15 +214,15 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) } static struct nfc_phy_ops i2c_phy_ops = { - .write = st21nfcb_nci_i2c_write, - .enable = st21nfcb_nci_i2c_enable, - .disable = st21nfcb_nci_i2c_disable, + .write = st_nci_i2c_write, + .enable = st_nci_i2c_enable, + .disable = st_nci_i2c_disable, }; #ifdef CONFIG_OF -static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +static int st_nci_i2c_of_request_resources(struct i2c_client *client) { - struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); struct device_node *pp; int gpio; int r; @@ -264,16 +253,16 @@ static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) return 0; } #else -static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +static int st_nci_i2c_of_request_resources(struct i2c_client *client) { return -ENODEV; } #endif -static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client) +static int st_nci_i2c_request_resources(struct i2c_client *client) { - struct st21nfcb_nfc_platform_data *pdata; - struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + struct st_nci_nfc_platform_data *pdata; + struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); int r; pdata = client->dev.platform_data; @@ -296,11 +285,11 @@ static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client) return 0; } -static int st21nfcb_nci_i2c_probe(struct i2c_client *client, +static int st_nci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct st21nfcb_i2c_phy *phy; - struct st21nfcb_nfc_platform_data *pdata; + struct st_nci_i2c_phy *phy; + struct st_nci_nfc_platform_data *pdata; int r; dev_dbg(&client->dev, "%s\n", __func__); @@ -311,7 +300,7 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, return -ENODEV; } - phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy), + phy = devm_kzalloc(&client->dev, sizeof(struct st_nci_i2c_phy), GFP_KERNEL); if (!phy) return -ENOMEM; @@ -322,13 +311,13 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (!pdata && client->dev.of_node) { - r = st21nfcb_nci_i2c_of_request_resources(client); + r = st_nci_i2c_of_request_resources(client); if (r) { nfc_err(&client->dev, "No platform data\n"); return r; } } else if (pdata) { - r = st21nfcb_nci_i2c_request_resources(client); + r = st_nci_i2c_request_resources(client); if (r) { nfc_err(&client->dev, "Cannot get platform resources\n"); @@ -349,50 +338,48 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, } r = devm_request_threaded_irq(&client->dev, client->irq, NULL, - st21nfcb_nci_irq_thread_fn, + st_nci_irq_thread_fn, phy->irq_polarity | IRQF_ONESHOT, - ST21NFCB_NCI_DRIVER_NAME, phy); + ST_NCI_DRIVER_NAME, phy); if (r < 0) nfc_err(&client->dev, "Unable to register IRQ handler\n"); return r; } -static int st21nfcb_nci_i2c_remove(struct i2c_client *client) +static int st_nci_i2c_remove(struct i2c_client *client) { - struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); dev_dbg(&client->dev, "%s\n", __func__); ndlc_remove(phy->ndlc); - if (phy->powered) - st21nfcb_nci_i2c_disable(phy); - return 0; } #ifdef CONFIG_OF -static const struct of_device_id of_st21nfcb_i2c_match[] = { +static const struct of_device_id of_st_nci_i2c_match[] = { { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, + { .compatible = "st,st21nfcc-i2c", }, {} }; -MODULE_DEVICE_TABLE(of, of_st21nfcb_i2c_match); +MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match); #endif -static struct i2c_driver st21nfcb_nci_i2c_driver = { +static struct i2c_driver st_nci_i2c_driver = { .driver = { .owner = THIS_MODULE, - .name = ST21NFCB_NCI_I2C_DRIVER_NAME, - .of_match_table = of_match_ptr(of_st21nfcb_i2c_match), + .name = ST_NCI_I2C_DRIVER_NAME, + .of_match_table = of_match_ptr(of_st_nci_i2c_match), }, - .probe = st21nfcb_nci_i2c_probe, - .id_table = st21nfcb_nci_i2c_id_table, - .remove = st21nfcb_nci_i2c_remove, + .probe = st_nci_i2c_probe, + .id_table = st_nci_i2c_id_table, + .remove = st_nci_i2c_remove, }; -module_i2c_driver(st21nfcb_nci_i2c_driver); +module_i2c_driver(st_nci_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st-nci/ndlc.c similarity index 94% rename from drivers/nfc/st21nfcb/ndlc.c rename to drivers/nfc/st-nci/ndlc.c index 6014b5859465..56c6a4cb4c96 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st-nci/ndlc.c @@ -1,7 +1,7 @@ /* * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,7 +20,7 @@ #include #include "ndlc.h" -#include "st21nfcb.h" +#include "st-nci.h" #define NDLC_TIMER_T1 100 #define NDLC_TIMER_T1_WAIT 400 @@ -59,13 +59,25 @@ int ndlc_open(struct llt_ndlc *ndlc) { /* toggle reset pin */ ndlc->ops->enable(ndlc->phy_id); + ndlc->powered = 1; return 0; } EXPORT_SYMBOL(ndlc_open); void ndlc_close(struct llt_ndlc *ndlc) { + struct nci_mode_set_cmd cmd; + + cmd.cmd_type = ST_NCI_SET_NFC_MODE; + cmd.mode = 0; + /* toggle reset pin */ + ndlc->ops->enable(ndlc->phy_id); + + nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP, + sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); + + ndlc->powered = 0; ndlc->ops->disable(ndlc->phy_id); } EXPORT_SYMBOL(ndlc_close); @@ -262,6 +274,7 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, ndlc->ops = phy_ops; ndlc->phy_id = phy_id; ndlc->dev = dev; + ndlc->powered = 0; *ndlc_id = ndlc; @@ -280,12 +293,14 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); - return st21nfcb_nci_probe(ndlc, phy_headroom, phy_tailroom); + return st_nci_probe(ndlc, phy_headroom, phy_tailroom); } EXPORT_SYMBOL(ndlc_probe); void ndlc_remove(struct llt_ndlc *ndlc) { + st_nci_remove(ndlc->ndev); + /* cancel timers */ del_timer_sync(&ndlc->t1_timer); del_timer_sync(&ndlc->t2_timer); @@ -294,7 +309,5 @@ void ndlc_remove(struct llt_ndlc *ndlc) skb_queue_purge(&ndlc->rcv_q); skb_queue_purge(&ndlc->send_q); - - st21nfcb_nci_remove(ndlc->ndev); } EXPORT_SYMBOL(ndlc_remove); diff --git a/drivers/nfc/st21nfcb/ndlc.h b/drivers/nfc/st-nci/ndlc.h similarity index 92% rename from drivers/nfc/st21nfcb/ndlc.h rename to drivers/nfc/st-nci/ndlc.h index b28140e0cd78..6361005ef003 100644 --- a/drivers/nfc/st21nfcb/ndlc.h +++ b/drivers/nfc/st-nci/ndlc.h @@ -1,7 +1,7 @@ /* * NCI based Driver for STMicroelectronics NFC Chip * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -43,10 +43,11 @@ struct llt_ndlc { struct device *dev; /* - * < 0 if hardware error occured + * < 0 if hardware error occurred * and prevents normal operation. */ int hard_fault; + int powered; }; int ndlc_open(struct llt_ndlc *ndlc); diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st-nci/st-nci.h similarity index 63% rename from drivers/nfc/st21nfcb/st21nfcb.h rename to drivers/nfc/st-nci/st-nci.h index 5ef8a58c9839..850a2395deb7 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.h +++ b/drivers/nfc/st-nci/st-nci.h @@ -16,23 +16,35 @@ * along with this program; if not, see . */ -#ifndef __LOCAL_ST21NFCB_H_ -#define __LOCAL_ST21NFCB_H_ +#ifndef __LOCAL_ST_NCI_H_ +#define __LOCAL_ST_NCI_H_ -#include "st21nfcb_se.h" +#include "st-nci_se.h" #include "ndlc.h" /* Define private flags: */ -#define ST21NFCB_NCI_RUNNING 1 +#define ST_NCI_RUNNING 1 -struct st21nfcb_nci_info { +#define ST_NCI_CORE_PROP 0x01 +#define ST_NCI_SET_NFC_MODE 0x02 + +struct nci_mode_set_cmd { + u8 cmd_type; + u8 mode; +} __packed; + +struct nci_mode_set_rsp { + u8 status; +} __packed; + +struct st_nci_info { struct llt_ndlc *ndlc; unsigned long flags; - struct st21nfcb_se_info se_info; + struct st_nci_se_info se_info; }; -void st21nfcb_nci_remove(struct nci_dev *ndev); -int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, +void st_nci_remove(struct nci_dev *ndev); +int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, int phy_tailroom); -#endif /* __LOCAL_ST21NFCB_H_ */ +#endif /* __LOCAL_ST_NCI_H_ */ diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st-nci/st-nci_se.c similarity index 54% rename from drivers/nfc/st21nfcb/st21nfcb_se.c rename to drivers/nfc/st-nci/st-nci_se.c index 24862a525fb5..97addfa96c6f 100644 --- a/drivers/nfc/st21nfcb/st21nfcb_se.c +++ b/drivers/nfc/st-nci/st-nci_se.c @@ -1,7 +1,7 @@ /* - * NCI based Driver for STMicroelectronics NFC Chip + * Secure Element driver for STMicroelectronics NFC NCI chip * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -22,10 +22,10 @@ #include #include -#include "st21nfcb.h" -#include "st21nfcb_se.h" +#include "st-nci.h" +#include "st-nci_se.h" -struct st21nfcb_pipe_info { +struct st_nci_pipe_info { u8 pipe_state; u8 src_host_id; u8 src_gate_id; @@ -34,166 +34,166 @@ struct st21nfcb_pipe_info { } __packed; /* Hosts */ -#define ST21NFCB_HOST_CONTROLLER_ID 0x00 -#define ST21NFCB_TERMINAL_HOST_ID 0x01 -#define ST21NFCB_UICC_HOST_ID 0x02 -#define ST21NFCB_ESE_HOST_ID 0xc0 +#define ST_NCI_HOST_CONTROLLER_ID 0x00 +#define ST_NCI_TERMINAL_HOST_ID 0x01 +#define ST_NCI_UICC_HOST_ID 0x02 +#define ST_NCI_ESE_HOST_ID 0xc0 /* Gates */ -#define ST21NFCB_DEVICE_MGNT_GATE 0x01 -#define ST21NFCB_APDU_READER_GATE 0xf0 -#define ST21NFCB_CONNECTIVITY_GATE 0x41 +#define ST_NCI_DEVICE_MGNT_GATE 0x01 +#define ST_NCI_APDU_READER_GATE 0xf0 +#define ST_NCI_CONNECTIVITY_GATE 0x41 /* Pipes */ -#define ST21NFCB_DEVICE_MGNT_PIPE 0x02 +#define ST_NCI_DEVICE_MGNT_PIPE 0x02 /* Connectivity pipe only */ -#define ST21NFCB_SE_COUNT_PIPE_UICC 0x01 +#define ST_NCI_SE_COUNT_PIPE_UICC 0x01 /* Connectivity + APDU Reader pipe */ -#define ST21NFCB_SE_COUNT_PIPE_EMBEDDED 0x02 +#define ST_NCI_SE_COUNT_PIPE_EMBEDDED 0x02 -#define ST21NFCB_SE_TO_HOT_PLUG 1000 /* msecs */ -#define ST21NFCB_SE_TO_PIPES 2000 +#define ST_NCI_SE_TO_HOT_PLUG 1000 /* msecs */ +#define ST_NCI_SE_TO_PIPES 2000 -#define ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) +#define ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) #define NCI_HCI_APDU_PARAM_ATR 0x01 #define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 #define NCI_HCI_ADMIN_PARAM_WHITELIST 0x03 #define NCI_HCI_ADMIN_PARAM_HOST_LIST 0x04 -#define ST21NFCB_EVT_SE_HARD_RESET 0x20 -#define ST21NFCB_EVT_TRANSMIT_DATA 0x10 -#define ST21NFCB_EVT_WTX_REQUEST 0x11 -#define ST21NFCB_EVT_SE_SOFT_RESET 0x11 -#define ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER 0x21 -#define ST21NFCB_EVT_HOT_PLUG 0x03 +#define ST_NCI_EVT_SE_HARD_RESET 0x20 +#define ST_NCI_EVT_TRANSMIT_DATA 0x10 +#define ST_NCI_EVT_WTX_REQUEST 0x11 +#define ST_NCI_EVT_SE_SOFT_RESET 0x11 +#define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER 0x21 +#define ST_NCI_EVT_HOT_PLUG 0x03 -#define ST21NFCB_SE_MODE_OFF 0x00 -#define ST21NFCB_SE_MODE_ON 0x01 +#define ST_NCI_SE_MODE_OFF 0x00 +#define ST_NCI_SE_MODE_ON 0x01 -#define ST21NFCB_EVT_CONNECTIVITY 0x10 -#define ST21NFCB_EVT_TRANSACTION 0x12 +#define ST_NCI_EVT_CONNECTIVITY 0x10 +#define ST_NCI_EVT_TRANSACTION 0x12 -#define ST21NFCB_DM_GETINFO 0x13 -#define ST21NFCB_DM_GETINFO_PIPE_LIST 0x02 -#define ST21NFCB_DM_GETINFO_PIPE_INFO 0x01 -#define ST21NFCB_DM_PIPE_CREATED 0x02 -#define ST21NFCB_DM_PIPE_OPEN 0x04 -#define ST21NFCB_DM_RF_ACTIVE 0x80 -#define ST21NFCB_DM_DISCONNECT 0x30 +#define ST_NCI_DM_GETINFO 0x13 +#define ST_NCI_DM_GETINFO_PIPE_LIST 0x02 +#define ST_NCI_DM_GETINFO_PIPE_INFO 0x01 +#define ST_NCI_DM_PIPE_CREATED 0x02 +#define ST_NCI_DM_PIPE_OPEN 0x04 +#define ST_NCI_DM_RF_ACTIVE 0x80 +#define ST_NCI_DM_DISCONNECT 0x30 -#define ST21NFCB_DM_IS_PIPE_OPEN(p) \ - ((p & 0x0f) == (ST21NFCB_DM_PIPE_CREATED | ST21NFCB_DM_PIPE_OPEN)) +#define ST_NCI_DM_IS_PIPE_OPEN(p) \ + ((p & 0x0f) == (ST_NCI_DM_PIPE_CREATED | ST_NCI_DM_PIPE_OPEN)) -#define ST21NFCB_ATR_DEFAULT_BWI 0x04 +#define ST_NCI_ATR_DEFAULT_BWI 0x04 /* * WT = 2^BWI/10[s], convert into msecs and add a secure * room by increasing by 2 this timeout */ -#define ST21NFCB_BWI_TO_TIMEOUT(x) ((1 << x) * 200) -#define ST21NFCB_ATR_GET_Y_FROM_TD(x) (x >> 4) +#define ST_NCI_BWI_TO_TIMEOUT(x) ((1 << x) * 200) +#define ST_NCI_ATR_GET_Y_FROM_TD(x) (x >> 4) /* If TA is present bit 0 is set */ -#define ST21NFCB_ATR_TA_PRESENT(x) (x & 0x01) +#define ST_NCI_ATR_TA_PRESENT(x) (x & 0x01) /* If TB is present bit 1 is set */ -#define ST21NFCB_ATR_TB_PRESENT(x) (x & 0x02) +#define ST_NCI_ATR_TB_PRESENT(x) (x & 0x02) -#define ST21NFCB_NUM_DEVICES 256 +#define ST_NCI_NUM_DEVICES 256 -static DECLARE_BITMAP(dev_mask, ST21NFCB_NUM_DEVICES); +static DECLARE_BITMAP(dev_mask, ST_NCI_NUM_DEVICES); -/* Here are the mandatory pipe for st21nfcb */ -static struct nci_hci_gate st21nfcb_gates[] = { +/* Here are the mandatory pipe for st_nci */ +static struct nci_hci_gate st_nci_gates[] = { {NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE, - ST21NFCB_HOST_CONTROLLER_ID}, + ST_NCI_HOST_CONTROLLER_ID}, {NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE, - ST21NFCB_HOST_CONTROLLER_ID}, - {ST21NFCB_DEVICE_MGNT_GATE, ST21NFCB_DEVICE_MGNT_PIPE, - ST21NFCB_HOST_CONTROLLER_ID}, + ST_NCI_HOST_CONTROLLER_ID}, + {ST_NCI_DEVICE_MGNT_GATE, ST_NCI_DEVICE_MGNT_PIPE, + ST_NCI_HOST_CONTROLLER_ID}, /* Secure element pipes are created by secure element host */ - {ST21NFCB_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, - ST21NFCB_HOST_CONTROLLER_ID}, - {ST21NFCB_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, - ST21NFCB_HOST_CONTROLLER_ID}, + {ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST_NCI_HOST_CONTROLLER_ID}, + {ST_NCI_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST_NCI_HOST_CONTROLLER_ID}, }; -static u8 st21nfcb_se_get_bwi(struct nci_dev *ndev) +static u8 st_nci_se_get_bwi(struct nci_dev *ndev) { int i; u8 td; - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */ - for (i = 1; i < ST21NFCB_ESE_MAX_LENGTH; i++) { - td = ST21NFCB_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); - if (ST21NFCB_ATR_TA_PRESENT(td)) + for (i = 1; i < ST_NCI_ESE_MAX_LENGTH; i++) { + td = ST_NCI_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); + if (ST_NCI_ATR_TA_PRESENT(td)) i++; - if (ST21NFCB_ATR_TB_PRESENT(td)) { + if (ST_NCI_ATR_TB_PRESENT(td)) { i++; return info->se_info.atr[i] >> 4; } } - return ST21NFCB_ATR_DEFAULT_BWI; + return ST_NCI_ATR_DEFAULT_BWI; } -static void st21nfcb_se_get_atr(struct nci_dev *ndev) +static void st_nci_se_get_atr(struct nci_dev *ndev) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); int r; struct sk_buff *skb; - r = nci_hci_get_param(ndev, ST21NFCB_APDU_READER_GATE, + r = nci_hci_get_param(ndev, ST_NCI_APDU_READER_GATE, NCI_HCI_APDU_PARAM_ATR, &skb); if (r < 0) return; - if (skb->len <= ST21NFCB_ESE_MAX_LENGTH) { + if (skb->len <= ST_NCI_ESE_MAX_LENGTH) { memcpy(info->se_info.atr, skb->data, skb->len); info->se_info.wt_timeout = - ST21NFCB_BWI_TO_TIMEOUT(st21nfcb_se_get_bwi(ndev)); + ST_NCI_BWI_TO_TIMEOUT(st_nci_se_get_bwi(ndev)); } kfree_skb(skb); } -int st21nfcb_hci_load_session(struct nci_dev *ndev) +int st_nci_hci_load_session(struct nci_dev *ndev) { int i, j, r; struct sk_buff *skb_pipe_list, *skb_pipe_info; - struct st21nfcb_pipe_info *dm_pipe_info; - u8 pipe_list[] = { ST21NFCB_DM_GETINFO_PIPE_LIST, - ST21NFCB_TERMINAL_HOST_ID}; - u8 pipe_info[] = { ST21NFCB_DM_GETINFO_PIPE_INFO, - ST21NFCB_TERMINAL_HOST_ID, 0}; + struct st_nci_pipe_info *dm_pipe_info; + u8 pipe_list[] = { ST_NCI_DM_GETINFO_PIPE_LIST, + ST_NCI_TERMINAL_HOST_ID}; + u8 pipe_info[] = { ST_NCI_DM_GETINFO_PIPE_INFO, + ST_NCI_TERMINAL_HOST_ID, 0}; - /* On ST21NFCB device pipes number are dynamics + /* On ST_NCI device pipes number are dynamics * If pipes are already created, hci_dev_up will fail. * Doing a clear all pipe is a bad idea because: * - It does useless EEPROM cycling * - It might cause issue for secure elements support * (such as removing connectivity or APDU reader pipe) - * A better approach on ST21NFCB is to: + * A better approach on ST_NCI is to: * - get a pipe list for each host. - * (eg: ST21NFCB_HOST_CONTROLLER_ID for now). + * (eg: ST_NCI_HOST_CONTROLLER_ID for now). * (TODO Later on UICC HOST and eSE HOST) * - get pipe information - * - match retrieved pipe list in st21nfcb_gates - * ST21NFCB_DEVICE_MGNT_GATE is a proprietary gate - * with ST21NFCB_DEVICE_MGNT_PIPE. + * - match retrieved pipe list in st_nci_gates + * ST_NCI_DEVICE_MGNT_GATE is a proprietary gate + * with ST_NCI_DEVICE_MGNT_PIPE. * Pipe can be closed and need to be open. */ - r = nci_hci_connect_gate(ndev, ST21NFCB_HOST_CONTROLLER_ID, - ST21NFCB_DEVICE_MGNT_GATE, - ST21NFCB_DEVICE_MGNT_PIPE); + r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID, + ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_DEVICE_MGNT_PIPE); if (r < 0) goto free_info; /* Get pipe list */ - r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, - ST21NFCB_DM_GETINFO, pipe_list, sizeof(pipe_list), + r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_DM_GETINFO, pipe_list, sizeof(pipe_list), &skb_pipe_list); if (r < 0) goto free_info; @@ -201,8 +201,8 @@ int st21nfcb_hci_load_session(struct nci_dev *ndev) /* Complete the existing gate_pipe table */ for (i = 0; i < skb_pipe_list->len; i++) { pipe_info[2] = skb_pipe_list->data[i]; - r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, - ST21NFCB_DM_GETINFO, pipe_info, + r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_DM_GETINFO, pipe_info, sizeof(pipe_info), &skb_pipe_info); if (r) @@ -217,81 +217,81 @@ int st21nfcb_hci_load_session(struct nci_dev *ndev) * - destination hid (1byte) * - destination gid (1byte) */ - dm_pipe_info = (struct st21nfcb_pipe_info *)skb_pipe_info->data; - if (dm_pipe_info->dst_gate_id == ST21NFCB_APDU_READER_GATE && - dm_pipe_info->src_host_id != ST21NFCB_ESE_HOST_ID) { + dm_pipe_info = (struct st_nci_pipe_info *)skb_pipe_info->data; + if (dm_pipe_info->dst_gate_id == ST_NCI_APDU_READER_GATE && + dm_pipe_info->src_host_id != ST_NCI_ESE_HOST_ID) { pr_err("Unexpected apdu_reader pipe on host %x\n", dm_pipe_info->src_host_id); continue; } - for (j = 0; (j < ARRAY_SIZE(st21nfcb_gates)) && - (st21nfcb_gates[j].gate != dm_pipe_info->dst_gate_id); j++) + for (j = 0; (j < ARRAY_SIZE(st_nci_gates)) && + (st_nci_gates[j].gate != dm_pipe_info->dst_gate_id); j++) ; - if (j < ARRAY_SIZE(st21nfcb_gates) && - st21nfcb_gates[j].gate == dm_pipe_info->dst_gate_id && - ST21NFCB_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) { - st21nfcb_gates[j].pipe = pipe_info[2]; + if (j < ARRAY_SIZE(st_nci_gates) && + st_nci_gates[j].gate == dm_pipe_info->dst_gate_id && + ST_NCI_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) { + st_nci_gates[j].pipe = pipe_info[2]; - ndev->hci_dev->gate2pipe[st21nfcb_gates[j].gate] = - st21nfcb_gates[j].pipe; - ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].gate = - st21nfcb_gates[j].gate; - ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].host = + ndev->hci_dev->gate2pipe[st_nci_gates[j].gate] = + st_nci_gates[j].pipe; + ndev->hci_dev->pipes[st_nci_gates[j].pipe].gate = + st_nci_gates[j].gate; + ndev->hci_dev->pipes[st_nci_gates[j].pipe].host = dm_pipe_info->src_host_id; } } - memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, - sizeof(st21nfcb_gates)); + memcpy(ndev->hci_dev->init_data.gates, st_nci_gates, + sizeof(st_nci_gates)); free_info: kfree_skb(skb_pipe_info); kfree_skb(skb_pipe_list); return r; } -EXPORT_SYMBOL_GPL(st21nfcb_hci_load_session); +EXPORT_SYMBOL_GPL(st_nci_hci_load_session); -static void st21nfcb_hci_admin_event_received(struct nci_dev *ndev, +static void st_nci_hci_admin_event_received(struct nci_dev *ndev, u8 event, struct sk_buff *skb) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); switch (event) { - case ST21NFCB_EVT_HOT_PLUG: + case ST_NCI_EVT_HOT_PLUG: if (info->se_info.se_active) { - if (!ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(skb)) { + if (!ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(skb)) { del_timer_sync(&info->se_info.se_active_timer); info->se_info.se_active = false; complete(&info->se_info.req_completion); } else { mod_timer(&info->se_info.se_active_timer, jiffies + - msecs_to_jiffies(ST21NFCB_SE_TO_PIPES)); + msecs_to_jiffies(ST_NCI_SE_TO_PIPES)); } } break; } } -static int st21nfcb_hci_apdu_reader_event_received(struct nci_dev *ndev, +static int st_nci_hci_apdu_reader_event_received(struct nci_dev *ndev, u8 event, struct sk_buff *skb) { int r = 0; - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); pr_debug("apdu reader gate event: %x\n", event); switch (event) { - case ST21NFCB_EVT_TRANSMIT_DATA: + case ST_NCI_EVT_TRANSMIT_DATA: del_timer_sync(&info->se_info.bwi_timer); info->se_info.bwi_active = false; info->se_info.cb(info->se_info.cb_context, skb->data, skb->len, 0); break; - case ST21NFCB_EVT_WTX_REQUEST: + case ST_NCI_EVT_WTX_REQUEST: mod_timer(&info->se_info.bwi_timer, jiffies + msecs_to_jiffies(info->se_info.wt_timeout)); break; @@ -306,7 +306,7 @@ static int st21nfcb_hci_apdu_reader_event_received(struct nci_dev *ndev, * <= 0: driver handled the event, skb consumed * 1: driver does not handle the event, please do standard processing */ -static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, +static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev, u8 host, u8 event, struct sk_buff *skb) { @@ -317,10 +317,10 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, pr_debug("connectivity gate event: %x\n", event); switch (event) { - case ST21NFCB_EVT_CONNECTIVITY: + case ST_NCI_EVT_CONNECTIVITY: break; - case ST21NFCB_EVT_TRANSACTION: + case ST_NCI_EVT_TRANSACTION: /* According to specification etsi 102 622 * 11.2.2.4 EVT_TRANSACTION Table 52 * Description Tag Length @@ -355,7 +355,7 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, return r; } -void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, +void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, u8 event, struct sk_buff *skb) { u8 gate = ndev->hci_dev->pipes[pipe].gate; @@ -363,32 +363,32 @@ void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, switch (gate) { case NCI_HCI_ADMIN_GATE: - st21nfcb_hci_admin_event_received(ndev, event, skb); + st_nci_hci_admin_event_received(ndev, event, skb); break; - case ST21NFCB_APDU_READER_GATE: - st21nfcb_hci_apdu_reader_event_received(ndev, event, skb); + case ST_NCI_APDU_READER_GATE: + st_nci_hci_apdu_reader_event_received(ndev, event, skb); break; - case ST21NFCB_CONNECTIVITY_GATE: - st21nfcb_hci_connectivity_event_received(ndev, host, event, + case ST_NCI_CONNECTIVITY_GATE: + st_nci_hci_connectivity_event_received(ndev, host, event, skb); break; } } -EXPORT_SYMBOL_GPL(st21nfcb_hci_event_received); +EXPORT_SYMBOL_GPL(st_nci_hci_event_received); -void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, +void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); u8 gate = ndev->hci_dev->pipes[pipe].gate; pr_debug("cmd: %x\n", cmd); switch (cmd) { case NCI_HCI_ANY_OPEN_PIPE: - if (gate != ST21NFCB_APDU_READER_GATE && - ndev->hci_dev->pipes[pipe].host != ST21NFCB_UICC_HOST_ID) + if (gate != ST_NCI_APDU_READER_GATE && + ndev->hci_dev->pipes[pipe].host != ST_NCI_UICC_HOST_ID) ndev->hci_dev->count_pipes++; if (ndev->hci_dev->count_pipes == @@ -401,28 +401,28 @@ void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, break; } } -EXPORT_SYMBOL_GPL(st21nfcb_hci_cmd_received); +EXPORT_SYMBOL_GPL(st_nci_hci_cmd_received); /* - * Remarks: On some early st21nfcb firmware, nci_nfcee_mode_set(0) + * Remarks: On some early st_nci firmware, nci_nfcee_mode_set(0) * is rejected */ -static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx, +static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx, u8 state) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); int r; struct sk_buff *sk_host_list; u8 host_id; switch (se_idx) { - case ST21NFCB_UICC_HOST_ID: + case ST_NCI_UICC_HOST_ID: ndev->hci_dev->count_pipes = 0; - ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_UICC; + ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_UICC; break; - case ST21NFCB_ESE_HOST_ID: + case ST_NCI_ESE_HOST_ID: ndev->hci_dev->count_pipes = 0; - ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_EMBEDDED; + ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_EMBEDDED; break; default: return -EINVAL; @@ -438,7 +438,7 @@ static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx, return r; mod_timer(&info->se_info.se_active_timer, jiffies + - msecs_to_jiffies(ST21NFCB_SE_TO_HOT_PLUG)); + msecs_to_jiffies(ST_NCI_SE_TO_HOT_PLUG)); info->se_info.se_active = true; /* Ignore return value and check in any case the host_list */ @@ -458,49 +458,49 @@ static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx, host_id = sk_host_list->data[sk_host_list->len - 1]; kfree_skb(sk_host_list); - if (state == ST21NFCB_SE_MODE_ON && host_id == se_idx) + if (state == ST_NCI_SE_MODE_ON && host_id == se_idx) return se_idx; - else if (state == ST21NFCB_SE_MODE_OFF && host_id != se_idx) + else if (state == ST_NCI_SE_MODE_OFF && host_id != se_idx) return se_idx; return -1; } -int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx) +int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx) { int r; - pr_debug("st21nfcb_nci_disable_se\n"); + pr_debug("st_nci_disable_se\n"); if (se_idx == NFC_SE_EMBEDDED) { - r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, - ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); + r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE, + ST_NCI_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); if (r < 0) return r; } return 0; } -EXPORT_SYMBOL_GPL(st21nfcb_nci_disable_se); +EXPORT_SYMBOL_GPL(st_nci_disable_se); -int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx) +int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx) { int r; - pr_debug("st21nfcb_nci_enable_se\n"); + pr_debug("st_nci_enable_se\n"); - if (se_idx == ST21NFCB_HCI_HOST_ID_ESE) { - r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, - ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + if (se_idx == ST_NCI_HCI_HOST_ID_ESE) { + r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE, + ST_NCI_EVT_SE_SOFT_RESET, NULL, 0); if (r < 0) return r; } return 0; } -EXPORT_SYMBOL_GPL(st21nfcb_nci_enable_se); +EXPORT_SYMBOL_GPL(st_nci_enable_se); -static int st21nfcb_hci_network_init(struct nci_dev *ndev) +static int st_nci_hci_network_init(struct nci_dev *ndev) { struct core_conn_create_dest_spec_params *dest_params; struct dest_spec_params spec_params; @@ -519,7 +519,8 @@ static int st21nfcb_hci_network_init(struct nci_dev *ndev) dest_params->length = sizeof(struct dest_spec_params); spec_params.id = ndev->hci_dev->nfcee_id; spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS; - memcpy(dest_params->value, &spec_params, sizeof(struct dest_spec_params)); + memcpy(dest_params->value, &spec_params, + sizeof(struct dest_spec_params)); r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1, sizeof(struct core_conn_create_dest_spec_params) + sizeof(struct dest_spec_params), @@ -531,15 +532,15 @@ static int st21nfcb_hci_network_init(struct nci_dev *ndev) if (!conn_info) goto free_dest_params; - memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, - sizeof(st21nfcb_gates)); + memcpy(ndev->hci_dev->init_data.gates, st_nci_gates, + sizeof(st_nci_gates)); /* * Session id must include the driver name + i2c bus addr * persistent info to discriminate 2 identical chips */ - dev_num = find_first_zero_bit(dev_mask, ST21NFCB_NUM_DEVICES); - if (dev_num >= ST21NFCB_NUM_DEVICES) { + dev_num = find_first_zero_bit(dev_mask, ST_NCI_NUM_DEVICES); + if (dev_num >= ST_NCI_NUM_DEVICES) { r = -ENODEV; goto free_dest_params; } @@ -564,72 +565,72 @@ exit: return r; } -int st21nfcb_nci_discover_se(struct nci_dev *ndev) +int st_nci_discover_se(struct nci_dev *ndev) { u8 param[2]; int r; int se_count = 0; - pr_debug("st21nfcb_nci_discover_se\n"); + pr_debug("st_nci_discover_se\n"); - r = st21nfcb_hci_network_init(ndev); + r = st_nci_hci_network_init(ndev); if (r != 0) return r; - param[0] = ST21NFCB_UICC_HOST_ID; - param[1] = ST21NFCB_HCI_HOST_ID_ESE; + param[0] = ST_NCI_UICC_HOST_ID; + param[1] = ST_NCI_HCI_HOST_ID_ESE; r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PARAM_WHITELIST, param, sizeof(param)); if (r != NCI_HCI_ANY_OK) return r; - r = st21nfcb_nci_control_se(ndev, ST21NFCB_UICC_HOST_ID, - ST21NFCB_SE_MODE_ON); - if (r == ST21NFCB_UICC_HOST_ID) { - nfc_add_se(ndev->nfc_dev, ST21NFCB_UICC_HOST_ID, NFC_SE_UICC); + r = st_nci_control_se(ndev, ST_NCI_UICC_HOST_ID, + ST_NCI_SE_MODE_ON); + if (r == ST_NCI_UICC_HOST_ID) { + nfc_add_se(ndev->nfc_dev, ST_NCI_UICC_HOST_ID, NFC_SE_UICC); se_count++; } /* Try to enable eSE in order to check availability */ - r = st21nfcb_nci_control_se(ndev, ST21NFCB_HCI_HOST_ID_ESE, - ST21NFCB_SE_MODE_ON); - if (r == ST21NFCB_HCI_HOST_ID_ESE) { - nfc_add_se(ndev->nfc_dev, ST21NFCB_HCI_HOST_ID_ESE, + r = st_nci_control_se(ndev, ST_NCI_HCI_HOST_ID_ESE, + ST_NCI_SE_MODE_ON); + if (r == ST_NCI_HCI_HOST_ID_ESE) { + nfc_add_se(ndev->nfc_dev, ST_NCI_HCI_HOST_ID_ESE, NFC_SE_EMBEDDED); se_count++; - st21nfcb_se_get_atr(ndev); + st_nci_se_get_atr(ndev); } return !se_count; } -EXPORT_SYMBOL_GPL(st21nfcb_nci_discover_se); +EXPORT_SYMBOL_GPL(st_nci_discover_se); -int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, +int st_nci_se_io(struct nci_dev *ndev, u32 se_idx, u8 *apdu, size_t apdu_length, se_io_cb_t cb, void *cb_context) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); pr_debug("\n"); switch (se_idx) { - case ST21NFCB_HCI_HOST_ID_ESE: + case ST_NCI_HCI_HOST_ID_ESE: info->se_info.cb = cb; info->se_info.cb_context = cb_context; mod_timer(&info->se_info.bwi_timer, jiffies + msecs_to_jiffies(info->se_info.wt_timeout)); info->se_info.bwi_active = true; - return nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, - ST21NFCB_EVT_TRANSMIT_DATA, apdu, + return nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE, + ST_NCI_EVT_TRANSMIT_DATA, apdu, apdu_length); default: return -ENODEV; } } -EXPORT_SYMBOL(st21nfcb_nci_se_io); +EXPORT_SYMBOL(st_nci_se_io); -static void st21nfcb_se_wt_timeout(unsigned long data) +static void st_nci_se_wt_timeout(unsigned long data) { /* * No answer from the secure element @@ -642,7 +643,7 @@ static void st21nfcb_se_wt_timeout(unsigned long data) */ /* hardware reset managed through VCC_UICC_OUT power supply */ u8 param = 0x01; - struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + struct st_nci_info *info = (struct st_nci_info *) data; pr_debug("\n"); @@ -650,19 +651,19 @@ static void st21nfcb_se_wt_timeout(unsigned long data) if (!info->se_info.xch_error) { info->se_info.xch_error = true; - nci_hci_send_event(info->ndlc->ndev, ST21NFCB_APDU_READER_GATE, - ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + nci_hci_send_event(info->ndlc->ndev, ST_NCI_APDU_READER_GATE, + ST_NCI_EVT_SE_SOFT_RESET, NULL, 0); } else { info->se_info.xch_error = false; - nci_hci_send_event(info->ndlc->ndev, ST21NFCB_DEVICE_MGNT_GATE, - ST21NFCB_EVT_SE_HARD_RESET, ¶m, 1); + nci_hci_send_event(info->ndlc->ndev, ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_EVT_SE_HARD_RESET, ¶m, 1); } info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME); } -static void st21nfcb_se_activation_timeout(unsigned long data) +static void st_nci_se_activation_timeout(unsigned long data) { - struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + struct st_nci_info *info = (struct st_nci_info *) data; pr_debug("\n"); @@ -671,35 +672,35 @@ static void st21nfcb_se_activation_timeout(unsigned long data) complete(&info->se_info.req_completion); } -int st21nfcb_se_init(struct nci_dev *ndev) +int st_nci_se_init(struct nci_dev *ndev) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); init_completion(&info->se_info.req_completion); /* initialize timers */ init_timer(&info->se_info.bwi_timer); info->se_info.bwi_timer.data = (unsigned long)info; - info->se_info.bwi_timer.function = st21nfcb_se_wt_timeout; + info->se_info.bwi_timer.function = st_nci_se_wt_timeout; info->se_info.bwi_active = false; init_timer(&info->se_info.se_active_timer); info->se_info.se_active_timer.data = (unsigned long)info; info->se_info.se_active_timer.function = - st21nfcb_se_activation_timeout; + st_nci_se_activation_timeout; info->se_info.se_active = false; info->se_info.xch_error = false; info->se_info.wt_timeout = - ST21NFCB_BWI_TO_TIMEOUT(ST21NFCB_ATR_DEFAULT_BWI); + ST_NCI_BWI_TO_TIMEOUT(ST_NCI_ATR_DEFAULT_BWI); return 0; } -EXPORT_SYMBOL(st21nfcb_se_init); +EXPORT_SYMBOL(st_nci_se_init); -void st21nfcb_se_deinit(struct nci_dev *ndev) +void st_nci_se_deinit(struct nci_dev *ndev) { - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + struct st_nci_info *info = nci_get_drvdata(ndev); if (info->se_info.bwi_active) del_timer_sync(&info->se_info.bwi_timer); @@ -709,5 +710,5 @@ void st21nfcb_se_deinit(struct nci_dev *ndev) info->se_info.se_active = false; info->se_info.bwi_active = false; } -EXPORT_SYMBOL(st21nfcb_se_deinit); +EXPORT_SYMBOL(st_nci_se_deinit); diff --git a/drivers/nfc/st-nci/st-nci_se.h b/drivers/nfc/st-nci/st-nci_se.h new file mode 100644 index 000000000000..ea66e879d67f --- /dev/null +++ b/drivers/nfc/st-nci/st-nci_se.h @@ -0,0 +1,61 @@ +/* + * Secure Element Driver for STMicroelectronics NFC NCI Chip + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef __LOCAL_ST_NCI_SE_H_ +#define __LOCAL_ST_NCI_SE_H_ + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST_NCI_ESE_MAX_LENGTH 33 +#define ST_NCI_HCI_HOST_ID_ESE 0xc0 + +struct st_nci_se_info { + u8 atr[ST_NCI_ESE_MAX_LENGTH]; + struct completion req_completion; + + struct timer_list bwi_timer; + int wt_timeout; /* in msecs */ + bool bwi_active; + + struct timer_list se_active_timer; + bool se_active; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; +}; + +int st_nci_se_init(struct nci_dev *ndev); +void st_nci_se_deinit(struct nci_dev *ndev); + +int st_nci_discover_se(struct nci_dev *ndev); +int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx); +int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx); +int st_nci_se_io(struct nci_dev *ndev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); +int st_nci_hci_load_session(struct nci_dev *ndev); +void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb); +void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, + struct sk_buff *skb); + + +#endif /* __LOCAL_ST_NCI_SE_H_ */ diff --git a/drivers/nfc/st21nfcb/Kconfig b/drivers/nfc/st21nfcb/Kconfig deleted file mode 100644 index e0322dd03a70..000000000000 --- a/drivers/nfc/st21nfcb/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -config NFC_ST21NFCB - tristate "STMicroelectronics ST21NFCB NFC driver" - depends on NFC_NCI - default n - ---help--- - STMicroelectronics ST21NFCB core driver. It implements the chipset - NCI logic and hooks into the NFC kernel APIs. Physical layers will - register against it. - - To compile this driver as a module, choose m here. The module will - be called st21nfcb. - Say N if unsure. - -config NFC_ST21NFCB_I2C - tristate "NFC ST21NFCB i2c support" - depends on NFC_ST21NFCB && I2C - ---help--- - This module adds support for the STMicroelectronics st21nfcb i2c interface. - Select this if your platform is using the i2c bus. - - If you choose to build a module, it'll be called st21nfcb_i2c. - Say N if unsure. diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile deleted file mode 100644 index ce659a9e5a1a..000000000000 --- a/drivers/nfc/st21nfcb/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for ST21NFCB NCI based NFC driver -# - -st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_se.o -obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb_nci.o - -st21nfcb_i2c-objs = i2c.o -obj-$(CONFIG_NFC_ST21NFCB_I2C) += st21nfcb_i2c.o diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c deleted file mode 100644 index ca9871ab3fb3..000000000000 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * NCI based Driver for STMicroelectronics NFC Chip - * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include - -#include "st21nfcb.h" -#include "st21nfcb_se.h" - -#define DRIVER_DESC "NCI NFC driver for ST21NFCB" - -#define ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 0x83 - -static int st21nfcb_nci_open(struct nci_dev *ndev) -{ - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); - int r; - - if (test_and_set_bit(ST21NFCB_NCI_RUNNING, &info->flags)) - return 0; - - r = ndlc_open(info->ndlc); - if (r) - clear_bit(ST21NFCB_NCI_RUNNING, &info->flags); - - return r; -} - -static int st21nfcb_nci_close(struct nci_dev *ndev) -{ - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); - - if (!test_and_clear_bit(ST21NFCB_NCI_RUNNING, &info->flags)) - return 0; - - ndlc_close(info->ndlc); - - return 0; -} - -static int st21nfcb_nci_send(struct nci_dev *ndev, struct sk_buff *skb) -{ - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); - - skb->dev = (void *)ndev; - - if (!test_bit(ST21NFCB_NCI_RUNNING, &info->flags)) - return -EBUSY; - - return ndlc_send(info->ndlc, skb); -} - -static __u32 st21nfcb_nci_get_rfprotocol(struct nci_dev *ndev, - __u8 rf_protocol) -{ - return rf_protocol == ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 ? - NFC_PROTO_ISO15693_MASK : 0; -} - -static struct nci_ops st21nfcb_nci_ops = { - .open = st21nfcb_nci_open, - .close = st21nfcb_nci_close, - .send = st21nfcb_nci_send, - .get_rfprotocol = st21nfcb_nci_get_rfprotocol, - .discover_se = st21nfcb_nci_discover_se, - .enable_se = st21nfcb_nci_enable_se, - .disable_se = st21nfcb_nci_disable_se, - .se_io = st21nfcb_nci_se_io, - .hci_load_session = st21nfcb_hci_load_session, - .hci_event_received = st21nfcb_hci_event_received, - .hci_cmd_received = st21nfcb_hci_cmd_received, -}; - -int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, - int phy_tailroom) -{ - struct st21nfcb_nci_info *info; - int r; - u32 protocols; - - info = devm_kzalloc(ndlc->dev, - sizeof(struct st21nfcb_nci_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - protocols = NFC_PROTO_JEWEL_MASK - | NFC_PROTO_MIFARE_MASK - | NFC_PROTO_FELICA_MASK - | NFC_PROTO_ISO14443_MASK - | NFC_PROTO_ISO14443_B_MASK - | NFC_PROTO_ISO15693_MASK - | NFC_PROTO_NFC_DEP_MASK; - - ndlc->ndev = nci_allocate_device(&st21nfcb_nci_ops, protocols, - phy_headroom, phy_tailroom); - if (!ndlc->ndev) { - pr_err("Cannot allocate nfc ndev\n"); - return -ENOMEM; - } - info->ndlc = ndlc; - - nci_set_drvdata(ndlc->ndev, info); - - r = nci_register_device(ndlc->ndev); - if (r) { - pr_err("Cannot register nfc device to nci core\n"); - nci_free_device(ndlc->ndev); - return r; - } - - return st21nfcb_se_init(ndlc->ndev); -} -EXPORT_SYMBOL_GPL(st21nfcb_nci_probe); - -void st21nfcb_nci_remove(struct nci_dev *ndev) -{ - struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); - - nci_unregister_device(ndev); - nci_free_device(ndev); - kfree(info); -} -EXPORT_SYMBOL_GPL(st21nfcb_nci_remove); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.h b/drivers/nfc/st21nfcb/st21nfcb_se.h deleted file mode 100644 index 52a323872bea..000000000000 --- a/drivers/nfc/st21nfcb/st21nfcb_se.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * NCI based Driver for STMicroelectronics NFC Chip - * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#ifndef __LOCAL_ST21NFCB_SE_H_ -#define __LOCAL_ST21NFCB_SE_H_ - -/* - * ref ISO7816-3 chap 8.1. the initial character TS is followed by a - * sequence of at most 32 characters. - */ -#define ST21NFCB_ESE_MAX_LENGTH 33 -#define ST21NFCB_HCI_HOST_ID_ESE 0xc0 - -struct st21nfcb_se_info { - u8 atr[ST21NFCB_ESE_MAX_LENGTH]; - struct completion req_completion; - - struct timer_list bwi_timer; - int wt_timeout; /* in msecs */ - bool bwi_active; - - struct timer_list se_active_timer; - bool se_active; - - bool xch_error; - - se_io_cb_t cb; - void *cb_context; -}; - -int st21nfcb_se_init(struct nci_dev *ndev); -void st21nfcb_se_deinit(struct nci_dev *ndev); - -int st21nfcb_nci_discover_se(struct nci_dev *ndev); -int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx); -int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx); -int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, - u8 *apdu, size_t apdu_length, - se_io_cb_t cb, void *cb_context); -int st21nfcb_hci_load_session(struct nci_dev *ndev); -void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, - u8 event, struct sk_buff *skb); -void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, - struct sk_buff *skb); - - -#endif /* __LOCAL_ST21NFCB_NCI_H_ */ diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index aa6a333b2ead..85b4d86772d8 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -149,6 +149,7 @@ */ #define TRF7970A_QUIRK_IRQ_STATUS_READ BIT(0) #define TRF7970A_QUIRK_EN2_MUST_STAY_LOW BIT(1) +#define TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE BIT(2) /* Direct commands */ #define TRF7970A_CMD_IDLE 0x00 @@ -446,6 +447,7 @@ struct trf7970a { u8 md_rf_tech; u8 tx_cmd; bool issue_eof; + bool adjust_resp_len; int en2_gpio; int en_gpio; struct mutex lock; @@ -626,6 +628,11 @@ static void trf7970a_send_upstream(struct trf7970a *trf) trf->aborting = false; } + if (trf->adjust_resp_len) { + skb_trim(trf->rx_skb, trf->rx_skb->len - 1); + trf->adjust_resp_len = false; + } + trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb); trf->rx_skb = NULL; @@ -1429,10 +1436,15 @@ static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb) trf->iso_ctrl = iso_ctrl; } - if ((trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) && - trf7970a_is_iso15693_write_or_lock(req[1]) && - (req[0] & ISO15693_REQ_FLAG_OPTION)) - trf->issue_eof = true; + if (trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) { + if (trf7970a_is_iso15693_write_or_lock(req[1]) && + (req[0] & ISO15693_REQ_FLAG_OPTION)) + trf->issue_eof = true; + else if ((trf->quirks & + TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE) && + (req[1] == ISO15693_CMD_READ_MULTIPLE_BLOCK)) + trf->adjust_resp_len = true; + } } return 0; @@ -1992,6 +2004,9 @@ static int trf7970a_probe(struct spi_device *spi) return ret; } + if (of_property_read_bool(np, "t5t-rmb-extra-byte-quirk")) + trf->quirks |= TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE; + if (of_property_read_bool(np, "irq-status-read-quirk")) trf->quirks |= TRF7970A_QUIRK_IRQ_STATUS_READ; diff --git a/include/linux/platform_data/nfcmrvl.h b/include/linux/platform_data/nfcmrvl.h new file mode 100644 index 000000000000..ac91707dabcb --- /dev/null +++ b/include/linux/platform_data/nfcmrvl.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _NFCMRVL_PTF_H_ +#define _NFCMRVL_PTF_H_ + +struct nfcmrvl_platform_data { + /* + * Generic + */ + + /* GPIO that is wired to RESET_N signal */ + unsigned int reset_n_io; + /* Tell if transport is muxed in HCI one */ + unsigned int hci_muxed; + + /* + * UART specific + */ + + /* Tell if UART needs flow control at init */ + unsigned int flow_control; + /* Tell if firmware supports break control for power management */ + unsigned int break_control; +}; + +#endif /* _NFCMRVL_PTF_H_ */ diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st-nci.h similarity index 70% rename from include/linux/platform_data/st21nfcb.h rename to include/linux/platform_data/st-nci.h index b023373d9874..d9d400a297bd 100644 --- a/include/linux/platform_data/st21nfcb.h +++ b/include/linux/platform_data/st-nci.h @@ -1,7 +1,7 @@ /* - * Driver include for the ST21NFCB NFC chip. + * Driver include for ST NCI NFC chip family. * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -16,14 +16,14 @@ * along with this program; if not, see . */ -#ifndef _ST21NFCB_NCI_H_ -#define _ST21NFCB_NCI_H_ +#ifndef _ST_NCI_H_ +#define _ST_NCI_H_ -#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci" +#define ST_NCI_DRIVER_NAME "st_nci" -struct st21nfcb_nfc_platform_data { +struct st_nci_nfc_platform_data { unsigned int gpio_reset; unsigned int irq_polarity; }; -#endif /* _ST21NFCB_NCI_H_ */ +#endif /* _ST_NCI_H_ */ diff --git a/include/linux/platform_data/st_nci.h b/include/linux/platform_data/st_nci.h new file mode 100644 index 000000000000..d9d400a297bd --- /dev/null +++ b/include/linux/platform_data/st_nci.h @@ -0,0 +1,29 @@ +/* + * Driver include for ST NCI NFC chip family. + * + * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ST_NCI_H_ +#define _ST_NCI_H_ + +#define ST_NCI_DRIVER_NAME "st_nci" + +struct st_nci_nfc_platform_data { + unsigned int gpio_reset; + unsigned int irq_polarity; +}; + +#endif /* _ST_NCI_H_ */ diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 020a814bc8ed..316694dafa5b 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -179,6 +179,13 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev); void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata); void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev); +static inline int nfc_hci_set_vendor_cmds(struct nfc_hci_dev *hdev, + struct nfc_vendor_cmd *cmds, + int n_cmds) +{ + return nfc_set_vendor_cmds(hdev->ndev, cmds, n_cmds); +} + void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err); int nfc_hci_result_to_errno(u8 result); diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index a2f2f3d3196d..75d2e1880059 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -35,6 +35,7 @@ #define NCI_MAX_NUM_RF_CONFIGS 10 #define NCI_MAX_NUM_CONN 10 #define NCI_MAX_PARAM_LEN 251 +#define NCI_MAX_PACKET_SIZE 258 /* NCI Status Codes */ #define NCI_STATUS_OK 0x00 diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d4dcc7199fd7..01fc8c531115 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -66,7 +67,14 @@ enum nci_state { struct nci_dev; +struct nci_prop_ops { + __u16 opcode; + int (*rsp)(struct nci_dev *dev, struct sk_buff *skb); + int (*ntf)(struct nci_dev *dev, struct sk_buff *skb); +}; + struct nci_ops { + int (*init)(struct nci_dev *ndev); int (*open)(struct nci_dev *ndev); int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); @@ -84,12 +92,16 @@ struct nci_ops { struct sk_buff *skb); void (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb); + + struct nci_prop_ops *prop_ops; + size_t n_prop_ops; }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 #define NCI_MAX_DISCOVERED_TARGETS 10 #define NCI_MAX_NUM_NFCEE 255 #define NCI_MAX_CONN_ID 7 +#define NCI_MAX_PROPRIETARY_CMD 64 struct nci_conn_info { struct list_head list; @@ -264,6 +276,8 @@ int nci_request(struct nci_dev *ndev, void (*req)(struct nci_dev *ndev, unsigned long opt), unsigned long opt, __u32 timeout); +int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload); + int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val); @@ -318,8 +332,19 @@ static inline void *nci_get_drvdata(struct nci_dev *ndev) return ndev->driver_data; } +static inline int nci_set_vendor_cmds(struct nci_dev *ndev, + struct nfc_vendor_cmd *cmds, + int n_cmds) +{ + return nfc_set_vendor_cmds(ndev->nfc_dev, cmds, n_cmds); +} + void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb); void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb); +int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb); +int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode, + struct sk_buff *skb); void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb); @@ -367,4 +392,50 @@ int nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb); struct sk_buff *nci_spi_read(struct nci_spi *nspi); +/* ----- NCI UART ---- */ + +/* Ioctl */ +#define NCIUARTSETDRIVER _IOW('U', 0, char *) + +enum nci_uart_driver { + NCI_UART_DRIVER_MARVELL = 0, + NCI_UART_DRIVER_MAX +}; + +struct nci_uart; + +struct nci_uart_ops { + int (*open)(struct nci_uart *nci_uart); + void (*close)(struct nci_uart *nci_uart); + int (*recv)(struct nci_uart *nci_uart, struct sk_buff *skb); + int (*recv_buf)(struct nci_uart *nci_uart, const u8 *data, char *flags, + int count); + int (*send)(struct nci_uart *nci_uart, struct sk_buff *skb); + void (*tx_start)(struct nci_uart *nci_uart); + void (*tx_done)(struct nci_uart *nci_uart); +}; + +struct nci_uart { + struct module *owner; + struct nci_uart_ops ops; + const char *name; + enum nci_uart_driver driver; + + /* Dynamic data */ + struct nci_dev *ndev; + spinlock_t rx_lock; + struct work_struct write_work; + struct tty_struct *tty; + unsigned long tx_state; + struct sk_buff_head tx_q; + struct sk_buff *tx_skb; + struct sk_buff *rx_skb; + int rx_packet_len; + void *drv_data; +}; + +int nci_uart_register(struct nci_uart *nu); +void nci_uart_unregister(struct nci_uart *nu); +void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl); + #endif /* __NCI_CORE_H */ diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 7ac029c07546..f9e58ae45f9c 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -165,6 +165,12 @@ struct nfc_genl_data { struct mutex genl_data_mutex; }; +struct nfc_vendor_cmd { + __u32 vendor_id; + __u32 subcmd; + int (*doit)(struct nfc_dev *dev, void *data, size_t data_len); +}; + struct nfc_dev { int idx; u32 target_next_idx; @@ -193,6 +199,9 @@ struct nfc_dev { struct rfkill *rfkill; + struct nfc_vendor_cmd *vendor_cmds; + int n_vendor_cmds; + struct nfc_ops *ops; }; #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) @@ -296,4 +305,17 @@ struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx); void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, u8 payload_type, u8 direction); +static inline int nfc_set_vendor_cmds(struct nfc_dev *dev, + struct nfc_vendor_cmd *cmds, + int n_cmds) +{ + if (dev->vendor_cmds || dev->n_vendor_cmds) + return -EINVAL; + + dev->vendor_cmds = cmds; + dev->n_vendor_cmds = n_cmds; + + return 0; +} + #endif /* __NET_NFC_H */ diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index c1e2e63cf9b5..dd3f75389076 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -86,6 +86,8 @@ * for this event is the application ID (AID). * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller. * @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element. + * @NFC_CMD_VENDOR: Vendor specific command, to be implemented directly + * from the driver in order to support hardware specific operations. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -117,6 +119,7 @@ enum nfc_commands { NFC_CMD_GET_SE, NFC_CMD_SE_IO, NFC_CMD_ACTIVATE_TARGET, + NFC_CMD_VENDOR, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -153,6 +156,10 @@ enum nfc_commands { * @NFC_ATTR_APDU: Secure element APDU * @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier * @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier + * @NFC_ATTR_VENDOR_ID: NFC manufacturer unique ID, typically an OUI + * @NFC_ATTR_VENDOR_SUBCMD: Vendor specific sub command + * @NFC_ATTR_VENDOR_DATA: Vendor specific data, to be optionally passed + * to a vendor specific command implementation */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -184,6 +191,9 @@ enum nfc_attrs { NFC_ATTR_TARGET_ISO15693_DSFID, NFC_ATTR_TARGET_ISO15693_UID, NFC_ATTR_SE_PARAMS, + NFC_ATTR_VENDOR_ID, + NFC_ATTR_VENDOR_SUBCMD, + NFC_ATTR_VENDOR_DATA, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/include/uapi/linux/tty.h b/include/uapi/linux/tty.h index dac199a2dba5..01c4410352ff 100644 --- a/include/uapi/linux/tty.h +++ b/include/uapi/linux/tty.h @@ -34,5 +34,6 @@ #define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */ #define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */ #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ +#define N_NCI 25 /* NFC NCI UART */ #endif /* _UAPI_LINUX_TTY_H */ diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig index a4f1e42e3481..901c1ddba841 100644 --- a/net/nfc/nci/Kconfig +++ b/net/nfc/nci/Kconfig @@ -19,3 +19,10 @@ config NFC_NCI_SPI an NFC Controller (NFCC) and a Device Host (DH). Say yes if you use an NCI driver that requires SPI link layer. + +config NFC_NCI_UART + depends on NFC_NCI && TTY + tristate "NCI over UART protocol support" + default n + help + Say yes if you use an NCI driver that requires UART link layer. diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile index 7ed8949266cc..b4b85b82e988 100644 --- a/net/nfc/nci/Makefile +++ b/net/nfc/nci/Makefile @@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_NCI) += nci.o nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o nci-$(CONFIG_NFC_NCI_SPI) += spi.o + +nci_uart-y += uart.o +obj-$(CONFIG_NFC_NCI_UART) += nci_uart.o diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 49ff32106080..95af2d24d5be 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -28,6 +28,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ #include +#include #include #include #include @@ -73,6 +74,7 @@ void nci_req_complete(struct nci_dev *ndev, int result) complete(&ndev->req_completion); } } +EXPORT_SYMBOL(nci_req_complete); static void nci_req_cancel(struct nci_dev *ndev, int err) { @@ -323,6 +325,32 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) sizeof(struct nci_rf_deactivate_cmd), &cmd); } +struct nci_prop_cmd_param { + __u16 opcode; + size_t len; + __u8 *payload; +}; + +static void nci_prop_cmd_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_prop_cmd_param *param = (struct nci_prop_cmd_param *)opt; + + nci_send_cmd(ndev, param->opcode, param->len, param->payload); +} + +int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload) +{ + struct nci_prop_cmd_param param; + + param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid); + param.len = len; + param.payload = payload; + + return __nci_request(ndev, nci_prop_cmd_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_CMD_TIMEOUT)); +} +EXPORT_SYMBOL(nci_prop_cmd); + static int nci_open_device(struct nci_dev *ndev) { int rc = 0; @@ -343,11 +371,17 @@ static int nci_open_device(struct nci_dev *ndev) set_bit(NCI_INIT, &ndev->flags); - rc = __nci_request(ndev, nci_reset_req, 0, - msecs_to_jiffies(NCI_RESET_TIMEOUT)); + if (ndev->ops->init) + rc = ndev->ops->init(ndev); - if (ndev->ops->setup) - ndev->ops->setup(ndev); + if (!rc) { + rc = __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); + } + + if (!rc && ndev->ops->setup) { + rc = ndev->ops->setup(ndev); + } if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, @@ -407,6 +441,12 @@ static int nci_close_device(struct nci_dev *ndev) set_bit(NCI_INIT, &ndev->flags); __nci_request(ndev, nci_reset_req, 0, msecs_to_jiffies(NCI_RESET_TIMEOUT)); + + /* After this point our queues are empty + * and no works are scheduled. + */ + ndev->ops->close(ndev); + clear_bit(NCI_INIT, &ndev->flags); del_timer_sync(&ndev->cmd_timer); @@ -414,10 +454,6 @@ static int nci_close_device(struct nci_dev *ndev) /* Flush cmd wq */ flush_workqueue(ndev->cmd_wq); - /* After this point our queues are empty - * and no works are scheduled. */ - ndev->ops->close(ndev); - /* Clear flags */ ndev->flags = 0; @@ -762,7 +798,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { nci_request(ndev, nci_rf_deactivate_req, - NCI_DEACTIVATE_TYPE_SLEEP_MODE, + NCI_DEACTIVATE_TYPE_IDLE_MODE, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } } @@ -961,6 +997,14 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, return NULL; ndev->ops = ops; + + if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) { + pr_err("Too many proprietary commands: %zd\n", + ops->n_prop_ops); + ops->prop_ops = NULL; + ops->n_prop_ops = 0; + } + ndev->tx_headroom = tx_headroom; ndev->tx_tailroom = tx_tailroom; init_completion(&ndev->req_completion); @@ -1165,6 +1209,49 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) return 0; } +/* Proprietary commands API */ +static struct nci_prop_ops *prop_cmd_lookup(struct nci_dev *ndev, + __u16 opcode) +{ + size_t i; + struct nci_prop_ops *prop_op; + + if (!ndev->ops->prop_ops || !ndev->ops->n_prop_ops) + return NULL; + + for (i = 0; i < ndev->ops->n_prop_ops; i++) { + prop_op = &ndev->ops->prop_ops[i]; + if (prop_op->opcode == opcode) + return prop_op; + } + + return NULL; +} + +int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode, + struct sk_buff *skb) +{ + struct nci_prop_ops *prop_op; + + prop_op = prop_cmd_lookup(ndev, rsp_opcode); + if (!prop_op || !prop_op->rsp) + return -ENOTSUPP; + + return prop_op->rsp(ndev, skb); +} + +int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode, + struct sk_buff *skb) +{ + struct nci_prop_ops *prop_op; + + prop_op = prop_cmd_lookup(ndev, ntf_opcode); + if (!prop_op || !prop_op->ntf) + return -ENOTSUPP; + + return prop_op->ntf(ndev, skb); +} + /* ---- NCI TX Data worker thread ---- */ static void nci_tx_work(struct work_struct *work) diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index ed54ec533836..af002df640c7 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -639,22 +639,19 @@ int nci_hci_dev_session_init(struct nci_dev *ndev) ndev->hci_dev->init_data.gates[0].gate, ndev->hci_dev->init_data.gates[0].pipe); if (r < 0) - goto exit; + return r; r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb); if (r < 0) - goto exit; + return r; if (skb->len && skb->len == strlen(ndev->hci_dev->init_data.session_id) && - memcmp(ndev->hci_dev->init_data.session_id, - skb->data, skb->len) == 0 && + !memcmp(ndev->hci_dev->init_data.session_id, skb->data, skb->len) && ndev->ops->hci_load_session) { /* Restore gate<->pipe table from some proprietary location. */ r = ndev->ops->hci_load_session(ndev); - if (r < 0) - goto exit; } else { r = nci_hci_dev_connect_gates(ndev, ndev->hci_dev->init_data.gate_count, @@ -667,8 +664,6 @@ int nci_hci_dev_session_init(struct nci_dev *ndev) ndev->hci_dev->init_data.session_id, strlen(ndev->hci_dev->init_data.session_id)); } - if (r == 0) - goto exit; exit: kfree_skb(skb); diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 3218071072ac..5d1c2e391c56 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -758,6 +758,15 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) /* strip the nci control header */ skb_pull(skb, NCI_CTRL_HDR_SIZE); + if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) { + if (nci_prop_ntf_packet(ndev, ntf_opcode, skb)) { + pr_err("unsupported ntf opcode 0x%x\n", + ntf_opcode); + } + + goto end; + } + switch (ntf_opcode) { case NCI_OP_CORE_CONN_CREDITS_NTF: nci_core_conn_credits_ntf_packet(ndev, skb); @@ -796,5 +805,6 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) break; } +end: kfree_skb(skb); } diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 02486bc2ceea..408bd8f857ab 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -296,6 +296,15 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) /* strip the nci control header */ skb_pull(skb, NCI_CTRL_HDR_SIZE); + if (nci_opcode_gid(rsp_opcode) == NCI_GID_PROPRIETARY) { + if (nci_prop_rsp_packet(ndev, rsp_opcode, skb) == -ENOTSUPP) { + pr_err("unsupported rsp opcode 0x%x\n", + rsp_opcode); + } + + goto end; + } + switch (rsp_opcode) { case NCI_OP_CORE_RESET_RSP: nci_core_reset_rsp_packet(ndev, skb); @@ -346,6 +355,7 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) break; } +end: kfree_skb(skb); /* trigger the next cmd */ diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c new file mode 100644 index 000000000000..2c48810af5bb --- /dev/null +++ b/net/nfc/nci/uart.c @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2015, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/* Inspired (hugely) by HCI LDISC implementation in Bluetooth. + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2004-2005 Marcel Holtmann + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* TX states */ +#define NCI_UART_SENDING 1 +#define NCI_UART_TX_WAKEUP 2 + +static struct nci_uart *nci_uart_drivers[NCI_UART_DRIVER_MAX]; + +static inline struct sk_buff *nci_uart_dequeue(struct nci_uart *nu) +{ + struct sk_buff *skb = nu->tx_skb; + + if (!skb) + skb = skb_dequeue(&nu->tx_q); + else + nu->tx_skb = NULL; + + return skb; +} + +static inline int nci_uart_queue_empty(struct nci_uart *nu) +{ + if (nu->tx_skb) + return 0; + + return skb_queue_empty(&nu->tx_q); +} + +static int nci_uart_tx_wakeup(struct nci_uart *nu) +{ + if (test_and_set_bit(NCI_UART_SENDING, &nu->tx_state)) { + set_bit(NCI_UART_TX_WAKEUP, &nu->tx_state); + return 0; + } + + schedule_work(&nu->write_work); + + return 0; +} + +static void nci_uart_write_work(struct work_struct *work) +{ + struct nci_uart *nu = container_of(work, struct nci_uart, write_work); + struct tty_struct *tty = nu->tty; + struct sk_buff *skb; + +restart: + clear_bit(NCI_UART_TX_WAKEUP, &nu->tx_state); + + if (nu->ops.tx_start) + nu->ops.tx_start(nu); + + while ((skb = nci_uart_dequeue(nu))) { + int len; + + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + len = tty->ops->write(tty, skb->data, skb->len); + skb_pull(skb, len); + if (skb->len) { + nu->tx_skb = skb; + break; + } + kfree_skb(skb); + } + + if (test_bit(NCI_UART_TX_WAKEUP, &nu->tx_state)) + goto restart; + + if (nu->ops.tx_done && nci_uart_queue_empty(nu)) + nu->ops.tx_done(nu); + + clear_bit(NCI_UART_SENDING, &nu->tx_state); +} + +static int nci_uart_set_driver(struct tty_struct *tty, unsigned int driver) +{ + struct nci_uart *nu = NULL; + int ret; + + if (driver >= NCI_UART_DRIVER_MAX) + return -EINVAL; + + if (!nci_uart_drivers[driver]) + return -ENOENT; + + nu = kzalloc(sizeof(*nu), GFP_KERNEL); + if (!nu) + return -ENOMEM; + + memcpy(nu, nci_uart_drivers[driver], sizeof(struct nci_uart)); + nu->tty = tty; + tty->disc_data = nu; + skb_queue_head_init(&nu->tx_q); + INIT_WORK(&nu->write_work, nci_uart_write_work); + spin_lock_init(&nu->rx_lock); + + ret = nu->ops.open(nu); + if (ret) { + tty->disc_data = NULL; + kfree(nu); + } else if (!try_module_get(nu->owner)) { + nu->ops.close(nu); + tty->disc_data = NULL; + kfree(nu); + return -ENOENT; + } + return ret; +} + +/* ------ LDISC part ------ */ + +/* nci_uart_tty_open + * + * Called when line discipline changed to NCI_UART. + * + * Arguments: + * tty pointer to tty info structure + * Return Value: + * 0 if success, otherwise error code + */ +static int nci_uart_tty_open(struct tty_struct *tty) +{ + /* Error if the tty has no write op instead of leaving an exploitable + * hole + */ + if (!tty->ops->write) + return -EOPNOTSUPP; + + tty->disc_data = NULL; + tty->receive_room = 65536; + + /* Flush any pending characters in the driver and line discipline. */ + + /* FIXME: why is this needed. Note don't use ldisc_ref here as the + * open path is before the ldisc is referencable. + */ + + if (tty->ldisc->ops->flush_buffer) + tty->ldisc->ops->flush_buffer(tty); + tty_driver_flush_buffer(tty); + + return 0; +} + +/* nci_uart_tty_close() + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. + */ +static void nci_uart_tty_close(struct tty_struct *tty) +{ + struct nci_uart *nu = (void *)tty->disc_data; + + /* Detach from the tty */ + tty->disc_data = NULL; + + if (!nu) + return; + + if (nu->tx_skb) + kfree_skb(nu->tx_skb); + if (nu->rx_skb) + kfree_skb(nu->rx_skb); + + skb_queue_purge(&nu->tx_q); + + nu->ops.close(nu); + nu->tty = NULL; + module_put(nu->owner); + + cancel_work_sync(&nu->write_work); + + kfree(nu); +} + +/* nci_uart_tty_wakeup() + * + * Callback for transmit wakeup. Called when low level + * device driver can accept more send data. + * + * Arguments: tty pointer to associated tty instance data + * Return Value: None + */ +static void nci_uart_tty_wakeup(struct tty_struct *tty) +{ + struct nci_uart *nu = (void *)tty->disc_data; + + if (!nu) + return; + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + + if (tty != nu->tty) + return; + + nci_uart_tx_wakeup(nu); +} + +/* nci_uart_tty_receive() + * + * Called by tty low level driver when receive data is + * available. + * + * Arguments: tty pointer to tty isntance data + * data pointer to received data + * flags pointer to flags for data + * count count of received data in bytes + * + * Return Value: None + */ +static void nci_uart_tty_receive(struct tty_struct *tty, const u8 *data, + char *flags, int count) +{ + struct nci_uart *nu = (void *)tty->disc_data; + + if (!nu || tty != nu->tty) + return; + + spin_lock(&nu->rx_lock); + nu->ops.recv_buf(nu, (void *)data, flags, count); + spin_unlock(&nu->rx_lock); + + tty_unthrottle(tty); +} + +/* nci_uart_tty_ioctl() + * + * Process IOCTL system call for the tty device. + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to open file object for device + * cmd IOCTL command code + * arg argument for IOCTL call (cmd dependent) + * + * Return Value: Command dependent + */ +static int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct nci_uart *nu = (void *)tty->disc_data; + int err = 0; + + switch (cmd) { + case NCIUARTSETDRIVER: + if (!nu) + return nci_uart_set_driver(tty, (unsigned int)arg); + else + return -EBUSY; + break; + default: + err = n_tty_ioctl_helper(tty, file, cmd, arg); + break; + } + + return err; +} + +/* We don't provide read/write/poll interface for user space. */ +static ssize_t nci_uart_tty_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + return 0; +} + +static ssize_t nci_uart_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *data, size_t count) +{ + return 0; +} + +static unsigned int nci_uart_tty_poll(struct tty_struct *tty, + struct file *filp, poll_table *wait) +{ + return 0; +} + +static int nci_uart_send(struct nci_uart *nu, struct sk_buff *skb) +{ + /* Queue TX packet */ + skb_queue_tail(&nu->tx_q, skb); + + /* Try to start TX (if possible) */ + nci_uart_tx_wakeup(nu); + + return 0; +} + +/* -- Default recv_buf handler -- + * + * This handler supposes that NCI frames are sent over UART link without any + * framing. It reads NCI header, retrieve the packet size and once all packet + * bytes are received it passes it to nci_uart driver for processing. + */ +static int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data, + char *flags, int count) +{ + int chunk_len; + + if (!nu->ndev) { + nfc_err(nu->tty->dev, + "receive data from tty but no NCI dev is attached yet, drop buffer\n"); + return 0; + } + + /* Decode all incoming data in packets + * and enqueue then for processing. + */ + while (count > 0) { + /* If this is the first data of a packet, allocate a buffer */ + if (!nu->rx_skb) { + nu->rx_packet_len = -1; + nu->rx_skb = nci_skb_alloc(nu->ndev, + NCI_MAX_PACKET_SIZE, + GFP_KERNEL); + if (!nu->rx_skb) + return -ENOMEM; + } + + /* Eat byte after byte till full packet header is received */ + if (nu->rx_skb->len < NCI_CTRL_HDR_SIZE) { + *skb_put(nu->rx_skb, 1) = *data++; + --count; + continue; + } + + /* Header was received but packet len was not read */ + if (nu->rx_packet_len < 0) + nu->rx_packet_len = NCI_CTRL_HDR_SIZE + + nci_plen(nu->rx_skb->data); + + /* Compute how many bytes are missing and how many bytes can + * be consumed. + */ + chunk_len = nu->rx_packet_len - nu->rx_skb->len; + if (count < chunk_len) + chunk_len = count; + memcpy(skb_put(nu->rx_skb, chunk_len), data, chunk_len); + data += chunk_len; + count -= chunk_len; + + /* Chcek if packet is fully received */ + if (nu->rx_packet_len == nu->rx_skb->len) { + /* Pass RX packet to driver */ + if (nu->ops.recv(nu, nu->rx_skb) != 0) + nfc_err(nu->tty->dev, "corrupted RX packet\n"); + /* Next packet will be a new one */ + nu->rx_skb = NULL; + } + } + + return 0; +} + +/* -- Default recv handler -- */ +static int nci_uart_default_recv(struct nci_uart *nu, struct sk_buff *skb) +{ + return nci_recv_frame(nu->ndev, skb); +} + +int nci_uart_register(struct nci_uart *nu) +{ + if (!nu || !nu->ops.open || + !nu->ops.recv || !nu->ops.close) + return -EINVAL; + + /* Set the send callback */ + nu->ops.send = nci_uart_send; + + /* Install default handlers if not overridden */ + if (!nu->ops.recv_buf) + nu->ops.recv_buf = nci_uart_default_recv_buf; + if (!nu->ops.recv) + nu->ops.recv = nci_uart_default_recv; + + /* Add this driver in the driver list */ + if (!nci_uart_drivers[nu->driver]) { + pr_err("driver %d is already registered\n", nu->driver); + return -EBUSY; + } + nci_uart_drivers[nu->driver] = nu; + + pr_info("NCI uart driver '%s [%d]' registered\n", nu->name, nu->driver); + + return 0; +} +EXPORT_SYMBOL_GPL(nci_uart_register); + +void nci_uart_unregister(struct nci_uart *nu) +{ + pr_info("NCI uart driver '%s [%d]' unregistered\n", nu->name, + nu->driver); + + /* Remove this driver from the driver list */ + nci_uart_drivers[nu->driver] = NULL; +} +EXPORT_SYMBOL_GPL(nci_uart_unregister); + +void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl) +{ + struct ktermios new_termios; + + if (!nu->tty) + return; + + down_read(&nu->tty->termios_rwsem); + new_termios = nu->tty->termios; + up_read(&nu->tty->termios_rwsem); + tty_termios_encode_baud_rate(&new_termios, baudrate, baudrate); + + if (flow_ctrl) + new_termios.c_cflag |= CRTSCTS; + else + new_termios.c_cflag &= ~CRTSCTS; + + tty_set_termios(nu->tty, &new_termios); +} +EXPORT_SYMBOL_GPL(nci_uart_set_config); + +static struct tty_ldisc_ops nci_uart_ldisc = { + .magic = TTY_LDISC_MAGIC, + .owner = THIS_MODULE, + .name = "n_nci", + .open = nci_uart_tty_open, + .close = nci_uart_tty_close, + .read = nci_uart_tty_read, + .write = nci_uart_tty_write, + .poll = nci_uart_tty_poll, + .receive_buf = nci_uart_tty_receive, + .write_wakeup = nci_uart_tty_wakeup, + .ioctl = nci_uart_tty_ioctl, +}; + +static int __init nci_uart_init(void) +{ + memset(nci_uart_drivers, 0, sizeof(nci_uart_drivers)); + return tty_register_ldisc(N_NCI, &nci_uart_ldisc); +} + +static void __exit nci_uart_exit(void) +{ + tty_unregister_ldisc(N_NCI); +} + +module_init(nci_uart_init); +module_exit(nci_uart_exit); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("NFC NCI UART driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_NCI); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 3763036710ae..f85f37ed19b2 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -5,6 +5,12 @@ * Lauro Ramos Venancio * Aloisio Almeida Jr * + * Vendor commands implementation based on net/wireless/nl80211.c + * which is: + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2013-2014 Intel Mobile Communications GmbH + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -1489,6 +1495,50 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info) return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); } +static int nfc_genl_vendor_cmd(struct sk_buff *skb, + struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_vendor_cmd *cmd; + u32 dev_idx, vid, subcmd; + u8 *data; + size_t data_len; + int i; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + !info->attrs[NFC_ATTR_VENDOR_ID] || + !info->attrs[NFC_ATTR_VENDOR_SUBCMD]) + return -EINVAL; + + dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + vid = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]); + + dev = nfc_get_device(dev_idx); + if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds) + return -ENODEV; + + data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); + if (data) { + data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]); + if (data_len == 0) + return -EINVAL; + } else { + data_len = 0; + } + + for (i = 0; i < dev->n_vendor_cmds; i++) { + cmd = &dev->vendor_cmds[i]; + + if (cmd->vendor_id != vid || cmd->subcmd != subcmd) + continue; + + return cmd->doit(dev, data, data_len); + } + + return -EOPNOTSUPP; +} + static const struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -1579,6 +1629,11 @@ static const struct genl_ops nfc_genl_ops[] = { .doit = nfc_genl_activate_target, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_VENDOR, + .doit = nfc_genl_vendor_cmd, + .policy = nfc_genl_policy, + }, };