diff --git a/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt new file mode 100644 index 0000000000000000000000000000000000000000..5b6cd9b3f628aa3f266e03179f60d1b2447b30b6 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt @@ -0,0 +1,35 @@ +* NXP Semiconductors NXP NCI NFC Controllers + +Required properties: +- compatible: Should be "nxp,nxp-nci-i2c". +- clock-frequency: I²C work frequency. +- reg: address on the bus +- interrupt-parent: phandle for the interrupt gpio controller +- interrupts: GPIO interrupt to which the chip is connected +- enable-gpios: Output GPIO pin used for enabling/disabling the chip +- firmware-gpios: Output GPIO pin used to enter firmware download mode + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2): + +&i2c2 { + + status = "okay"; + + npc100: npc100@29 { + + compatible = "nxp,nxp-nci-i2c"; + + reg = <0x29>; + clock-frequency = <100000>; + + interrupt-parent = <&gpio1>; + interrupts = <29 GPIO_ACTIVE_HIGH>; + + enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>; + firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 9091b4ad1cc3c74c380da5e1eff26c14f8e165d1..dcaa542524790f94e02c06f3faeb0f1c5a8be7f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6944,6 +6944,13 @@ S: Supported F: drivers/block/nvme* F: include/linux/nvme.h +NXP-NCI NFC DRIVER +M: Clément Perrochaud +R: Charles Gorand +L: linux-nfc@lists.01.org (moderated for non-subscribers) +S: Supported +F: drivers/nfc/nxp-nci + NXP TDA998X DRM DRIVER M: Russell King S: Supported diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 7929fac13e1c55ac072d46f6c2d3a6ea10528fa7..107714e4405fd348331c6f2ecdbfc18c43709d51 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -73,4 +73,5 @@ source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" source "drivers/nfc/st21nfca/Kconfig" source "drivers/nfc/st21nfcb/Kconfig" +source "drivers/nfc/nxp-nci/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 6b23a2c6e34adf7c1ed35e0aa7e40aa2e9a7cc66..a4292d790f9b900130462d7f4bf7b3f64e3dec7b 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ 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/microread/i2c.c b/drivers/nfc/microread/i2c.c index df85cd3d9db0c76e1d80ebbf3bcc204f3e3f8a0c..661e2c8143c40a8e4ab6e98b76a007eafa9a6fd4 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -286,7 +286,7 @@ static int microread_i2c_probe(struct i2c_client *client, if (r < 0) goto err_irq; - nfc_info(&client->dev, "Probed"); + nfc_info(&client->dev, "Probed\n"); return 0; diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 85e8bcf986936123bdab959e5b01fb8738d80e79..ad4933cefbd1b2c60ae5ef23aeece275d2fcf0fd 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -111,7 +111,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); if (!priv->ndev) { - nfc_err(dev, "nci_allocate_device failed"); + nfc_err(dev, "nci_allocate_device failed\n"); rc = -ENOMEM; goto error; } @@ -120,7 +120,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, rc = nci_register_device(priv->ndev); if (rc) { - nfc_err(dev, "nci_register_device failed %d", rc); + nfc_err(dev, "nci_register_device failed %d\n", rc); nci_free_device(priv->ndev); goto error; } diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 3221ca37d6c9463700af6ed4f5a25fe7d4825379..6cf15c1a261831dadc5194bdc3615c7362c6ce0d 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -80,7 +80,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb) 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"); + nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); } if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) @@ -96,7 +96,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb) */ if (err != -EPERM && err != -ENODEV) nfc_err(&drv_data->udev->dev, - "urb %p failed to resubmit (%d)", urb, -err); + "urb %p failed to resubmit (%d)\n", urb, -err); usb_unanchor_urb(urb); } } @@ -137,7 +137,7 @@ nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) if (err) { if (err != -EPERM && err != -ENODEV) nfc_err(&drv_data->udev->dev, - "urb %p submission failed (%d)", urb, -err); + "urb %p submission failed (%d)\n", urb, -err); usb_unanchor_urb(urb); } @@ -153,7 +153,7 @@ static void nfcmrvl_tx_complete(struct urb *urb) struct nfcmrvl_private *priv = nci_get_drvdata(ndev); struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; - nfc_info(priv->dev, "urb %p status %d count %d", + nfc_info(priv->dev, "urb %p status %d count %d\n", urb, urb->status, urb->actual_length); spin_lock(&drv_data->txlock); @@ -253,7 +253,7 @@ static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, if (err) { if (err != -EPERM && err != -ENODEV) nfc_err(&drv_data->udev->dev, - "urb %p submission failed (%d)", urb, -err); + "urb %p submission failed (%d)\n", urb, -err); kfree(urb->setup_packet); usb_unanchor_urb(urb); } else { @@ -293,7 +293,7 @@ static int nfcmrvl_probe(struct usb_interface *intf, int i; struct usb_device *udev = interface_to_usbdev(intf); - nfc_info(&udev->dev, "intf %p id %p", intf, id); + nfc_info(&udev->dev, "intf %p id %p\n", intf, id); drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) @@ -348,7 +348,7 @@ static void nfcmrvl_disconnect(struct usb_interface *intf) if (!drv_data) return; - nfc_info(&drv_data->udev->dev, "intf %p", intf); + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); nfcmrvl_nci_unregister_dev(drv_data->priv); @@ -360,7 +360,7 @@ static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) { struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); - nfc_info(&drv_data->udev->dev, "intf %p", intf); + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); if (drv_data->suspend_count++) return 0; @@ -401,7 +401,7 @@ static int nfcmrvl_resume(struct usb_interface *intf) struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); int err = 0; - nfc_info(&drv_data->udev->dev, "intf %p", intf); + nfc_info(&drv_data->udev->dev, "intf %p\n", intf); if (--drv_data->suspend_count) return 0; diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..37b40612520d94511cdc9a72e7822719e1523439 --- /dev/null +++ b/drivers/nfc/nxp-nci/Kconfig @@ -0,0 +1,25 @@ +config NFC_NXP_NCI + tristate "NXP-NCI NFC driver" + depends on NFC_NCI + default n + ---help--- + Generic core driver for NXP NCI chips such as the NPC100 + or PN7150 families. + This is a driver based on the NCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called nxp_nci. + Say N if unsure. + +config NFC_NXP_NCI_I2C + tristate "NXP-NCI I2C support" + depends on NFC_NXP_NCI && I2C + ---help--- + This module adds support for an I2C interface to the NXP NCI + chips. + Select this if your platform is using the I2C bus. + + To compile this driver as a module, choose m here. The module will + be called nxp_nci_i2c. + Say Y if unsure. diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c008be30bb187a072642bdd22a15351588579a93 --- /dev/null +++ b/drivers/nfc/nxp-nci/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for NXP-NCI NFC driver +# + +nxp-nci-objs = core.o firmware.o +nxp-nci_i2c-objs = i2c.o + +obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o +obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o + +ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c new file mode 100644 index 0000000000000000000000000000000000000000..8979636d48eaa117e933ec85b0850bf6e22dab97 --- /dev/null +++ b/drivers/nfc/nxp-nci/core.c @@ -0,0 +1,186 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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 "nxp-nci.h" + +#define NXP_NCI_HDR_LEN 4 + +#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_NFC_DEP_MASK) + +static int nxp_nci_open(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->mode != NXP_NCI_MODE_COLD) { + r = -EBUSY; + goto open_exit; + } + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); + + info->mode = NXP_NCI_MODE_NCI; + +open_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_close(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r = 0; + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + info->mode = NXP_NCI_MODE_COLD; + + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (!info->phy_ops->write) { + r = -ENOTSUPP; + goto send_exit; + } + + if (info->mode != NXP_NCI_MODE_NCI) { + r = -EINVAL; + goto send_exit; + } + + r = info->phy_ops->write(info->phy_id, skb); + if (r < 0) + kfree_skb(skb); + +send_exit: + return r; +} + +static struct nci_ops nxp_nci_ops = { + .open = nxp_nci_open, + .close = nxp_nci_close, + .send = nxp_nci_send, + .fw_download = nxp_nci_fw_download, +}; + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev) +{ + struct nxp_nci_info *info; + int r; + + info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto probe_exit; + } + + info->phy_id = phy_id; + info->pdev = pdev; + info->phy_ops = phy_ops; + info->max_payload = max_payload; + INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); + init_completion(&info->fw_info.cmd_completion); + mutex_init(&info->info_lock); + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0) + goto probe_exit; + } + + info->mode = NXP_NCI_MODE_COLD; + + info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, + NXP_NCI_HDR_LEN, 0); + if (!info->ndev) { + r = -ENOMEM; + goto probe_exit; + } + + nci_set_parent_dev(info->ndev, pdev); + nci_set_drvdata(info->ndev, info); + r = nci_register_device(info->ndev); + if (r < 0) + goto probe_exit_free_nci; + + *ndev = info->ndev; + + goto probe_exit; + +probe_exit_free_nci: + nci_free_device(info->ndev); +probe_exit: + return r; +} +EXPORT_SYMBOL(nxp_nci_probe); + +void nxp_nci_remove(struct nci_dev *ndev) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + + if (info->mode == NXP_NCI_MODE_FW) + nxp_nci_fw_work_complete(info, -ESHUTDOWN); + cancel_work_sync(&info->fw_info.work); + + mutex_lock(&info->info_lock); + + if (info->phy_ops->set_mode) + info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + + nci_unregister_device(ndev); + nci_free_device(ndev); + + mutex_unlock(&info->info_lock); +} +EXPORT_SYMBOL(nxp_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NXP NCI NFC driver"); +MODULE_AUTHOR("Clément Perrochaud "); diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c new file mode 100644 index 0000000000000000000000000000000000000000..5291797324bae0752098a15b6b3f7221c612b8ce --- /dev/null +++ b/drivers/nfc/nxp-nci/firmware.c @@ -0,0 +1,325 @@ +/* + * Generic driver for NXP NCI NFC chips + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Author: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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 "nxp-nci.h" + +/* Crypto operations can take up to 30 seconds */ +#define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000) + +#define NXP_NCI_FW_CMD_RESET 0xF0 +#define NXP_NCI_FW_CMD_GETVERSION 0xF1 +#define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0 +#define NXP_NCI_FW_CMD_WRITE 0xC0 +#define NXP_NCI_FW_CMD_READ 0xA2 +#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2 +#define NXP_NCI_FW_CMD_LOG 0xA7 +#define NXP_NCI_FW_CMD_FORCE 0xD0 +#define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4 + +#define NXP_NCI_FW_CHUNK_FLAG 0x0400 + +#define NXP_NCI_FW_RESULT_OK 0x00 +#define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01 +#define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02 +#define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B +#define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C +#define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D +#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E +#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F +#define NXP_NCI_FW_RESULT_MEM_BSY 0x20 +#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21 +#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24 +#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28 +#define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A +#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D +#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E +#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5 + +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + if (info->phy_ops->set_mode) { + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); + if (r < 0 && result == 0) + result = -r; + } + + info->mode = NXP_NCI_MODE_COLD; + + if (fw_info->fw) { + release_firmware(fw_info->fw); + fw_info->fw = NULL; + } + + nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result); +} + +/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */ +static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len) +{ + u16 crc = 0xffff; + + while (len--) { + crc = ((crc >> 8) | (crc << 8)) ^ *buffer++; + crc ^= (crc & 0xff) >> 4; + crc ^= (crc & 0xff) << 12; + crc ^= (crc & 0xff) << 5; + } + + return crc; +} + +static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + u16 header, crc; + struct sk_buff *skb; + size_t chunk_len; + size_t remaining_len; + int r; + + skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL); + if (!skb) { + r = -ENOMEM; + goto chunk_exit; + } + + chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN; + remaining_len = fw_info->frame_size - fw_info->written; + + if (remaining_len > chunk_len) { + header = NXP_NCI_FW_CHUNK_FLAG; + } else { + chunk_len = remaining_len; + header = 0x0000; + } + + header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK; + put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN)); + + memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written, + chunk_len); + + crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN); + put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN)); + + r = info->phy_ops->write(info->phy_id, skb); + if (r >= 0) + r = chunk_len; + + kfree_skb(skb); + +chunk_exit: + return r; +} + +static int nxp_nci_fw_send(struct nxp_nci_info *info) +{ + struct nxp_nci_fw_info *fw_info = &info->fw_info; + long completion_rc; + int r; + + reinit_completion(&fw_info->cmd_completion); + + if (fw_info->written == 0) { + fw_info->frame_size = get_unaligned_be16(fw_info->data) & + NXP_NCI_FW_FRAME_LEN_MASK; + fw_info->data += NXP_NCI_FW_HDR_LEN; + fw_info->size -= NXP_NCI_FW_HDR_LEN; + } + + if (fw_info->frame_size > fw_info->size) + return -EMSGSIZE; + + r = nxp_nci_fw_send_chunk(info); + if (r < 0) + return r; + + fw_info->written += r; + + if (*fw_info->data == NXP_NCI_FW_CMD_RESET) { + fw_info->cmd_result = 0; + if (fw_info->fw) + schedule_work(&fw_info->work); + } else { + completion_rc = wait_for_completion_interruptible_timeout( + &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT); + if (completion_rc == 0) + return -ETIMEDOUT; + } + + return 0; +} + +void nxp_nci_fw_work(struct work_struct *work) +{ + struct nxp_nci_info *info; + struct nxp_nci_fw_info *fw_info; + int r; + + fw_info = container_of(work, struct nxp_nci_fw_info, work); + info = container_of(fw_info, struct nxp_nci_info, fw_info); + + mutex_lock(&info->info_lock); + + r = fw_info->cmd_result; + if (r < 0) + goto exit_work; + + if (fw_info->written == fw_info->frame_size) { + fw_info->data += fw_info->frame_size; + fw_info->size -= fw_info->frame_size; + fw_info->written = 0; + } + + if (fw_info->size > 0) + r = nxp_nci_fw_send(info); + +exit_work: + if (r < 0 || fw_info->size == 0) + nxp_nci_fw_work_complete(info, r); + mutex_unlock(&info->info_lock); +} + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + int r; + + mutex_lock(&info->info_lock); + + if (!info->phy_ops->set_mode || !info->phy_ops->write) { + r = -ENOTSUPP; + goto fw_download_exit; + } + + if (!firmware_name || firmware_name[0] == '\0') { + r = -EINVAL; + goto fw_download_exit; + } + + strcpy(fw_info->name, firmware_name); + + r = request_firmware(&fw_info->fw, firmware_name, + ndev->nfc_dev->dev.parent); + if (r < 0) + goto fw_download_exit; + + r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW); + if (r < 0) { + release_firmware(fw_info->fw); + goto fw_download_exit; + } + + info->mode = NXP_NCI_MODE_FW; + + fw_info->data = fw_info->fw->data; + fw_info->size = fw_info->fw->size; + fw_info->written = 0; + fw_info->frame_size = 0; + fw_info->cmd_result = 0; + + schedule_work(&fw_info->work); + +fw_download_exit: + mutex_unlock(&info->info_lock); + return r; +} + +static int nxp_nci_fw_read_status(u8 stat) +{ + switch (stat) { + case NXP_NCI_FW_RESULT_OK: + return 0; + case NXP_NCI_FW_RESULT_INVALID_ADDR: + return -EINVAL; + case NXP_NCI_FW_RESULT_UNKNOWN_CMD: + return -EINVAL; + case NXP_NCI_FW_RESULT_ABORTED_CMD: + return -EMSGSIZE; + case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: + return -EADDRNOTAVAIL; + case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: + return -ENOBUFS; + case NXP_NCI_FW_RESULT_MEM_BSY: + return -ENOKEY; + case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: + return -EKEYREJECTED; + case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: + return -EALREADY; + case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: + return -EPROTO; + case NXP_NCI_FW_RESULT_SFWU_DEGRADED: + return -EHWPOISON; + case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: + return 0; + case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: + return -EINVAL; + default: + return -EIO; + } +} + +static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) +{ + u16 crc, frame_crc; + size_t len = skb->len - NXP_NCI_FW_CRC_LEN; + + crc = nxp_nci_fw_crc(skb->data, len); + frame_crc = get_unaligned_be16(skb->data + len); + + return (crc ^ frame_crc); +} + +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nxp_nci_info *info = nci_get_drvdata(ndev); + struct nxp_nci_fw_info *fw_info = &info->fw_info; + + complete(&fw_info->cmd_completion); + + if (skb) { + if (nxp_nci_fw_check_crc(skb) != 0x00) + fw_info->cmd_result = -EBADMSG; + else + fw_info->cmd_result = nxp_nci_fw_read_status( + *skb_pull(skb, NXP_NCI_FW_HDR_LEN)); + kfree_skb(skb); + } else { + fw_info->cmd_result = -EIO; + } + + if (fw_info->fw) + schedule_work(&fw_info->work); +} +EXPORT_SYMBOL(nxp_nci_fw_recv_frame); diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..17bd67dbebf05c27d2265752d200af9ff6deb932 --- /dev/null +++ b/drivers/nfc/nxp-nci/i2c.c @@ -0,0 +1,415 @@ +/* + * I2C link layer for the NXP NCI driver + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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 . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nxp-nci.h" + +#define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c" + +#define NXP_NCI_I2C_MAX_PAYLOAD 32 + +struct nxp_nci_i2c_phy { + struct i2c_client *i2c_dev; + struct nci_dev *ndev; + + unsigned int gpio_en; + unsigned int gpio_fw; + + int hard_fault; /* + * < 0 if hardware error occurred (e.g. i2c err) + * and prevents normal operation. + */ +}; + +static int nxp_nci_i2c_set_mode(void *phy_id, + enum nxp_nci_mode mode) +{ + struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id; + + gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0); + gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0); + usleep_range(10000, 15000); + + if (mode == NXP_NCI_MODE_COLD) + phy->hard_fault = 0; + + return 0; +} + +static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r; + struct nxp_nci_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + if (phy->hard_fault != 0) + return phy->hard_fault; + + r = i2c_master_send(client, skb->data, skb->len); + if (r == -EREMOTEIO) { + /* Retry, chip was in standby */ + usleep_range(110000, 120000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r < 0) { + nfc_err(&client->dev, "Error %d on I2C send\n", r); + } else if (r != skb->len) { + nfc_err(&client->dev, + "Invalid length sent: %u (expected %u)\n", + r, skb->len); + r = -EREMOTEIO; + } else { + /* Success but return 0 and not number of bytes */ + r = 0; + } + + return r; +} + +static struct nxp_nci_phy_ops i2c_phy_ops = { + .set_mode = nxp_nci_i2c_set_mode, + .write = nxp_nci_i2c_write, +}; + +static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy, + struct sk_buff **skb) +{ + struct i2c_client *client = phy->i2c_dev; + u16 header; + size_t frame_len; + int r; + + r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN); + if (r < 0) { + goto fw_read_exit; + } else if (r != NXP_NCI_FW_HDR_LEN) { + nfc_err(&client->dev, "Incorrect header length: %u\n", r); + r = -EBADMSG; + goto fw_read_exit; + } + + frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) + + NXP_NCI_FW_CRC_LEN; + + *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto fw_read_exit; + } + + memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN); + + r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len); + if (r != frame_len) { + nfc_err(&client->dev, + "Invalid frame length: %u (expected %zu)\n", + r, frame_len); + r = -EBADMSG; + goto fw_read_exit_free_skb; + } + + return 0; + +fw_read_exit_free_skb: + kfree_skb(*skb); +fw_read_exit: + return r; +} + +static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy, + struct sk_buff **skb) +{ + struct nci_ctrl_hdr header; /* May actually be a data header */ + struct i2c_client *client = phy->i2c_dev; + int r; + + r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE); + if (r < 0) { + goto nci_read_exit; + } else if (r != NCI_CTRL_HDR_SIZE) { + nfc_err(&client->dev, "Incorrect header length: %u\n", r); + r = -EBADMSG; + goto nci_read_exit; + } + + *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL); + if (*skb == NULL) { + r = -ENOMEM; + goto nci_read_exit; + } + + memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header, + NCI_CTRL_HDR_SIZE); + + r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen); + if (r != header.plen) { + nfc_err(&client->dev, + "Invalid frame payload length: %u (expected %u)\n", + r, header.plen); + r = -EBADMSG; + goto nci_read_exit_free_skb; + } + + return 0; + +nci_read_exit_free_skb: + kfree_skb(*skb); +nci_read_exit: + return r; +} + +static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id) +{ + struct nxp_nci_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct nxp_nci_info *info; + + struct sk_buff *skb = NULL; + int r = 0; + + if (!phy || !phy->ndev) + goto exit_irq_none; + + client = phy->i2c_dev; + + if (!client || irq != client->irq) + goto exit_irq_none; + + info = nci_get_drvdata(phy->ndev); + + if (!info) + goto exit_irq_none; + + mutex_lock(&info->info_lock); + + if (phy->hard_fault != 0) + goto exit_irq_handled; + + switch (info->mode) { + case NXP_NCI_MODE_NCI: + r = nxp_nci_i2c_nci_read(phy, &skb); + break; + case NXP_NCI_MODE_FW: + r = nxp_nci_i2c_fw_read(phy, &skb); + break; + case NXP_NCI_MODE_COLD: + r = -EREMOTEIO; + break; + } + + if (r == -EREMOTEIO) { + phy->hard_fault = r; + skb = NULL; + } else if (r < 0) { + nfc_err(&client->dev, "Read failed with error %d\n", r); + goto exit_irq_handled; + } + + switch (info->mode) { + case NXP_NCI_MODE_NCI: + nci_recv_frame(phy->ndev, skb); + break; + case NXP_NCI_MODE_FW: + nxp_nci_fw_recv_frame(phy->ndev, skb); + break; + case NXP_NCI_MODE_COLD: + break; + } + +exit_irq_handled: + mutex_unlock(&info->info_lock); + return IRQ_HANDLED; +exit_irq_none: + WARN_ON_ONCE(1); + return IRQ_NONE; +} + +#ifdef CONFIG_OF + +static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) +{ + struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + r = of_get_named_gpio(pp, "enable-gpios", 0); + if (r == -EPROBE_DEFER) + r = of_get_named_gpio(pp, "enable-gpios", 0); + if (r < 0) { + nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r); + return r; + } + phy->gpio_en = r; + + r = of_get_named_gpio(pp, "firmware-gpios", 0); + if (r == -EPROBE_DEFER) + r = of_get_named_gpio(pp, "firmware-gpios", 0); + if (r < 0) { + nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r); + return r; + } + phy->gpio_fw = r; + + r = irq_of_parse_and_map(pp, 0); + if (r < 0) { + nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); + return r; + } + client->irq = r; + + return 0; +} + +#else + +static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) +{ + return -ENODEV; +} + +#endif + +static int nxp_nci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nxp_nci_i2c_phy *phy; + struct nxp_nci_nfc_platform_data *pdata; + int r; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + r = -ENODEV; + goto probe_exit; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy), + GFP_KERNEL); + if (!phy) { + r = -ENOMEM; + goto probe_exit; + } + + phy->i2c_dev = client; + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + + if (!pdata && client->dev.of_node) { + r = nxp_nci_i2c_parse_devtree(client); + if (r < 0) { + nfc_err(&client->dev, "Failed to get DT data\n"); + goto probe_exit; + } + } else if (pdata) { + phy->gpio_en = pdata->gpio_en; + phy->gpio_fw = pdata->gpio_fw; + client->irq = pdata->irq; + } else { + nfc_err(&client->dev, "No platform data\n"); + r = -EINVAL; + goto probe_exit; + } + + r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, + GPIOF_OUT_INIT_LOW, "nxp_nci_en"); + if (r < 0) + goto probe_exit; + + r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw, + GPIOF_OUT_INIT_LOW, "nxp_nci_fw"); + if (r < 0) + goto probe_exit; + + r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops, + NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev); + if (r < 0) + goto probe_exit; + + r = request_threaded_irq(client->irq, NULL, + nxp_nci_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + NXP_NCI_I2C_DRIVER_NAME, phy); + if (r < 0) + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + +probe_exit: + return r; +} + +static int nxp_nci_i2c_remove(struct i2c_client *client) +{ + struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); + + nxp_nci_remove(phy->ndev); + free_irq(client->irq, phy); + + return 0; +} + +static struct i2c_device_id nxp_nci_i2c_id_table[] = { + {"nxp-nci_i2c", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table); + +static const struct of_device_id of_nxp_nci_i2c_match[] = { + { .compatible = "nxp,nxp-nci-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); + +static struct i2c_driver nxp_nci_i2c_driver = { + .driver = { + .name = NXP_NCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_nxp_nci_i2c_match), + }, + .probe = nxp_nci_i2c_probe, + .id_table = nxp_nci_i2c_id_table, + .remove = nxp_nci_i2c_remove, +}; + +module_i2c_driver(nxp_nci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers"); +MODULE_AUTHOR("Clément Perrochaud "); diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h new file mode 100644 index 0000000000000000000000000000000000000000..f1fecc4e24578d299c71a678f2dd54b21400944f --- /dev/null +++ b/drivers/nfc/nxp-nci/nxp-nci.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * Derived from PN544 device driver: + * Copyright (C) 2012 Intel Corporation. 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_NXP_NCI_H_ +#define __LOCAL_NXP_NCI_H_ + +#include +#include +#include +#include + +#include + +#define NXP_NCI_FW_HDR_LEN 2 +#define NXP_NCI_FW_CRC_LEN 2 + +#define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF + +enum nxp_nci_mode { + NXP_NCI_MODE_COLD, + NXP_NCI_MODE_NCI, + NXP_NCI_MODE_FW +}; + +struct nxp_nci_phy_ops { + int (*set_mode)(void *id, enum nxp_nci_mode mode); + int (*write)(void *id, struct sk_buff *skb); +}; + +struct nxp_nci_fw_info { + char name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; + const struct firmware *fw; + + size_t size; + size_t written; + + const u8 *data; + size_t frame_size; + + struct work_struct work; + struct completion cmd_completion; + + int cmd_result; +}; + +struct nxp_nci_info { + struct nci_dev *ndev; + void *phy_id; + struct device *pdev; + + enum nxp_nci_mode mode; + + struct nxp_nci_phy_ops *phy_ops; + unsigned int max_payload; + + struct mutex info_lock; + + struct nxp_nci_fw_info fw_info; +}; + +int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name); +void nxp_nci_fw_work(struct work_struct *work); +void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result); + +int nxp_nci_probe(void *phy_id, struct device *pdev, + struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, + struct nci_dev **ndev); +void nxp_nci_remove(struct nci_dev *ndev); + +#endif /* __LOCAL_NXP_NCI_H_ */ diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d46a700a9637a9152aee5bc5c41b58eacfbe8ec5..a03e4eb5fe2978c24d10899d249ddc2373968db6 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1820,7 +1820,7 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg, if (IS_ERR(resp)) { rc = PTR_ERR(resp); - nfc_err(&dev->interface->dev, "RF setting error %d", rc); + nfc_err(&dev->interface->dev, "RF setting error %d\n", rc); return rc; } @@ -2554,8 +2554,10 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, } skb = pn533_build_response(dev); - if (!skb) + if (!skb) { + rc = -ENOMEM; goto error; + } arg->cb(arg->cb_context, skb, 0); kfree(arg); diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index cdde745b96bd142e07a90b441447ed57bd94b087..6fd986f5ac3ed522e46a1c226ba418f4576b1711 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -953,7 +953,7 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) } nfc_info(dev, "GPIO resource, no:%d irq:%d\n", - desc_to_gpio(gpiod_irq), ret); + desc_to_gpio(gpiod_irq), ret); client->irq = ret; return 0; @@ -1062,11 +1062,8 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), GFP_KERNEL); - if (!phy) { - nfc_err(&client->dev, - "Cannot allocate memory for pn544 i2c phy.\n"); + if (!phy) return -ENOMEM; - } INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work); phy->fw_work_state = FW_WORK_STATE_IDLE; diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 4ac4d31f6c598309a9c9e35f871cd0316419ba5f..87d509996704039c3ac224dcb95bb4b096570542 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -604,11 +604,11 @@ static void port100_recv_response(struct urb *urb) case -ECONNRESET: case -ENOENT: nfc_err(&dev->interface->dev, - "The urb has been canceled (status %d)", urb->status); + "The urb has been canceled (status %d)\n", urb->status); goto sched_wq; case -ESHUTDOWN: default: - nfc_err(&dev->interface->dev, "Urb failure (status %d)", + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", urb->status); goto sched_wq; } @@ -616,7 +616,7 @@ static void port100_recv_response(struct urb *urb) in_frame = dev->in_urb->transfer_buffer; if (!port100_rx_frame_is_valid(in_frame)) { - nfc_err(&dev->interface->dev, "Received an invalid frame"); + nfc_err(&dev->interface->dev, "Received an invalid frame\n"); cmd->status = -EIO; goto sched_wq; } @@ -626,7 +626,7 @@ static void port100_recv_response(struct urb *urb) if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { nfc_err(&dev->interface->dev, - "It's not the response to the last command"); + "It's not the response to the last command\n"); cmd->status = -EIO; goto sched_wq; } @@ -657,11 +657,11 @@ static void port100_recv_ack(struct urb *urb) case -ECONNRESET: case -ENOENT: nfc_err(&dev->interface->dev, - "The urb has been stopped (status %d)", urb->status); + "The urb has been stopped (status %d)\n", urb->status); goto sched_wq; case -ESHUTDOWN: default: - nfc_err(&dev->interface->dev, "Urb failure (status %d)", + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", urb->status); goto sched_wq; } @@ -669,7 +669,7 @@ static void port100_recv_ack(struct urb *urb) in_frame = dev->in_urb->transfer_buffer; if (!port100_rx_frame_is_ack(in_frame)) { - nfc_err(&dev->interface->dev, "Received an invalid ack"); + nfc_err(&dev->interface->dev, "Received an invalid ack\n"); cmd->status = -EIO; goto sched_wq; } @@ -677,7 +677,7 @@ static void port100_recv_ack(struct urb *urb) rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); if (rc) { nfc_err(&dev->interface->dev, - "usb_submit_urb failed with result %d", rc); + "usb_submit_urb failed with result %d\n", rc); cmd->status = rc; goto sched_wq; } @@ -873,11 +873,11 @@ static void port100_send_complete(struct urb *urb) case -ECONNRESET: case -ENOENT: nfc_err(&dev->interface->dev, - "The urb has been stopped (status %d)", urb->status); + "The urb has been stopped (status %d)\n", urb->status); break; case -ESHUTDOWN: default: - nfc_err(&dev->interface->dev, "Urb failure (status %d)", + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", urb->status); } } @@ -1094,7 +1094,7 @@ static void port100_in_comm_rf_complete(struct port100 *dev, void *arg, if (resp->len < 4) { nfc_err(&dev->interface->dev, - "Invalid packet length received.\n"); + "Invalid packet length received\n"); rc = -EIO; goto error; } @@ -1250,7 +1250,7 @@ static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated) PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; break; default: - nfc_err(&dev->interface->dev, "Unknonwn command type.\n"); + nfc_err(&dev->interface->dev, "Unknown command type\n"); return false; } @@ -1481,7 +1481,7 @@ static int port100_probe(struct usb_interface *interface, cmd_type_mask = port100_get_command_type_mask(dev); if (!cmd_type_mask) { nfc_err(&interface->dev, - "Could not get supported command types.\n"); + "Could not get supported command types\n"); rc = -ENODEV; goto error; } @@ -1494,7 +1494,7 @@ static int port100_probe(struct usb_interface *interface, rc = port100_set_command_type(dev, dev->cmd_type); if (rc) { nfc_err(&interface->dev, - "The device does not support command type %u.\n", + "The device does not support command type %u\n", dev->cmd_type); goto error; } @@ -1502,7 +1502,7 @@ static int port100_probe(struct usb_interface *interface, fw_version = port100_get_firmware_version(dev); if (!fw_version) nfc_err(&interface->dev, - "Could not get device firmware version.\n"); + "Could not get device firmware version\n"); nfc_info(&interface->dev, "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", @@ -1515,7 +1515,7 @@ static int port100_probe(struct usb_interface *interface, dev->skb_tailroom); if (!dev->nfc_digital_dev) { nfc_err(&interface->dev, - "Could not allocate nfc_digital_dev.\n"); + "Could not allocate nfc_digital_dev\n"); rc = -ENOMEM; goto error; } @@ -1526,7 +1526,7 @@ static int port100_probe(struct usb_interface *interface, rc = nfc_digital_register_device(dev->nfc_digital_dev); if (rc) { nfc_err(&interface->dev, - "Could not register digital device.\n"); + "Could not register digital device\n"); goto free_nfc_dev; } @@ -1562,7 +1562,7 @@ static void port100_disconnect(struct usb_interface *interface) kfree(dev->cmd); - nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected"); + nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n"); } static struct usb_driver port100_driver = { diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c index 24d3d240d5f42c83484925ed110f3a9dbf74ffcf..d251f7229c4e8fe5aa9da16baf09e75ed0a5b412 100644 --- a/drivers/nfc/st21nfca/st21nfca.c +++ b/drivers/nfc/st21nfca/st21nfca.c @@ -572,7 +572,7 @@ static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak) return r; } -static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, +static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid, int *len) { int r; @@ -588,7 +588,7 @@ static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, goto exit; } - gate = uid_skb->data; + memcpy(uid, uid_skb->data, uid_skb->len); *len = uid_skb->len; exit: kfree_skb(uid_skb); diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c index bd13cac9c66ac711cda7c1c3d0b8e5e3cb0b2ccd..3197e9bb66f73c53d940a3292b2140a2e7e72171 100644 --- a/drivers/nfc/st21nfca/st21nfca_se.c +++ b/drivers/nfc/st21nfca/st21nfca_se.c @@ -310,6 +310,13 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, case ST21NFCA_EVT_CONNECTIVITY: break; case ST21NFCA_EVT_TRANSACTION: + /* + * According to specification etsi 102 622 + * 11.2.2.4 EVT_TRANSACTION Table 52 + * Description Tag Length + * AID 81 5 to 16 + * PARAMETERS 82 0 to 255 + */ if (skb->len < NFC_MIN_AID_LENGTH + 2 && skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) return -EPROTO; @@ -318,8 +325,10 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, skb->len - 2, GFP_KERNEL); transaction->aid_len = skb->data[1]; - memcpy(transaction->aid, &skb->data[2], skb->data[1]); + memcpy(transaction->aid, &skb->data[2], + transaction->aid_len); + /* Check next byte is PARAMETERS tag (82) */ if (skb->data[transaction->aid_len + 2] != NFC_EVT_TRANSACTION_PARAMS_TAG) return -EPROTO; diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index eb886932d97278cfa044fb66a20dea49ffcaca6b..76a4cad41cec9b3affa4f903683a313d4620b389 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -109,7 +109,7 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) return phy->ndlc->hard_fault; r = i2c_master_send(client, skb->data, skb->len); - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + if (r < 0) { /* Retry, chip was in standby */ usleep_range(1000, 4000); r = i2c_master_send(client, skb->data, skb->len); } @@ -148,7 +148,7 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, struct i2c_client *client = phy->i2c_dev; r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + if (r < 0) { /* Retry, chip was in standby */ usleep_range(1000, 4000); r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); } @@ -313,11 +313,8 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy), GFP_KERNEL); - if (!phy) { - nfc_err(&client->dev, - "Cannot allocate memory for st21nfcb i2c phy.\n"); + if (!phy) return -ENOMEM; - } phy->i2c_dev = client; diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index 5fbf59d2138c1b0a0c723550905d37931a89bcbe..6014b585946595dc9afa1f51b5f24553d2a52e16 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -256,10 +256,9 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, struct llt_ndlc *ndlc; ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); - if (!ndlc) { - nfc_err(dev, "Cannot allocate memory for ndlc.\n"); + if (!ndlc) return -ENOMEM; - } + ndlc->ops = phy_ops; ndlc->phy_id = phy_id; ndlc->dev = dev; diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c index 7c82e9d87a65d718a92b7066e6b06c3b233e5c60..24862a525fb5044bbb3d18c8ea593e82b6f2c9bb 100644 --- a/drivers/nfc/st21nfcb/st21nfcb_se.c +++ b/drivers/nfc/st21nfcb/st21nfcb_se.c @@ -321,6 +321,12 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, break; case ST21NFCB_EVT_TRANSACTION: + /* According to specification etsi 102 622 + * 11.2.2.4 EVT_TRANSACTION Table 52 + * Description Tag Length + * AID 81 5 to 16 + * PARAMETERS 82 0 to 255 + */ if (skb->len < NFC_MIN_AID_LENGTH + 2 && skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) return -EPROTO; @@ -329,8 +335,9 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, skb->len - 2, GFP_KERNEL); transaction->aid_len = skb->data[1]; - memcpy(transaction->aid, &skb->data[2], skb->data[1]); + memcpy(transaction->aid, &skb->data[2], transaction->aid_len); + /* Check next byte is PARAMETERS tag (82) */ if (skb->data[transaction->aid_len + 2] != NFC_EVT_TRANSACTION_PARAMS_TAG) return -EPROTO; @@ -340,6 +347,7 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, transaction->aid_len + 4, transaction->params_len); r = nfc_se_transaction(ndev->nfc_dev, host, transaction); + break; default: return 1; } @@ -542,14 +550,12 @@ static int st21nfcb_hci_network_init(struct nci_dev *ndev) r = nci_hci_dev_session_init(ndev); if (r != NCI_HCI_ANY_OK) - goto exit; + goto free_dest_params; r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, NCI_NFCEE_ENABLE); if (r != NCI_STATUS_OK) - goto exit; - - return 0; + goto free_dest_params; free_dest_params: kfree(dest_params); diff --git a/include/linux/platform_data/nxp-nci.h b/include/linux/platform_data/nxp-nci.h new file mode 100644 index 0000000000000000000000000000000000000000..d6ed28679bb2a54316630b3ecf0d32d5eb693368 --- /dev/null +++ b/include/linux/platform_data/nxp-nci.h @@ -0,0 +1,27 @@ +/* + * Generic platform data for the NXP NCI NFC chips. + * + * Copyright (C) 2014 NXP Semiconductors All rights reserved. + * + * Authors: Clément Perrochaud + * + * 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. + */ + +#ifndef _NXP_NCI_H_ +#define _NXP_NCI_H_ + +struct nxp_nci_nfc_platform_data { + unsigned int gpio_en; + unsigned int gpio_fw; + unsigned int irq; +}; + +#endif /* _NXP_NCI_H_ */ diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index ab672b537dd4618dc25c31d1e10f400c96284fd2..020a814bc8ed7145f7b7491c6c21ba07b01e9c36 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -83,6 +83,10 @@ struct nfc_hci_pipe { }; #define NFC_HCI_MAX_CUSTOM_GATES 50 +/* + * According to specification 102 622 chapter 4.4 Pipes, + * the pipe identifier is 7 bits long. + */ #define NFC_HCI_MAX_PIPES 127 struct nfc_hci_init_data { u8 gate_count; diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index ff87f8611fa3246d4570c9c780d29f8ee4ff0f6c..d4dcc7199fd766aeddd2cfe51a8411d6a873581f 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -71,6 +71,7 @@ struct nci_ops { int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); int (*setup)(struct nci_dev *ndev); + int (*fw_download)(struct nci_dev *ndev, const char *firmware_name); __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol); int (*discover_se)(struct nci_dev *ndev); int (*disable_se)(struct nci_dev *ndev, u32 se_idx); @@ -137,6 +138,10 @@ struct nci_conn_info { #define NCI_HCI_INVALID_HOST 0x80 #define NCI_HCI_MAX_CUSTOM_GATES 50 +/* + * According to specification 102 622 chapter 4.4 Pipes, + * the pipe identifier is 7 bits long. + */ #define NCI_HCI_MAX_PIPES 127 struct nci_hci_gate { diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 73190e65d5c13aa7217cce910203f4bd5f63d385..7ac029c0754678d900bb1cb78dc20fee6ba859c1 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -157,7 +157,7 @@ struct nfc_evt_transaction { u32 aid_len; u8 aid[NFC_MAX_AID_LENGTH]; u8 params_len; - u8 params[NFC_MAX_PARAMS_LENGTH]; + u8 params[0]; } __packed; struct nfc_genl_data { diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 9575a1892607c5dbf18f5bc04f531ed826dfe812..49ff321060809a320966d5efc24ea6aaaab1add8 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -907,6 +907,16 @@ static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, return 0; } +static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (!ndev->ops->fw_download) + return -ENOTSUPP; + + return ndev->ops->fw_download(ndev, firmware_name); +} + static struct nfc_ops nci_nfc_ops = { .dev_up = nci_dev_up, .dev_down = nci_dev_down, @@ -922,6 +932,7 @@ static struct nfc_ops nci_nfc_ops = { .disable_se = nci_disable_se, .discover_se = nci_discover_se, .se_io = nci_se_io, + .fw_download = nci_fw_download, }; /* ---- Interface to NCI drivers ---- */