From fe0219cc505578636a6ed38d493e41ea5d599e3e Mon Sep 17 00:00:00 2001 From: Salil Kapur Date: Thu, 31 Oct 2013 23:17:46 +0530 Subject: [PATCH 01/20] NFC: mei_phy: Using kfree_skb() instead of kfree() Using kfree_skb() instead of kfree() for struct sk_buff Signed-off-by: Salil Kapur Signed-off-by: Samuel Ortiz --- drivers/nfc/mei_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 1d7860551985..11c7cbdade66 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -127,7 +127,7 @@ void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ); if (reply_size < MEI_NFC_HEADER_SIZE) { - kfree(skb); + kfree_skb(skb); return; } From 34f7e57b231ec4fd943d0d172fb7c1bb143f1da8 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Thu, 14 Nov 2013 17:07:32 -0800 Subject: [PATCH 02/20] NFC: pn544: Refactor hw_config values Some of the EEPROM configurations that are assigned by the PN544 driver are set by the firmware and should not be modified by the driver. Others are certain user mode configurations that are currently getting set to values that shouldn't necessarily be dictated by the driver. This patch changes most user and system mode configurations to the firmware defaults. Signed-off-by: Arman Uguray Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 51e21a87cd84..3df4a109cfad 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -195,42 +195,42 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9e, 0xaa}, 0x01}, - {{0x9b, 0xd1}, 0x0d}, - {{0x9b, 0xd2}, 0x24}, - {{0x9b, 0xd3}, 0x0a}, - {{0x9b, 0xd4}, 0x22}, - {{0x9b, 0xd5}, 0x08}, - {{0x9b, 0xd6}, 0x1e}, - {{0x9b, 0xdd}, 0x1c}, + {{0x9b, 0xd1}, 0x17}, + {{0x9b, 0xd2}, 0x58}, + {{0x9b, 0xd3}, 0x10}, + {{0x9b, 0xd4}, 0x47}, + {{0x9b, 0xd5}, 0x0c}, + {{0x9b, 0xd6}, 0x37}, + {{0x9b, 0xdd}, 0x33}, - {{0x9b, 0x84}, 0x13}, - {{0x99, 0x81}, 0x7f}, - {{0x99, 0x31}, 0x70}, + {{0x9b, 0x84}, 0x00}, + {{0x99, 0x81}, 0x79}, + {{0x99, 0x31}, 0x79}, {{0x98, 0x00}, 0x3f}, - {{0x9f, 0x09}, 0x00}, + {{0x9f, 0x09}, 0x02}, {{0x9f, 0x0a}, 0x05}, {{0x9e, 0xd1}, 0xa1}, - {{0x99, 0x23}, 0x00}, - - {{0x9e, 0x74}, 0x80}, + {{0x99, 0x23}, 0x01}, + {{0x9e, 0x74}, 0x00}, + {{0x9e, 0x90}, 0x00}, {{0x9f, 0x28}, 0x10}, - {{0x9f, 0x35}, 0x14}, + {{0x9f, 0x35}, 0x04}, - {{0x9f, 0x36}, 0x60}, + {{0x9f, 0x36}, 0x11}, {{0x9c, 0x31}, 0x00}, - {{0x9c, 0x32}, 0xc8}, + {{0x9c, 0x32}, 0x00}, - {{0x9c, 0x19}, 0x40}, + {{0x9c, 0x19}, 0x0a}, - {{0x9c, 0x1a}, 0x40}, + {{0x9c, 0x1a}, 0x0a}, {{0x9c, 0x0c}, 0x00}, @@ -240,13 +240,13 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9c, 0x13}, 0x00}, - {{0x98, 0xa2}, 0x0e}, + {{0x98, 0xa2}, 0x09}, - {{0x98, 0x93}, 0x40}, + {{0x98, 0x93}, 0x00}, - {{0x98, 0x7d}, 0x02}, + {{0x98, 0x7d}, 0x08}, {{0x98, 0x7e}, 0x00}, - {{0x9f, 0xc8}, 0x01}, + {{0x9f, 0xc8}, 0x00}, }; struct hw_config *p = hw_config; int count = ARRAY_SIZE(hw_config); From 249eb5bd74553da189fbff90c20c6d884a1db71e Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 13 Nov 2013 01:00:07 +0100 Subject: [PATCH 03/20] NFC: Return driver failure upon unknown event reception If the device is polling, this will trigger a netlink event to notify userspace about the polling error. Signed-off-by: Samuel Ortiz --- net/nfc/hci/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 3b9610031baa..d45b638e77c7 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -335,11 +335,8 @@ exit: kfree_skb(skb); exit_noskb: - if (r) { - /* TODO: There was an error dispatching the event, - * how to propagate up to nfc core? - */ - } + if (r) + nfc_hci_driver_failure(hdev, r); } static void nfc_hci_cmd_timeout(unsigned long data) From 43d53c29dd8548404256c05573ff557c927d214b Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:14:57 +0100 Subject: [PATCH 04/20] NFC: llcp: Fix possible memory leak while sending I frames If sending was not completed due to low memory condition msg_data was not free before returning from function. Signed-off-by: Szymon Janc Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 693cd1aad582..80057a818737 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -684,8 +684,10 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, pdu = llcp_allocate_pdu(sock, LLCP_PDU_I, frag_len + LLCP_SEQUENCE_SIZE); - if (pdu == NULL) + if (pdu == NULL) { + kfree(msg_data); return -ENOMEM; + } skb_put(pdu, LLCP_SEQUENCE_SIZE); From 11bfb1c4b94fe24a83ebeae5c3310280f9606e0e Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Sat, 30 Nov 2013 16:59:23 +0100 Subject: [PATCH 05/20] NFC: llcp: Use default MIU if none was specified on connect If MIUX is not present in CONNECT or CC use default MIU value (128) instead of one announced durring link setup. This was affecting Bluetooth handover with Android 4.3+ NCI stack. Signed-off-by: Szymon Janc Signed-off-by: Samuel Ortiz --- net/nfc/llcp_commands.c | 2 +- net/nfc/llcp_core.c | 1 - net/nfc/llcp_sock.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 80057a818737..bec6ed15f503 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -675,7 +675,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, do { remote_miu = sock->remote_miu > LLCP_MAX_MIU ? - local->remote_miu : sock->remote_miu; + LLCP_DEFAULT_MIU : sock->remote_miu; frag_len = min_t(size_t, remote_miu, remaining_len); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 1349074e1ffc..6184bd1fba3a 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -943,7 +943,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, new_sock->local = nfc_llcp_local_get(local); new_sock->rw = sock->rw; new_sock->miux = sock->miux; - new_sock->remote_miu = local->remote_miu; new_sock->nfc_protocol = sock->nfc_protocol; new_sock->dsap = ssap; new_sock->target_idx = local->target_idx; diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 69fbc8dadba7..4a53bb58a463 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -700,7 +700,6 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, llcp_sock->dev = dev; llcp_sock->local = nfc_llcp_local_get(local); - llcp_sock->remote_miu = llcp_sock->local->remote_miu; llcp_sock->ssap = nfc_llcp_get_local_ssap(local); if (llcp_sock->ssap == LLCP_SAP_MAX) { ret = -ENOMEM; From a434c2407467a76c0e1416c45f7b31cfbe1b6b3b Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 22 Dec 2013 01:00:20 +0100 Subject: [PATCH 06/20] NFC: Only warn on SE discovery error SE discovery errors are currently overwriting the dev_up() return error. This is wrong for many reasons: - We don't want to report an error if we actually brought the device up but it failed to discover SEs. By doing so we pretend we don't have an NFC functional device even we do. The only thing we could not do was checking for SEs availability. This is the false negative case. - In some cases the actual device power up failed but the SE discovery succeeded. Userspace then believes the device is up while it's not. This is the false positive case. Signed-off-by: Samuel Ortiz --- net/nfc/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/nfc/core.c b/net/nfc/core.c index 02ab34132157..c1903f439aac 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -133,11 +133,8 @@ int nfc_dev_up(struct nfc_dev *dev) dev->dev_up = true; /* We have to enable the device before discovering SEs */ - if (dev->ops->discover_se) { - rc = dev->ops->discover_se(dev); - if (rc) - pr_warn("SE discovery failed\n"); - } + if (dev->ops->discover_se && dev->ops->discover_se(dev)) + pr_err("SE discovery failed\n"); error: device_unlock(&dev->dev); From ea87a5efa9efa84cd48fbf7a969d951b32c9e5e4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 4 Jan 2014 03:23:35 +0100 Subject: [PATCH 07/20] NFC: pn533: Frame is invalid if ccid.datalen is 0 Some ACR122 firmwares seem to send 0 length data frames. Before using that length as a data index, we check that it's not 0. If it is we report the frame as being invalid. Reported-by: Arthur Taylor Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 3df19e657bc1..cf1a87bb74f8 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -521,6 +521,9 @@ static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev) if (frame->ccid.type != 0x83) return false; + if (!frame->ccid.datalen) + return false; + if (frame->data[frame->ccid.datalen - 2] == 0x63) return false; From 444fb98eed98f7292a83f9bf123d1c78f171327e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:12 +0100 Subject: [PATCH 08/20] NFC: digital: Add a note about asynchronous functions This explains how and why the timeout parameter must be handled by the driver implementation. Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 36acecd5f06c..81af21e9bcd4 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -122,6 +122,16 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev, * switch_rf to turn the radio on. A call to in|tg_configure_hw must turn * the device radio on. * @abort_cmd: Discard the last sent command. + * + * Notes: Asynchronous functions have a timeout parameter. It is the driver + * responsibility to call the digital stack back through the + * nfc_digital_cmd_complete_t callback when no RF respsonse has been + * received within the specified time (in milliseconds). In that case the + * driver must set the resp sk_buff to ERR_PTR(-ETIMEDOUT). + * Since the digital stack serializes commands to be sent, it's mandatory + * for the driver to handle the timeout correctly. Otherwise the stack + * would not be able to send new commands, waiting for the reply of the + * current one. */ struct nfc_digital_ops { int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type, From 67af1d7a0fb4b5397cb093c0fe98d5b95516c228 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:13 +0100 Subject: [PATCH 09/20] NFC: digital: Fix incorrect use of ERR_PTR and PTR_ERR macros It's bad to use these macros when not dealing with error code. this patch changes calls to these macros with correct casts. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 07bbc24fb4c7..e42ecb66e712 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -472,7 +472,7 @@ int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { - u8 rf_tech = PTR_ERR(arg); + u8 rf_tech = (unsigned long)arg; if (IS_ERR(resp)) return; @@ -508,7 +508,7 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did, ddev->skb_add_crc(skb); rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete, - ERR_PTR(rf_tech)); + (void *)(unsigned long)rf_tech); if (rc) kfree_skb(skb); From 4f319e3251937a73719b13397d738640622ba513 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:14 +0100 Subject: [PATCH 10/20] NFC: digital: Use NFC_NFCID3_MAXSIZE from nfc.h This removes the declaration of NFCID3 size in digital_dep.c and now uses the one from nfc.h. This also removes a faulty and unneeded call to max(). Reported-by: Dan Carpenter Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index e42ecb66e712..470a0b4016ed 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -32,7 +32,6 @@ #define DIGITAL_ATR_REQ_MIN_SIZE 16 #define DIGITAL_ATR_REQ_MAX_SIZE 64 -#define DIGITAL_NFCID3_LEN ((u8)8) #define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 #define DIGITAL_GB_BIT 0x02 @@ -206,10 +205,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; atr_req->cmd = DIGITAL_CMD_ATR_REQ; if (target->nfcid2_len) - memcpy(atr_req->nfcid3, target->nfcid2, - max(target->nfcid2_len, DIGITAL_NFCID3_LEN)); + memcpy(atr_req->nfcid3, target->nfcid2, NFC_NFCID2_MAXSIZE); else - get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN); + get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE); atr_req->did = 0; atr_req->bs = 0; From c36aeba8c072d20a08e8546483494ab0f6458963 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sun, 5 Jan 2014 00:08:05 +0400 Subject: [PATCH 11/20] NFC: port100: Fix device leak port100_probe() calls usb_get_dev(), but there is no usb_put_dev() in port100_disconnect(). The patch adds one. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 8a0571eb2627..a8555f81cbba 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -1509,6 +1509,7 @@ static void port100_disconnect(struct usb_interface *interface) usb_free_urb(dev->in_urb); usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); kfree(dev->cmd); From fa9be5f009e8cfc695a2cd71617fc620ab5c1c18 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 23 Dec 2013 14:15:13 -0800 Subject: [PATCH 12/20] NFC: NCI: Cancel cmd_timer in nci_close_device() nci_close_device() sends nci reset command to the device. If there is no response for this command, nci request timeout occurs first and then cmd timeout happens. Because command timer has started after sending the command. We are immediately flushing command workqueue after nci timeout. Later we will try to schedule cmd_work in command timer which leads to a crash. Cancel cmd_timer before flushing the workqueue to fix the problem. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index f0e955e3a385..0a2ee83c3bd3 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -361,6 +361,8 @@ static int nci_close_device(struct nci_dev *ndev) msecs_to_jiffies(NCI_RESET_TIMEOUT)); clear_bit(NCI_INIT, &ndev->flags); + del_timer_sync(&ndev->cmd_timer); + /* Flush cmd wq */ flush_workqueue(ndev->cmd_wq); From 1907299867431fd899ae630a29b08b878ca1e50f Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:15 -0800 Subject: [PATCH 13/20] NFC: NCI: Don't reverse local general bytes Local general bytes returned by nfc_get_local_general_bytes() are already in correct order. We don't need to reverse them. Remove local_gb[] local array as it's not needed any more. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- net/nfc/nci/core.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 0a2ee83c3bd3..6fbb7bab46e7 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -414,8 +414,6 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); struct nci_set_config_param param; - __u8 local_gb[NFC_MAX_GT_LEN]; - int i; param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); if ((param.val == NULL) || (param.len == 0)) @@ -424,11 +422,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) if (param.len > NFC_MAX_GT_LEN) return -EINVAL; - for (i = 0; i < param.len; i++) - local_gb[param.len-1-i] = param.val[i]; - param.id = NCI_PN_ATR_REQ_GEN_BYTES; - param.val = local_gb; return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); From 86e8586ed5beea15ce7c359f02a1084c2da93bc7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:17 -0800 Subject: [PATCH 14/20] NFC: NCI: Add setup handler Some drivers require special configuration while initializing. This patch adds setup handler for this custom configuration. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 6126f1f992b4..5c6fadd63483 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -68,6 +68,7 @@ struct nci_ops { int (*open)(struct nci_dev *ndev); int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); + int (*setup)(struct nci_dev *ndev); }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6fbb7bab46e7..29c1caf3e975 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -301,6 +301,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)) + ndev->ops->setup(ndev); + if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, msecs_to_jiffies(NCI_INIT_TIMEOUT)); From 22c15bf30b70ab2eae300f093ffc64e182620aba Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:18 -0800 Subject: [PATCH 15/20] NFC: NCI: Add set_config API This API can be used by drivers to send their custom configuration using SET_CONFIG NCI command to the device. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5c6fadd63483..2b93b77b210c 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -155,6 +155,7 @@ void nci_free_device(struct nci_dev *ndev); int nci_register_device(struct nci_dev *ndev); void nci_unregister_device(struct nci_dev *ndev); 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); static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, unsigned int len, diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 29c1caf3e975..46bda010bf11 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -413,6 +413,22 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) return nci_close_device(ndev); } +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) +{ + struct nci_set_config_param param; + + if (!val || !len) + return 0; + + param.id = id; + param.len = len; + param.val = val; + + return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); +} +EXPORT_SYMBOL(nci_set_config); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); From f26e30cc6b50ba81e30ca3016c29ad4b48b93eaa Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:19 -0800 Subject: [PATCH 16/20] NFC: nfcmrvl: Initial commit for Marvell NFC driver This patch adds NFC support for Marvell 8897 NFC-over-USB chipset. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/nfcmrvl/Kconfig | 23 ++ drivers/nfc/nfcmrvl/Makefile | 9 + drivers/nfc/nfcmrvl/main.c | 145 +++++++++++ drivers/nfc/nfcmrvl/nfcmrvl.h | 42 ++++ drivers/nfc/nfcmrvl/usb.c | 459 ++++++++++++++++++++++++++++++++++ 7 files changed, 680 insertions(+) create mode 100644 drivers/nfc/nfcmrvl/Kconfig create mode 100644 drivers/nfc/nfcmrvl/Makefile create mode 100644 drivers/nfc/nfcmrvl/main.c create mode 100644 drivers/nfc/nfcmrvl/nfcmrvl.h create mode 100644 drivers/nfc/nfcmrvl/usb.c diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index c1fb20603338..fe20e1cc0545 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -58,5 +58,6 @@ config NFC_PORT100 source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" +source "drivers/nfc/nfcmrvl/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index c715fe8582a8..56ab822ba03d 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_NFC_WILINK) += nfcwilink.o obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig new file mode 100644 index 000000000000..5e18afd9abe2 --- /dev/null +++ b/drivers/nfc/nfcmrvl/Kconfig @@ -0,0 +1,23 @@ +config NFC_MRVL + tristate "Marvell NFC driver support" + depends on NFC_NCI + help + The core driver to support Marvell NFC devices. + + This driver is required if you want to support + Marvell NFC device 8897. + + Say Y here to compile Marvell NFC driver into the kernel or + say M to compile it as module. + +config NFC_MRVL_USB + tristate "Marvell NFC-over-USB driver" + depends on NFC_MRVL && USB + help + Marvell NFC-over-USB driver. + + This driver provides support for Marvell NFC-over-USB devices: + 8897. + + Say Y here to compile support for Marvell NFC-over-USB driver + into the kernel or say M to compile it as module. diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile new file mode 100644 index 000000000000..97a0de72dc01 --- /dev/null +++ b/drivers/nfc/nfcmrvl/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for NFCMRVL NCI based NFC driver +# + +nfcmrvl-y += main.o +obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o + +nfcmrvl_usb-y += usb.o +obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c new file mode 100644 index 000000000000..396fc270ffc3 --- /dev/null +++ b/drivers/nfc/nfcmrvl/main.c @@ -0,0 +1,145 @@ +/* + * Marvell NFC driver: major functions + * + * Copyright (C) 2014, 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 "nfcmrvl.h" + +#define VERSION "1.0" + +static int nfcmrvl_nci_open(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + int err; + + if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + err = priv->if_ops->nci_open(priv); + + if (err) + clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags); + + return err; +} + +static int nfcmrvl_nci_close(struct nci_dev *ndev) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return 0; + + priv->if_ops->nci_close(priv); + + return 0; +} + +static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nfcmrvl_private *priv = nci_get_drvdata(ndev); + + nfc_info(priv->dev, "send entry, len %d\n", skb->len); + + skb->dev = (void *)ndev; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) + return -EBUSY; + + return priv->if_ops->nci_send(priv, skb); +} + +static struct nci_ops nfcmrvl_nci_ops = { + .open = nfcmrvl_nci_open, + .close = nfcmrvl_nci_close, + .send = nfcmrvl_nci_send, +}; + +struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, + struct nfcmrvl_if_ops *ops, + struct device *dev) +{ + struct nfcmrvl_private *priv; + int rc; + u32 protocols; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + priv->drv_data = drv_data; + priv->if_ops = ops; + priv->dev = dev; + + 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; + + priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); + if (!priv->ndev) { + nfc_err(dev, "nci_allocate_device failed"); + return ERR_PTR(-ENOMEM); + } + + nci_set_drvdata(priv->ndev, priv); + + rc = nci_register_device(priv->ndev); + if (rc) { + nfc_err(dev, "nci_register_device failed %d", rc); + nci_free_device(priv->ndev); + return ERR_PTR(rc); + } + + nfc_info(dev, "registered with nci successfully\n"); + return priv; +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); + +void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) +{ + struct nci_dev *ndev = priv->ndev; + + nci_unregister_device(ndev); + nci_free_device(ndev); + kfree(priv); +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); + +int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count) +{ + struct sk_buff *skb; + + skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, count), data, count); + nci_recv_frame(priv->ndev, skb); + + return count; +} +EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h new file mode 100644 index 000000000000..a007175f77fe --- /dev/null +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -0,0 +1,42 @@ +/** + * Marvell NFC driver + * + * Copyright (C) 2014, 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. + **/ + +/* Define private flags: */ +#define NFCMRVL_NCI_RUNNING 1 + +#define NFCMRVL_NCI_MAX_EVENT_SIZE 260 + +struct nfcmrvl_private { + struct nci_dev *ndev; + unsigned long flags; + void *drv_data; + struct device *dev; + struct nfcmrvl_if_ops *if_ops; +}; + +struct nfcmrvl_if_ops { + int (*nci_open) (struct nfcmrvl_private *priv); + int (*nci_close) (struct nfcmrvl_private *priv); + int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb); +}; + +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); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c new file mode 100644 index 000000000000..3221ca37d6c9 --- /dev/null +++ b/drivers/nfc/nfcmrvl/usb.c @@ -0,0 +1,459 @@ +/** + * Marvell NFC-over-USB driver: USB interface related functions + * + * Copyright (C) 2014, 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" + +#define VERSION "1.0" + +static struct usb_device_id nfcmrvl_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, nfcmrvl_table); + +#define NFCMRVL_USB_BULK_RUNNING 1 +#define NFCMRVL_USB_SUSPENDING 2 + +struct nfcmrvl_usb_drv_data { + struct usb_device *udev; + struct usb_interface *intf; + unsigned long flags; + struct work_struct waker; + struct usb_anchor tx_anchor; + struct usb_anchor bulk_anchor; + struct usb_anchor deferred; + int tx_in_flight; + /* protects tx_in_flight */ + spinlock_t txlock; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; + int suspend_count; + struct nfcmrvl_private *priv; +}; + +static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data) +{ + unsigned long flags; + int rv; + + spin_lock_irqsave(&drv_data->txlock, flags); + rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + if (!rv) + drv_data->tx_in_flight++; + spin_unlock_irqrestore(&drv_data->txlock, flags); + + return rv; +} + +static void nfcmrvl_bulk_complete(struct urb *urb) +{ + struct nfcmrvl_usb_drv_data *drv_data = urb->context; + int err; + + dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d", + 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"); + } + + if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) + return; + + usb_anchor_urb(urb, &drv_data->bulk_anchor); + usb_mark_last_busy(drv_data->udev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected + */ + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p failed to resubmit (%d)", urb, -err); + usb_unanchor_urb(urb); + } +} + +static int +nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) +{ + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE; + + if (!drv_data->bulk_rx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, mem_flags); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(drv_data->udev, + drv_data->bulk_rx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size, + nfcmrvl_bulk_complete, drv_data); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_mark_last_busy(drv_data->udev); + usb_anchor_urb(urb, &drv_data->bulk_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)", urb, -err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void nfcmrvl_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct nci_dev *ndev = (struct nci_dev *)skb->dev; + 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", + urb, urb->status, urb->actual_length); + + spin_lock(&drv_data->txlock); + drv_data->tx_in_flight--; + spin_unlock(&drv_data->txlock); + + kfree(urb->setup_packet); + kfree_skb(skb); +} + +static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return err; + + drv_data->intf->needs_remote_wakeup = 1; + + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + if (err) + goto failed; + + set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL); + + usb_autopm_put_interface(drv_data->intf); + return 0; + +failed: + usb_autopm_put_interface(drv_data->intf); + return err; +} + +static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data) +{ + usb_kill_anchored_urbs(&drv_data->bulk_anchor); +} + +static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + int err; + + cancel_work_sync(&drv_data->waker); + + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + err = usb_autopm_get_interface(drv_data->intf); + if (err) + goto failed; + + drv_data->intf->needs_remote_wakeup = 0; + usb_autopm_put_interface(drv_data->intf); + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); + return 0; +} + +static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, + struct sk_buff *skb) +{ + struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; + struct urb *urb; + unsigned int pipe; + int err; + + if (!drv_data->bulk_tx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + pipe = usb_sndbulkpipe(drv_data->udev, + drv_data->bulk_tx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len, + nfcmrvl_tx_complete, skb); + + err = nfcmrvl_inc_tx(drv_data); + if (err) { + usb_anchor_urb(urb, &drv_data->deferred); + schedule_work(&drv_data->waker); + err = 0; + goto done; + } + + usb_anchor_urb(urb, &drv_data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + if (err != -EPERM && err != -ENODEV) + nfc_err(&drv_data->udev->dev, + "urb %p submission failed (%d)", urb, -err); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } else { + usb_mark_last_busy(drv_data->udev); + } + +done: + usb_free_urb(urb); + return err; +} + +static struct nfcmrvl_if_ops usb_ops = { + .nci_open = nfcmrvl_usb_nci_open, + .nci_close = nfcmrvl_usb_nci_close, + .nci_send = nfcmrvl_usb_nci_send, +}; + +static void nfcmrvl_waker(struct work_struct *work) +{ + struct nfcmrvl_usb_drv_data *drv_data = + container_of(work, struct nfcmrvl_usb_drv_data, waker); + int err; + + err = usb_autopm_get_interface(drv_data->intf); + if (err) + return; + + usb_autopm_put_interface(drv_data->intf); +} + +static int nfcmrvl_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_desc; + struct nfcmrvl_usb_drv_data *drv_data; + struct nfcmrvl_private *priv; + int i; + struct usb_device *udev = interface_to_usbdev(intf); + + nfc_info(&udev->dev, "intf %p id %p", intf, id); + + drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + + if (!drv_data->bulk_tx_ep && + usb_endpoint_is_bulk_out(ep_desc)) { + drv_data->bulk_tx_ep = ep_desc; + continue; + } + + if (!drv_data->bulk_rx_ep && + usb_endpoint_is_bulk_in(ep_desc)) { + drv_data->bulk_rx_ep = ep_desc; + continue; + } + } + + if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep) + return -ENODEV; + + drv_data->udev = udev; + drv_data->intf = intf; + + INIT_WORK(&drv_data->waker, nfcmrvl_waker); + spin_lock_init(&drv_data->txlock); + + init_usb_anchor(&drv_data->tx_anchor); + init_usb_anchor(&drv_data->bulk_anchor); + init_usb_anchor(&drv_data->deferred); + + priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops, + &drv_data->udev->dev); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + drv_data->priv = priv; + priv->dev = &drv_data->udev->dev; + + usb_set_intfdata(intf, drv_data); + + return 0; +} + +static void nfcmrvl_disconnect(struct usb_interface *intf) +{ + struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); + + if (!drv_data) + return; + + nfc_info(&drv_data->udev->dev, "intf %p", intf); + + nfcmrvl_nci_unregister_dev(drv_data->priv); + + usb_set_intfdata(drv_data->intf, NULL); +} + +#ifdef CONFIG_PM +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); + + if (drv_data->suspend_count++) + return 0; + + spin_lock_irq(&drv_data->txlock); + if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) { + set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + } else { + spin_unlock_irq(&drv_data->txlock); + drv_data->suspend_count--; + return -EBUSY; + } + + nfcmrvl_usb_stop_traffic(drv_data); + usb_kill_anchored_urbs(&drv_data->tx_anchor); + + return 0; +} + +static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data) +{ + struct urb *urb; + int err; + + while ((urb = usb_get_from_anchor(&drv_data->deferred))) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) + break; + + drv_data->tx_in_flight++; + } + usb_scuttle_anchored_urbs(&drv_data->deferred); +} + +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); + + if (--drv_data->suspend_count) + return 0; + + if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags)) + goto done; + + if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) { + err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + if (err) { + clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags); + goto failed; + } + + nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO); + } + + spin_lock_irq(&drv_data->txlock); + nfcmrvl_play_deferred(drv_data); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return 0; + +failed: + usb_scuttle_anchored_urbs(&drv_data->deferred); +done: + spin_lock_irq(&drv_data->txlock); + clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags); + spin_unlock_irq(&drv_data->txlock); + + return err; +} +#endif + +static struct usb_driver nfcmrvl_usb_driver = { + .name = "nfcmrvl", + .probe = nfcmrvl_probe, + .disconnect = nfcmrvl_disconnect, +#ifdef CONFIG_PM + .suspend = nfcmrvl_suspend, + .resume = nfcmrvl_resume, + .reset_resume = nfcmrvl_resume, +#endif + .id_table = nfcmrvl_table, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, + .soft_unbind = 1, +}; +module_usb_driver(nfcmrvl_usb_driver); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); From 15203b4c79aaeb443f89ac3190eed3447bf2178b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:20 -0800 Subject: [PATCH 17/20] NFC: nfcmrvl: Add setup handler Marvell nfc device provides support for external coexistance control. It allows Device Host to inhibit the NFCC from polling when required by asserting a GPIO pin. A second pin allows the DH to have feedback on the current NFCC state. The required configuration for this feature is done in setup handler. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 15 +++++++++++++++ drivers/nfc/nfcmrvl/nfcmrvl.h | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 396fc270ffc3..5f91d4571429 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -66,10 +66,25 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) return priv->if_ops->nci_send(priv, skb); } +static int nfcmrvl_nci_setup(struct nci_dev *ndev) +{ + __u8 val; + + val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED; + nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val); + val = NFCMRVL_GPIO_PIN_NFC_ACTIVE; + nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val); + val = NFCMRVL_EXT_COEX_ENABLE; + nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val); + + return 0; +} + static struct nci_ops nfcmrvl_nci_ops = { .open = nfcmrvl_nci_open, .close = nfcmrvl_nci_close, .send = nfcmrvl_nci_send, + .setup = nfcmrvl_nci_setup, }; struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index a007175f77fe..54c4a956bd45 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -19,6 +19,12 @@ /* Define private flags: */ #define NFCMRVL_NCI_RUNNING 1 +#define NFCMRVL_EXT_COEX_ID 0xE0 +#define NFCMRVL_NOT_ALLOWED_ID 0xE1 +#define NFCMRVL_ACTIVE_ID 0xE2 +#define NFCMRVL_EXT_COEX_ENABLE 1 +#define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED 0xA +#define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB #define NFCMRVL_NCI_MAX_EVENT_SIZE 260 struct nfcmrvl_private { From 48e1044515967a0d88ee076045b2141535557d8e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:37 +0100 Subject: [PATCH 18/20] NFC: digital: Set current target active on activate_target() call The curr_protocol field of nfc_digital_dev structure used to determine if a target is currently active was set too soon, immediately when a target is found. This is not good since there is no other way than deactivate_target() to reset curr_protocol and if activate_target() is not called, the target remains active and it's not possible to put the device in poll mode anymore. With this patch curr_protocol is set when nfc core activates a target, puts a device up, or when an ATR_REQ is received in target mode. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_core.c | 28 ++++++++++++++++++++++++++-- net/nfc/digital_dep.c | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 09fc95439955..c129d1571ca6 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -339,7 +339,6 @@ int digital_target_found(struct nfc_digital_dev *ddev, pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol); ddev->curr_rf_tech = rf_tech; - ddev->curr_protocol = protocol; if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { ddev->skb_add_crc = digital_skb_add_crc_none; @@ -541,8 +540,14 @@ static int digital_dep_link_up(struct nfc_dev *nfc_dev, __u8 comm_mode, __u8 *gb, size_t gb_len) { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + int rc; - return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); + rc = digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); + + if (!rc) + ddev->curr_protocol = NFC_PROTO_NFC_DEP; + + return rc; } static int digital_dep_link_down(struct nfc_dev *nfc_dev) @@ -557,6 +562,20 @@ static int digital_dep_link_down(struct nfc_dev *nfc_dev) static int digital_activate_target(struct nfc_dev *nfc_dev, struct nfc_target *target, __u32 protocol) { + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + if (ddev->poll_tech_count) { + pr_err("Can't activate a target while polling\n"); + return -EBUSY; + } + + if (ddev->curr_protocol) { + pr_err("A target is already active\n"); + return -EBUSY; + } + + ddev->curr_protocol = protocol; + return 0; } @@ -565,6 +584,11 @@ static void digital_deactivate_target(struct nfc_dev *nfc_dev, { struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + if (!ddev->curr_protocol) { + pr_err("No active target\n"); + return; + } + ddev->curr_protocol = 0; } diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 470a0b4016ed..562bec9ad8d9 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -681,6 +681,8 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, ddev->skb_check_crc = digital_skb_check_crc_none; } + ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK; + rc = ddev->skb_check_crc(resp); if (rc) { PROTOCOL_ERR("14.4.1.6"); From b711ad524bf5a6a078c4d0a1a44ca1db204802f6 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 6 Jan 2014 23:34:48 +0100 Subject: [PATCH 19/20] NFC: digital: Set rf tech and crc functions when receiving a PSL_REQ This patch sets the correct rf tech value and crc functions in target mode when receiving a PSL_REQ, as done when receiving an ATR_REQ. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/digital_dep.c | 44 ++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index 562bec9ad8d9..43e450f78d0a 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -380,6 +380,33 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev, data_exch); } +static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + ddev->curr_rf_tech = rf_tech; + + ddev->skb_add_crc = digital_skb_add_crc_none; + ddev->skb_check_crc = digital_skb_check_crc_none; + + if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) + return; + + switch (ddev->curr_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + ddev->skb_add_crc = digital_skb_add_crc_a; + ddev->skb_check_crc = digital_skb_check_crc_a; + break; + + case NFC_DIGITAL_RF_TECH_212F: + case NFC_DIGITAL_RF_TECH_424F: + ddev->skb_add_crc = digital_skb_add_crc_f; + ddev->skb_check_crc = digital_skb_check_crc_f; + break; + + default: + break; + } +} + static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -475,6 +502,8 @@ static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, if (IS_ERR(resp)) return; + digital_tg_set_rf_tech(ddev, rf_tech); + digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL); @@ -659,16 +688,10 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) { min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2; - - ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A; - ddev->skb_add_crc = digital_skb_add_crc_a; - ddev->skb_check_crc = digital_skb_check_crc_a; + digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_106A); } else { min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1; - - ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F; - ddev->skb_add_crc = digital_skb_add_crc_f; - ddev->skb_check_crc = digital_skb_check_crc_f; + digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_212F); } if (resp->len < min_size) { @@ -676,11 +699,6 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, goto exit; } - if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) { - ddev->skb_add_crc = digital_skb_add_crc_none; - ddev->skb_check_crc = digital_skb_check_crc_none; - } - ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK; rc = ddev->skb_check_crc(resp); From bb55dc2ae4367b8f711d43a2f8668a6ed42c4fd3 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 8 Jan 2014 10:52:27 -0800 Subject: [PATCH 20/20] NFC: nfcmrvl: Fix possible memory leak issue This patch fixes memory leaks in the error paths of nfcmrvl_nci_register_dev() routine. Reported-by: Dan Carpenter Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 5f91d4571429..85e8bcf98693 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -112,7 +112,8 @@ 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"); - return ERR_PTR(-ENOMEM); + rc = -ENOMEM; + goto error; } nci_set_drvdata(priv->ndev, priv); @@ -121,11 +122,15 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, if (rc) { nfc_err(dev, "nci_register_device failed %d", rc); nci_free_device(priv->ndev); - return ERR_PTR(rc); + goto error; } nfc_info(dev, "registered with nci successfully\n"); return priv; + +error: + kfree(priv); + return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);