From 551e306905ef2075b3ad03390ef3e19bd1739598 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakivskyy Date: Mon, 25 May 2015 13:55:13 +0300 Subject: [PATCH 01/39] NFC: nxp-nci_i2c: Add support for enumerating through ACPI Signed-off-by: Oleg Zhurakivskyy Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 17bd67dbebf0..2c58bd32c991 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,6 +25,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -48,6 +51,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 +312,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 +378,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 +399,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 +438,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 +463,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 "); From 262e7198bc0e77614f5e9301e0804462741a29fc Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 28 May 2015 17:10:03 +0200 Subject: [PATCH 02/39] NFC: nxp-nci: Fix build warning When GPIO is not enabled we hit this kind of warning: drivers/nfc/nxp-nci/i2c.c: In function 'nxp_nci_i2c_acpi_config': drivers/nfc/nxp-nci/i2c.c:320:2: error: implicit declaration of function 'devm_gpiod_get_index' [-Werror=implicit-function-declaration] gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2); This is fixed by explicitely including gpio/consumer.h. Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 2c58bd32c991..2f77f1d03638 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include From d96097e03f5155115542d42580b1439cafdb6088 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 5 Jun 2015 21:38:19 +0900 Subject: [PATCH 03/39] NFC: Fix typo in nfc-hci.txt This patch fix a spelling typo in nfc-hci.txt Signed-off-by: Masanari Iida Signed-off-by: Samuel Ortiz --- Documentation/nfc/nfc-hci.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); From ab714817d7e891608d31f6996b1e4c43cf2bf342 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 24 Apr 2015 10:37:22 -0700 Subject: [PATCH 04/39] NFC: trf7970a: Handle extra byte in response to Type 5 RMB commands The current versions of the trf7970a has an erratum where it returns an extra byte in the response to 'Read Multiple Block' (RMB) commands. This command is issued to Type 5 tags (i.e., ISO/IEC 15693 tags) by the neard daemon. To handle this, define a new Device Tree property, 't5t-rmb-extra-byte-quirk', which indicates that the associated trf7970a device has this erratum. The trf7970a device driver will then ensure that the response length to RMB commands is reduced by one byte (for devices with the erratum). Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/net/nfc/trf7970a.txt | 4 ++++ drivers/nfc/trf7970a.c | 23 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) 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/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; From 71fa6fba4abfe26299c7ca8179aa7b744f047a7d Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 28 Apr 2015 11:08:47 +0200 Subject: [PATCH 05/39] NFC: Remove obsolete setting of DEBUG CONFIG_DYNAMIC_DEBUG is the right toggle to enable pr_debug(). Signed-off-by: Valentin Rothberg Signed-off-by: Samuel Ortiz --- drivers/nfc/Makefile | 2 -- drivers/nfc/nxp-nci/Makefile | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index a4292d790f9b..13b648baf175 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -14,5 +14,3 @@ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ - -ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG 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 From 0cd6f66739ac8a936d4660d45ecb99eb801709a4 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 May 2015 16:38:30 +0300 Subject: [PATCH 06/39] NFC: microread: drop unused variable In microread_i2c_irq_thread_fn 'client' set but not used Cc: Lauro Ramos Venancio Cc: Aloisio Almeida Jr Signed-off-by: Tomas Winkler Signed-off-by: Samuel Ortiz --- drivers/nfc/microread/i2c.c | 3 --- 1 file changed, 3 deletions(-) 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; From 8a2151c587715e84af676ee6c210fd41b912de76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 19 May 2015 09:22:56 +0200 Subject: [PATCH 07/39] NFC: pn544: use flags argument of devm_gpiod_get to set direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 39b2bbe3d715 (gpio: add flags argument to gpiod_get*() functions) which appeared in v3.17-rc1, the gpiod_get* functions take an additional parameter that allows to specify direction and initial value for output. Use this to simplify the driver. Furthermore this is one caller less that stops us making the flags argument to gpiod_get*() mandatory. While touching this also do some minor coding style fixes. Signed-off-by: Uwe Kleine-König Reviewed-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 43 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 32 deletions(-) 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) { From 2622e2a03cd7320f57a4f5171c242dccdab035dd Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 31 May 2015 17:44:45 -0700 Subject: [PATCH 08/39] NFC: nci: hci: Fix releasing uninitialized skbs Several of these goto exit; uses should be direct returns as skb is not yet initialized by nci_hci_get_param(). Miscellanea: o Use !memcmp instead of memcmp() == 0 o Remove unnecessary goto from if () {... goto exit;} else {...} exit: Signed-off-by: Joe Perches Signed-off-by: Samuel Ortiz --- net/nfc/nci/hci.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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); From b6355e972aaab0173ce11a1650e7dba67f820918 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 6 Jun 2015 13:16:37 +0200 Subject: [PATCH 09/39] NFC: nci: Handle proprietary response and notifications Allow for drivers to explicitly define handlers for each proprietary notifications and responses they expect to support. Reviewed-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 14 ++++++++++ net/nfc/nci/core.c | 52 ++++++++++++++++++++++++++++++++++++++ net/nfc/nci/ntf.c | 10 ++++++++ net/nfc/nci/rsp.c | 10 ++++++++ 4 files changed, 86 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d4dcc7199fd7..c49688c09853 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -66,6 +66,12 @@ 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 (*open)(struct nci_dev *ndev); int (*close)(struct nci_dev *ndev); @@ -84,12 +90,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; @@ -320,6 +330,10 @@ static inline void *nci_get_drvdata(struct nci_dev *ndev) 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); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 49ff32106080..56d57c93ea1a 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 @@ -961,6 +962,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 +1174,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/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 */ From 81859ab8779567af491fbf83ea628cdf09188d90 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:39 +0200 Subject: [PATCH 10/39] NFC: nci: Add NCI_RESET return code check before setup setup was executed in any case, even if NCI_RESET failed. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 56d57c93ea1a..b900e6a2a284 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -347,8 +347,9 @@ static int nci_open_device(struct nci_dev *ndev) rc = __nci_request(ndev, nci_reset_req, 0, msecs_to_jiffies(NCI_RESET_TIMEOUT)); - if (ndev->ops->setup) - ndev->ops->setup(ndev); + if (!rc && ndev->ops->setup) { + rc = ndev->ops->setup(ndev); + } if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, From c39daeee50eb0b95d3b91bda21b77955a459ee5f Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:40 +0200 Subject: [PATCH 11/39] NFC: nci: Add nci init ops for early device initialization Some device may need to execute some proprietary commands in order to "wake-up"; Before the nci state initialization. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index c49688c09853..886854a4ea91 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -73,6 +73,7 @@ struct nci_prop_ops { }; 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); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index b900e6a2a284..458e58bb9cb1 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -344,8 +344,13 @@ 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 (!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); From 759afb8d288ffbe9a1cdb20af037b5c072dc38b2 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:41 +0200 Subject: [PATCH 12/39] NFC: nci: Add nci_prop_cmd allowing to send proprietary nci cmd Handle allowing to send proprietary nci commands anywhere in the nci state machine. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 2 ++ net/nfc/nci/core.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 886854a4ea91..98f18a20dc77 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -275,6 +275,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); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 458e58bb9cb1..b5dc15044466 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -324,6 +324,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; From 38bd83f04c5e9695011dc5c294e0c4e6a9f9052d Mon Sep 17 00:00:00 2001 From: Firo Yang Date: Sat, 6 Jun 2015 13:16:42 +0200 Subject: [PATCH 13/39] NFC: st21nfcb: Remove inappropriate kfree on a devm_kzalloc pointer Since ndev->driver_data is allocated by devm_kzalloc(), we do not need the inappropriate kfree to free it in driver's remove function. Freeing will trigger when driver unloads. Cc: stable@vger.kernel.org Acked-by: Christophe Ricard Signed-off-by: Firo Yang Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/st21nfcb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index ca9871ab3fb3..c7dc282d5c3b 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -131,11 +131,8 @@ 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); From 09f39a950523b1bb830c30a8670b77e0067da092 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:43 +0200 Subject: [PATCH 14/39] NFC: st21nfcb: Do not remove header once the payload is sent Once the data is sent, we need to preserve the full frame for the ndlc state machine. If the NDLC ACK is not received in time, the ndlc layer will resend the same frame. Having the header byte pulled will corrupt the frame. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/i2c.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index 76a4cad41cec..4bc15ec29b03 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -87,11 +87,6 @@ static void st21nfcb_nci_i2c_disable(void *phy_id) gpio_set_value(phy->gpio_reset, 1); } -static void st21nfcb_nci_remove_header(struct sk_buff *skb) -{ - skb_pull(skb, ST21NFCB_FRAME_HEADROOM); -} - /* * Writing a frame must not return the number of written bytes. * It must return either zero for success, or <0 for error. @@ -121,8 +116,6 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) r = 0; } - st21nfcb_nci_remove_header(skb); - return r; } From 4ac82e894825126816d7b7f662743335ce2b015e Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:44 +0200 Subject: [PATCH 15/39] NFC: st21nfcb: remove st21nfcb_nci_i2c_disable ndlc_remove already calls st21nfcb_nci_i2c_disable and phy->powered is already set to 0. Cc: stable@vger.kernel.org Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/i2c.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index 4bc15ec29b03..c44f8cf5391a 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -359,9 +359,6 @@ static int st21nfcb_nci_i2c_remove(struct i2c_client *client) ndlc_remove(phy->ndlc); - if (phy->powered) - st21nfcb_nci_i2c_disable(phy); - return 0; } From 6628b09f03dfbe2d3d234f1c6e5dff8752182be0 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:45 +0200 Subject: [PATCH 16/39] NFC: st21nfcb: Add ndlc_close in st21nfcb_nci_remove In order to release the st21nfcb properly close the ndlc layer first. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/st21nfcb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index c7dc282d5c3b..7a51bf056a69 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -131,6 +131,10 @@ EXPORT_SYMBOL_GPL(st21nfcb_nci_probe); void st21nfcb_nci_remove(struct nci_dev *ndev) { + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + ndlc_close(info->ndlc); + nci_unregister_device(ndev); nci_free_device(ndev); } From cc1417eb24f154ed7a7cd8aa7018b80fa120b732 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:46 +0200 Subject: [PATCH 17/39] NFC: st21nfcb: Fix st21nfcb_nci_close When closing st21nfcb driver, flag ST21NFCB_NCI_RUNNING can be cleared only once the ndlc and the transport (i2c or spi) layers are released. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/st21nfcb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index 7a51bf056a69..12699fa0b1b9 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -47,11 +47,13 @@ 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)) + if (!test_bit(ST21NFCB_NCI_RUNNING, &info->flags)) return 0; ndlc_close(info->ndlc); + clear_bit(ST21NFCB_NCI_RUNNING, &info->flags); + return 0; } From dbd70b4046c8cdb7c8f5ffea9bdcae1a35e24df4 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:47 +0200 Subject: [PATCH 18/39] NFC: st21nfcb: Add support for nci proprietary commands On st21nfcb, nci proprietary commands are available to run specific chip operations (for example: power management) Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/st21nfcb.c | 21 +++++++++++++++++++++ drivers/nfc/st21nfcb/st21nfcb.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index 12699fa0b1b9..50ad6d04f0f4 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "st21nfcb.h" #include "st21nfcb_se.h" @@ -76,6 +78,23 @@ static __u32 st21nfcb_nci_get_rfprotocol(struct nci_dev *ndev, NFC_PROTO_ISO15693_MASK : 0; } +static int st21nfcb_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 st21nfcb_nci_prop_ops[] = { + { + .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, + ST21NFCB_NCI_CORE_PROP), + .rsp = st21nfcb_nci_prop_rsp_packet, + }, +}; + static struct nci_ops st21nfcb_nci_ops = { .open = st21nfcb_nci_open, .close = st21nfcb_nci_close, @@ -88,6 +107,8 @@ static struct nci_ops st21nfcb_nci_ops = { .hci_load_session = st21nfcb_hci_load_session, .hci_event_received = st21nfcb_hci_event_received, .hci_cmd_received = st21nfcb_hci_cmd_received, + .prop_ops = st21nfcb_nci_prop_ops, + .n_prop_ops = ARRAY_SIZE(st21nfcb_nci_prop_ops), }; int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h index 5ef8a58c9839..7c12d9dd03d0 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.h +++ b/drivers/nfc/st21nfcb/st21nfcb.h @@ -25,6 +25,12 @@ /* Define private flags: */ #define ST21NFCB_NCI_RUNNING 1 +#define ST21NFCB_NCI_CORE_PROP 0x01 + +struct nci_mode_set_rsp { + u8 status; +} __packed; + struct st21nfcb_nci_info { struct llt_ndlc *ndlc; unsigned long flags; From 0e70cba71f8b61e0a0c7df526f5cee2d842ee93c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:48 +0200 Subject: [PATCH 19/39] NFC: nci: Move close ops call in nci_close_device When closing the device some data (proprietary commands) might be sent. The core state machine needs to be set for correct command execution. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index b5dc15044466..edc10cc8e10b 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -440,6 +440,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); @@ -447,10 +453,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; From e8b72c205e5e02b6574be1e83c0c8c99335f8288 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:49 +0200 Subject: [PATCH 20/39] NFC: st21nfcb: Move st21nfcb_nci_remove in ndlc_remove Removing st21nfcb may need to execute some specific commands before stopping the ndlc state machine. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/ndlc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index 6014b5859465..429cf055d532 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -286,6 +286,8 @@ EXPORT_SYMBOL(ndlc_probe); void ndlc_remove(struct llt_ndlc *ndlc) { + st21nfcb_nci_remove(ndlc->ndev); + /* cancel timers */ del_timer_sync(&ndlc->t1_timer); del_timer_sync(&ndlc->t2_timer); @@ -294,7 +296,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); From 183fe2d06dba2f45ae47360cdb911f95551917ed Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:50 +0200 Subject: [PATCH 21/39] NFC: st21nfcb: Move powered flag from phy to ndlc layer The powered flag can be set from the ndlc_open and ndlc_close layer. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/i2c.c | 6 +----- drivers/nfc/st21nfcb/ndlc.c | 3 +++ drivers/nfc/st21nfcb/ndlc.h | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index c44f8cf5391a..41b5bdb11cdb 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -52,8 +52,6 @@ struct st21nfcb_i2c_phy { unsigned int gpio_reset; unsigned int irq_polarity; - - int powered; }; #define I2C_DUMP_SKB(info, skb) \ @@ -70,7 +68,6 @@ static int st21nfcb_nci_i2c_enable(void *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); return 0; @@ -80,7 +77,6 @@ static void st21nfcb_nci_i2c_disable(void *phy_id) { struct st21nfcb_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); @@ -203,7 +199,7 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) if (phy->ndlc->hard_fault) return IRQ_HANDLED; - if (!phy->powered) { + if (!phy->ndlc->powered) { st21nfcb_nci_i2c_disable(phy); return IRQ_HANDLED; } diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index 429cf055d532..3ee22b44cd99 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -59,6 +59,7 @@ 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); @@ -67,6 +68,7 @@ void ndlc_close(struct llt_ndlc *ndlc) { /* toggle reset pin */ ndlc->ops->disable(ndlc->phy_id); + ndlc->powered = 0; } EXPORT_SYMBOL(ndlc_close); @@ -262,6 +264,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; diff --git a/drivers/nfc/st21nfcb/ndlc.h b/drivers/nfc/st21nfcb/ndlc.h index b28140e0cd78..cf6a9d9f2983 100644 --- a/drivers/nfc/st21nfcb/ndlc.h +++ b/drivers/nfc/st21nfcb/ndlc.h @@ -47,6 +47,7 @@ struct llt_ndlc { * and prevents normal operation. */ int hard_fault; + int powered; }; int ndlc_open(struct llt_ndlc *ndlc); From 05f0939f76659260763d31890cb962e8d6060038 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:51 +0200 Subject: [PATCH 22/39] NFC: st21nfcb: disable irq when st21nfcb is disabled When st21nfcb is disabled, the irq line may remain active while no data are available, flooding the system with irrelevant i2c transaction. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/i2c.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index 41b5bdb11cdb..dbc0dfd8ae85 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -70,6 +70,9 @@ static int st21nfcb_nci_i2c_enable(void *phy_id) gpio_set_value(phy->gpio_reset, 1); usleep_range(80000, 85000); + if (phy->ndlc->powered == 0) + enable_irq(phy->i2c_dev->irq); + return 0; } @@ -77,10 +80,7 @@ static void st21nfcb_nci_i2c_disable(void *phy_id) { struct st21nfcb_i2c_phy *phy = phy_id; - /* 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); + disable_irq_nosync(phy->i2c_dev->irq); } /* From 41fc2f50a7a2bfef1deaf78107a4d7b5bf4bd7a5 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sat, 6 Jun 2015 13:16:52 +0200 Subject: [PATCH 23/39] NFC: st21nfcb: Configure CLF with NCI proprietary command In order to prevent any NFC feature when NFC is disable and to save power, (down to 4uA) put the CLF in hibernate mode with RF deactivated. Add the equivalent to enable the NFC feature when initiating the st21nfcb driver. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/ndlc.c | 12 +++++++++++- drivers/nfc/st21nfcb/st21nfcb.c | 12 ++++++++++++ drivers/nfc/st21nfcb/st21nfcb.h | 6 ++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index 3ee22b44cd99..91e81f37b3a6 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -66,9 +66,19 @@ EXPORT_SYMBOL(ndlc_open); void ndlc_close(struct llt_ndlc *ndlc) { + struct nci_mode_set_cmd cmd; + + cmd.cmd_type = ST21NFCB_NCI_SET_NFC_MODE; + cmd.mode = 0; + /* toggle reset pin */ - ndlc->ops->disable(ndlc->phy_id); + ndlc->ops->enable(ndlc->phy_id); + + nci_prop_cmd(ndlc->ndev, ST21NFCB_NCI_CORE_PROP, + sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); + ndlc->powered = 0; + ndlc->ops->disable(ndlc->phy_id); } EXPORT_SYMBOL(ndlc_close); diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index 50ad6d04f0f4..a16c3a3d3fff 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -30,6 +30,17 @@ #define ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 0x83 +static int st21nfcb_nci_init(struct nci_dev *ndev) +{ + struct nci_mode_set_cmd cmd; + + cmd.cmd_type = ST21NFCB_NCI_SET_NFC_MODE; + cmd.mode = 1; + + return nci_prop_cmd(ndev, ST21NFCB_NCI_CORE_PROP, + sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); +} + static int st21nfcb_nci_open(struct nci_dev *ndev) { struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); @@ -96,6 +107,7 @@ static struct nci_prop_ops st21nfcb_nci_prop_ops[] = { }; static struct nci_ops st21nfcb_nci_ops = { + .init = st21nfcb_nci_init, .open = st21nfcb_nci_open, .close = st21nfcb_nci_close, .send = st21nfcb_nci_send, diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h index 7c12d9dd03d0..710636325c1f 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.h +++ b/drivers/nfc/st21nfcb/st21nfcb.h @@ -26,6 +26,12 @@ #define ST21NFCB_NCI_RUNNING 1 #define ST21NFCB_NCI_CORE_PROP 0x01 +#define ST21NFCB_NCI_SET_NFC_MODE 0x02 + +struct nci_mode_set_cmd { + u8 cmd_type; + u8 mode; +} __packed; struct nci_mode_set_rsp { u8 status; From 8115dd5905318afcde713726064ec052b7d488cf Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 14 Oct 2014 01:42:23 +0200 Subject: [PATCH 24/39] NFC: Introduce vendor commands structures Together with inline routines to associate a vendor commands array with an NFC device. Vendor commands allow vendors to implement their very specific operations from driver code instead of adding new stack ops for non NFC generic commands. Vendors need to select their own unique IDs and use that as a namespace for defining sub commands. Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 7 +++++++ include/net/nfc/nci_core.h | 7 +++++++ include/net/nfc/nfc.h | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) 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_core.h b/include/net/nfc/nci_core.h index 98f18a20dc77..9d77ed556b78 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -331,6 +331,13 @@ 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, 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 */ From 9e58095f9660f88d6a2febe87d5073a6b2e9c399 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 14 Oct 2014 02:19:46 +0200 Subject: [PATCH 25/39] NFC: netlink: Implement vendor command support Vendor commands are passed from userspace through the NFC_CMD_VENDOR netlink command, allowing driver and hardware specific operations implementations like for example RF tuning or production line calibration. Drivers will associate a set of vendor commands to a vendor id, which could typically be an OUI. The netlink kernel implementation will try to match the received vendor id and sub command attributes with the registered ones. When such match is found, the driver defined sub command routine is called. Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 10 ++++++++ net/nfc/netlink.c | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) 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/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, + }, }; From 2df7f8c69521a4d85dfbc788da260b3c4030980c Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 10 Jun 2015 12:50:22 +0200 Subject: [PATCH 26/39] NFC: nci: Export nci_req_complete Drivers implementing proprietary ops may need it now. Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index edc10cc8e10b..f9aa08780b06 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -74,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) { From ed06aeefdac348cfb91a3db5fe1067e3202afd70 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 9 Jun 2015 22:26:05 +0200 Subject: [PATCH 27/39] nfc: st-nci: Rename st21nfcb to st-nci STMicroelectronics NFC NCI chips family is extending with the new ST21NFCC using the AMS AS39230 RF booster. The st21nfcb driver is relevant for this solution and might be with future products. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- .../net/nfc/{st21nfcb.txt => st-nci.txt} | 4 +- drivers/nfc/Kconfig | 2 +- drivers/nfc/Makefile | 2 +- drivers/nfc/st-nci/Kconfig | 23 ++ drivers/nfc/st-nci/Makefile | 9 + drivers/nfc/st-nci/core.c | 179 ++++++++ drivers/nfc/{st21nfcb => st-nci}/i2c.c | 123 +++--- drivers/nfc/{st21nfcb => st-nci}/ndlc.c | 12 +- drivers/nfc/{st21nfcb => st-nci}/ndlc.h | 4 +- .../{st21nfcb/st21nfcb.h => st-nci/st-nci.h} | 22 +- .../st21nfcb_se.c => st-nci/st-nci_se.c} | 383 +++++++++--------- drivers/nfc/st-nci/st-nci_se.h | 61 +++ drivers/nfc/st21nfcb/Kconfig | 22 - drivers/nfc/st21nfcb/Makefile | 9 - drivers/nfc/st21nfcb/st21nfcb.c | 179 -------- drivers/nfc/st21nfcb/st21nfcb_se.h | 61 --- .../platform_data/{st21nfcb.h => st-nci.h} | 14 +- include/linux/platform_data/st_nci.h | 29 ++ 18 files changed, 585 insertions(+), 553 deletions(-) rename Documentation/devicetree/bindings/net/nfc/{st21nfcb.txt => st-nci.txt} (87%) create mode 100644 drivers/nfc/st-nci/Kconfig create mode 100644 drivers/nfc/st-nci/Makefile create mode 100644 drivers/nfc/st-nci/core.c rename drivers/nfc/{st21nfcb => st-nci}/i2c.c (67%) rename drivers/nfc/{st21nfcb => st-nci}/ndlc.c (96%) rename drivers/nfc/{st21nfcb => st-nci}/ndlc.h (93%) rename drivers/nfc/{st21nfcb/st21nfcb.h => st-nci/st-nci.h} (69%) rename drivers/nfc/{st21nfcb/st21nfcb_se.c => st-nci/st-nci_se.c} (54%) create mode 100644 drivers/nfc/st-nci/st-nci_se.h delete mode 100644 drivers/nfc/st21nfcb/Kconfig delete mode 100644 drivers/nfc/st21nfcb/Makefile delete mode 100644 drivers/nfc/st21nfcb/st21nfcb.c delete mode 100644 drivers/nfc/st21nfcb/st21nfcb_se.h rename include/linux/platform_data/{st21nfcb.h => st-nci.h} (70%) create mode 100644 include/linux/platform_data/st_nci.h 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/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 13b648baf175..368b6dfe71b3 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -12,5 +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/ 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 67% rename from drivers/nfc/st21nfcb/i2c.c rename to drivers/nfc/st-nci/i2c.c index dbc0dfd8ae85..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,18 +35,18 @@ #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; @@ -61,9 +61,9 @@ 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); @@ -76,9 +76,9 @@ static int st21nfcb_nci_i2c_enable(void *phy_id) 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; disable_irq_nosync(phy->i2c_dev->irq); } @@ -88,13 +88,13 @@ static void st21nfcb_nci_i2c_disable(void *phy_id) * 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; @@ -124,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; @@ -169,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); @@ -181,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; @@ -200,11 +200,11 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) return IRQ_HANDLED; if (!phy->ndlc->powered) { - st21nfcb_nci_i2c_disable(phy); + 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; @@ -214,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; @@ -253,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; @@ -285,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__); @@ -300,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; @@ -311,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"); @@ -338,18 +338,18 @@ 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__); @@ -359,26 +359,27 @@ static int st21nfcb_nci_i2c_remove(struct i2c_client *client) } #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 96% rename from drivers/nfc/st21nfcb/ndlc.c rename to drivers/nfc/st-nci/ndlc.c index 91e81f37b3a6..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 @@ -68,13 +68,13 @@ void ndlc_close(struct llt_ndlc *ndlc) { struct nci_mode_set_cmd cmd; - cmd.cmd_type = ST21NFCB_NCI_SET_NFC_MODE; + 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, ST21NFCB_NCI_CORE_PROP, + nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP, sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); ndlc->powered = 0; @@ -293,13 +293,13 @@ 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) { - st21nfcb_nci_remove(ndlc->ndev); + st_nci_remove(ndlc->ndev); /* cancel timers */ del_timer_sync(&ndlc->t1_timer); diff --git a/drivers/nfc/st21nfcb/ndlc.h b/drivers/nfc/st-nci/ndlc.h similarity index 93% rename from drivers/nfc/st21nfcb/ndlc.h rename to drivers/nfc/st-nci/ndlc.h index cf6a9d9f2983..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,7 +43,7 @@ struct llt_ndlc { struct device *dev; /* - * < 0 if hardware error occured + * < 0 if hardware error occurred * and prevents normal operation. */ int hard_fault; diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st-nci/st-nci.h similarity index 69% rename from drivers/nfc/st21nfcb/st21nfcb.h rename to drivers/nfc/st-nci/st-nci.h index 710636325c1f..850a2395deb7 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.h +++ b/drivers/nfc/st-nci/st-nci.h @@ -16,17 +16,17 @@ * 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 -#define ST21NFCB_NCI_CORE_PROP 0x01 -#define ST21NFCB_NCI_SET_NFC_MODE 0x02 +#define ST_NCI_CORE_PROP 0x01 +#define ST_NCI_SET_NFC_MODE 0x02 struct nci_mode_set_cmd { u8 cmd_type; @@ -37,14 +37,14 @@ struct nci_mode_set_rsp { u8 status; } __packed; -struct st21nfcb_nci_info { +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 a16c3a3d3fff..000000000000 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ /dev/null @@ -1,179 +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 -#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_init(struct nci_dev *ndev) -{ - struct nci_mode_set_cmd cmd; - - cmd.cmd_type = ST21NFCB_NCI_SET_NFC_MODE; - cmd.mode = 1; - - return nci_prop_cmd(ndev, ST21NFCB_NCI_CORE_PROP, - sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); -} - -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_bit(ST21NFCB_NCI_RUNNING, &info->flags)) - return 0; - - ndlc_close(info->ndlc); - - clear_bit(ST21NFCB_NCI_RUNNING, &info->flags); - - 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 int st21nfcb_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 st21nfcb_nci_prop_ops[] = { - { - .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, - ST21NFCB_NCI_CORE_PROP), - .rsp = st21nfcb_nci_prop_rsp_packet, - }, -}; - -static struct nci_ops st21nfcb_nci_ops = { - .init = st21nfcb_nci_init, - .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, - .prop_ops = st21nfcb_nci_prop_ops, - .n_prop_ops = ARRAY_SIZE(st21nfcb_nci_prop_ops), -}; - -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); - - ndlc_close(info->ndlc); - - nci_unregister_device(ndev); - nci_free_device(ndev); -} -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/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_ */ From d18ee5a5b0926f1a5f6969e9207d9c4f99533f9b Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:42 +0200 Subject: [PATCH 28/39] NFC: nfcmrvl: remove integration related settings These settings are related to a specific integration that requires the firmware to drive some GPIOs for external RF coexistency. Since this is really linked to specific hardware integration let's remove them. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index ad4933cefbd1..acb37c0c5d8c 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -68,15 +68,6 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *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); - return 0; } From f1f1a7da2b3853bf55ee5aab47c8916454b65fa8 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:43 +0200 Subject: [PATCH 29/39] NFC: nfcmrvl: add support of HCI-based transport In some configuration NCI packet can be encapsulated in HCI packets. This patch had the support of this. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 35 +++++++++++++++++++++++++++++++++-- drivers/nfc/nfcmrvl/nfcmrvl.h | 19 ++++++++++++++++++- drivers/nfc/nfcmrvl/usb.c | 2 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index acb37c0c5d8c..48d8b00744df 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -63,6 +63,17 @@ 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->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); } @@ -80,10 +91,12 @@ 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 device *dev, + unsigned int flags) { struct nfcmrvl_private *priv; int rc; + int headroom = 0; u32 protocols; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -93,6 +106,10 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->drv_data = drv_data; priv->if_ops = ops; priv->dev = dev; + priv->hci_muxed = (flags & NFCMRVL_DEV_FLAG_HCI_MUXED) ? 1 : 0; + + if (priv->hci_muxed) + headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE; protocols = NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK @@ -100,7 +117,8 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, | NFC_PROTO_ISO14443_B_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; @@ -144,6 +162,19 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) return -ENOMEM; memcpy(skb_put(skb, count), data, count); + + if (priv->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; + } + } + nci_recv_frame(priv->ndev, skb); return count; diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 54c4a956bd45..b04cddd57388 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -27,7 +27,23 @@ #define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB #define NFCMRVL_NCI_MAX_EVENT_SIZE 260 +/* +** 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 + +#define NFCMRVL_DEV_FLAG_HCI_MUXED (1 << 0) + struct nfcmrvl_private { + + /* Tell if NCI packets are encapsulated in HCI ones */ + int hci_muxed; struct nci_dev *ndev; unsigned long flags; void *drv_data; @@ -45,4 +61,5 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count); struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, struct nfcmrvl_if_ops *ops, - struct device *dev); + struct device *dev, + unsigned int flags); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 6cf15c1a2618..df534b90468b 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -329,7 +329,7 @@ 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, 0); if (IS_ERR(priv)) return PTR_ERR(priv); From e1bf80c2a59895f12f596608b91856bab1c562bb Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:44 +0200 Subject: [PATCH 30/39] NFC: nfcmrvl: update nci recv frame API Update internal nci recv frame API to use skbuff phy management to generic part of the driver. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 20 +++++++++----------- drivers/nfc/nfcmrvl/nfcmrvl.h | 2 +- drivers/nfc/nfcmrvl/usb.c | 17 +++++++++++++---- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 48d8b00744df..e7f579b2d987 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -153,16 +153,8 @@ 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; - - skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - memcpy(skb_put(skb, count), data, count); - if (priv->hci_muxed) { if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE && skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) { @@ -175,9 +167,15 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) } } - nci_recv_frame(priv->ndev, skb); + 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; + } - return count; + return 0; } EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index b04cddd57388..7a10dabaf1b2 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -58,7 +58,7 @@ 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, diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index df534b90468b..f81861af5b25 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -69,18 +69,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)) From 8a81a96bd116ef5c505f37e035ce141f6b35a6a7 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:45 +0200 Subject: [PATCH 31/39] NFC: nfcmrvl: update USB device id Device ID was not restrictive enough. This patch select the USB device with the full device and interface characteristics. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index f81861af5b25..c4046b681bfa 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 */ }; From 4a2b947f56b33cde68d6e0543160ea09ce651fd9 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:46 +0200 Subject: [PATCH 32/39] NFC: nfcmrvl: add chip reset management Low level driver can specify a GPIO that will be used to reset the chip. Thanks to this the driver can ensure the state of the device at init. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 30 ++++++++++++++++++++++++++++++ drivers/nfc/nfcmrvl/nfcmrvl.h | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index e7f579b2d987..708aad28c567 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -17,6 +17,8 @@ */ #include +#include +#include #include #include #include @@ -107,6 +109,16 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->if_ops = ops; priv->dev = dev; priv->hci_muxed = (flags & NFCMRVL_DEV_FLAG_HCI_MUXED) ? 1 : 0; + priv->reset_n_io = NFCMRVL_DEV_FLAG_GET_RESET_N_IO(flags); + + if (priv->reset_n_io) { + rc = devm_gpio_request_one(dev, + priv->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->hci_muxed) headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE; @@ -127,6 +139,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); @@ -179,6 +193,22 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) } 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->reset_n_io) { + nfc_info(priv->dev, "reset the chip\n"); + gpio_set_value(priv->reset_n_io, 0); + usleep_range(5000, 10000); + gpio_set_value(priv->reset_n_io, 1); + } else + nfc_info(priv->dev, "no reset available on this interface\n"); +} + 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 7a10dabaf1b2..2edae9a4b6bd 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -39,12 +39,18 @@ #define NFCMRVL_HCI_OCF 0xFE #define NFCMRVL_DEV_FLAG_HCI_MUXED (1 << 0) +#define NFCMRVL_DEV_FLAG_SET_RESET_N_IO(X) ((X) << 16) +#define NFCMRVL_DEV_FLAG_GET_RESET_N_IO(X) ((X) >> 16) struct nfcmrvl_private { /* Tell if NCI packets are encapsulated in HCI ones */ int hci_muxed; struct nci_dev *ndev; + + /* Reset IO (0 if not available) */ + int reset_n_io; + unsigned long flags; void *drv_data; struct device *dev; @@ -63,3 +69,5 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, struct nfcmrvl_if_ops *ops, struct device *dev, unsigned int flags); + +void nfcmrvl_chip_reset(struct nfcmrvl_private *priv); From 9961127d4bce6325e9a0b0fb105e0c85a6c62cb7 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 11:25:47 +0200 Subject: [PATCH 33/39] NFC: nci: add generic uart support Some NFC controller supports UART as host interface. As with SPI, a lot of code can be shared between vendor drivers. This patch add the generic support of UART and provides some extension API for vendor specific needs. This code is strongly inspired by the Bluetooth HCI ldisc implementation. NCI UART vendor drivers will have to register themselves to this layer via nci_uart_register. Underlying tty will have to be configured from user land thanks to an ioctl. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- include/net/nfc/nci.h | 1 + include/net/nfc/nci_core.h | 47 ++++ include/uapi/linux/tty.h | 1 + net/nfc/nci/Kconfig | 7 + net/nfc/nci/Makefile | 3 + net/nfc/nci/uart.c | 495 +++++++++++++++++++++++++++++++++++++ 6 files changed, 554 insertions(+) create mode 100644 net/nfc/nci/uart.c 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 9d77ed556b78..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 @@ -391,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/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/uart.c b/net/nfc/nci/uart.c new file mode 100644 index 000000000000..70c279543074 --- /dev/null +++ b/net/nfc/nci/uart.c @@ -0,0 +1,495 @@ +/* + * 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); From dc14bdef8762a8098b1da881b611d722e24fe787 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 14:00:19 +0200 Subject: [PATCH 34/39] NFC: nfcmrvl: add platform_data and DT configuration Declare nfcmrvl platform_data structure and few DT parameters for nfcmrvl driver. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 66 +++++++++++++++++++++------ drivers/nfc/nfcmrvl/nfcmrvl.h | 36 ++++++++++----- drivers/nfc/nfcmrvl/usb.c | 6 ++- include/linux/platform_data/nfcmrvl.h | 31 +++++++++++++ 4 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 include/linux/platform_data/nfcmrvl.h diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 708aad28c567..e317a69a4560 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +66,7 @@ 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->hci_muxed) { + if (priv->config.hci_muxed) { unsigned char *hdr; unsigned char len = skb->len; @@ -92,9 +93,9 @@ 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, - unsigned int flags) + struct nfcmrvl_if_ops *ops, + struct device *dev, + struct nfcmrvl_platform_data *pdata) { struct nfcmrvl_private *priv; int rc; @@ -108,23 +109,24 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->drv_data = drv_data; priv->if_ops = ops; priv->dev = dev; - priv->hci_muxed = (flags & NFCMRVL_DEV_FLAG_HCI_MUXED) ? 1 : 0; - priv->reset_n_io = NFCMRVL_DEV_FLAG_GET_RESET_N_IO(flags); - if (priv->reset_n_io) { + memcpy(&priv->config, pdata, sizeof(*pdata)); + + if (priv->config.reset_n_io) { rc = devm_gpio_request_one(dev, - priv->reset_n_io, + 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->hci_muxed) + 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_NFC_DEP_MASK; @@ -169,7 +171,7 @@ EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) { - if (priv->hci_muxed) { + 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 */ @@ -200,15 +202,51 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) * To be improved. */ - if (priv->reset_n_io) { + if (priv->config.reset_n_io) { nfc_info(priv->dev, "reset the chip\n"); - gpio_set_value(priv->reset_n_io, 0); + gpio_set_value(priv->config.reset_n_io, 0); usleep_range(5000, 10000); - gpio_set_value(priv->reset_n_io, 1); + 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 2edae9a4b6bd..214412bd6110 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 @@ -38,22 +43,25 @@ #define NFCMRVL_HCI_OGF 0x81 #define NFCMRVL_HCI_OCF 0xFE -#define NFCMRVL_DEV_FLAG_HCI_MUXED (1 << 0) -#define NFCMRVL_DEV_FLAG_SET_RESET_N_IO(X) ((X) << 16) -#define NFCMRVL_DEV_FLAG_GET_RESET_N_IO(X) ((X) >> 16) struct nfcmrvl_private { - /* Tell if NCI packets are encapsulated in HCI ones */ - int hci_muxed; + unsigned long flags; + + /* Platform configuration */ + struct nfcmrvl_platform_data config; + struct nci_dev *ndev; - /* Reset IO (0 if not available) */ - int reset_n_io; + /* + ** PHY related information + */ - unsigned long flags; + /* PHY driver context */ void *drv_data; + /* PHY device */ struct device *dev; + /* Low level driver ops */ struct nfcmrvl_if_ops *if_ops; }; @@ -66,8 +74,14 @@ struct nfcmrvl_if_ops { void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); 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, - unsigned int flags); + 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/usb.c b/drivers/nfc/nfcmrvl/usb.c index c4046b681bfa..aa3f3c1cff0a 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -302,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); @@ -339,7 +343,7 @@ 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, 0); + &drv_data->udev->dev, &config); if (IS_ERR(priv)) return PTR_ERR(priv); diff --git a/include/linux/platform_data/nfcmrvl.h b/include/linux/platform_data/nfcmrvl.h new file mode 100644 index 000000000000..106cfe5ed589 --- /dev/null +++ b/include/linux/platform_data/nfcmrvl.h @@ -0,0 +1,31 @@ +/* + * 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; +}; + +#endif /* _NFCMRVL_PTF_H_ */ From e097dc624f784debbde49701a493bf920bc422c7 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Thu, 11 Jun 2015 14:00:20 +0200 Subject: [PATCH 35/39] NFC: nfcmrvl: add UART driver Add support of Marvell NFC chip controlled over UART Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/net/nfc/nfcmrvl.txt | 29 +++ drivers/nfc/nfcmrvl/Kconfig | 11 + drivers/nfc/nfcmrvl/Makefile | 3 + drivers/nfc/nfcmrvl/nfcmrvl.h | 7 + drivers/nfc/nfcmrvl/uart.c | 225 ++++++++++++++++++ include/linux/platform_data/nfcmrvl.h | 9 + net/nfc/nci/uart.c | 1 - 7 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt create mode 100644 drivers/nfc/nfcmrvl/uart.c 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/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/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 214412bd6110..09780d57c9b8 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -43,6 +43,11 @@ #define NFCMRVL_HCI_OGF 0x81 #define NFCMRVL_HCI_OCF 0xFE +enum nfcmrvl_phy { + NFCMRVL_PHY_USB = 0, + NFCMRVL_PHY_UART = 1, +}; + struct nfcmrvl_private { @@ -61,6 +66,8 @@ struct nfcmrvl_private { void *drv_data; /* PHY device */ struct device *dev; + /* PHY type */ + enum nfcmrvl_phy phy; /* Low level driver ops */ struct nfcmrvl_if_ops *if_ops; }; 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/include/linux/platform_data/nfcmrvl.h b/include/linux/platform_data/nfcmrvl.h index 106cfe5ed589..ac91707dabcb 100644 --- a/include/linux/platform_data/nfcmrvl.h +++ b/include/linux/platform_data/nfcmrvl.h @@ -26,6 +26,15 @@ struct nfcmrvl_platform_data { 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/net/nfc/nci/uart.c b/net/nfc/nci/uart.c index 70c279543074..2c48810af5bb 100644 --- a/net/nfc/nci/uart.c +++ b/net/nfc/nci/uart.c @@ -12,7 +12,6 @@ * 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. From 43465b7af2e6c0cdcbe4771fa268786f2ccd642d Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Fri, 12 Jun 2015 15:35:51 +0200 Subject: [PATCH 36/39] NFC: nfcmrvl: small fix in USB driver Marvell NFC USB driver has to be updated to follow new multi PHY driver interface. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index aa3f3c1cff0a..7d1fe436c9f6 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -348,6 +348,7 @@ static int nfcmrvl_probe(struct usb_interface *intf, 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); From 83d567259b943bbe85aba563b521f7b5611c6a1c Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Fri, 12 Jun 2015 15:35:52 +0200 Subject: [PATCH 37/39] NFC: nfcmrvl: Allow ISO15693 protocol Reference Marvell NFC controller as ISO15693 capable. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index e317a69a4560..54ed8acc2a71 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -129,6 +129,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, | 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, From 34ac49664149dd8923e0de5d871f86c80292d27b Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Fri, 12 Jun 2015 15:35:53 +0200 Subject: [PATCH 38/39] NFC: nci: remove current SLEEP mode management NCI deactivate management was modified to support all NCI deactivation type. Problem is that all the API are not ready yet for it. Problem is that with current code, when neard asks to deactivate the tag it sends a deactivate SLEEP but nobody will then send a IDLE deactivate. This IDLE deactivate is mandatory since NFC controller can only be unlocked by DH. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f9aa08780b06..95af2d24d5be 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -798,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)); } } From d0dcad8bd32a34aa85bcbd5d2033658cb3964377 Mon Sep 17 00:00:00 2001 From: Vincent Cuissard Date: Fri, 12 Jun 2015 15:35:54 +0200 Subject: [PATCH 39/39] NFC: nfcmrvl: set PB_BAIL_OUT at setup PB_BAIL_OUT parameter as to be set to one. This is needed because digital protocol 1.0 is used in combination with ISO15693 protocol. Signed-off-by: Vincent Cuissard Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 3 +++ drivers/nfc/nfcmrvl/nfcmrvl.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 54ed8acc2a71..4a8866d62941 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -82,6 +82,9 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) static int nfcmrvl_nci_setup(struct nci_dev *ndev) { + __u8 val = 1; + + nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val); return 0; } diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 09780d57c9b8..e5a7e5464f2e 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -32,6 +32,12 @@ #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 */