NFC: Backport upstream latest NFC code
authorArron Wang <arron.wang@intel.com>
Thu, 22 Nov 2012 07:07:52 +0000 (15:07 +0800)
committerArron Wang <arron.wang@intel.com>
Fri, 30 Nov 2012 01:44:34 +0000 (09:44 +0800)
Change-Id: Icdf455992d8fb88596038a6b6ac1823436480a06
Signed-off-by: Arron Wang <arron.wang@intel.com>
37 files changed:
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/Makefile [new file with mode: 0644]
drivers/nfc/pn544/i2c.c [new file with mode: 0644]
drivers/nfc/pn544/pn544.c [moved from drivers/nfc/pn544_hci.c with 51% similarity]
drivers/nfc/pn544/pn544.h [new file with mode: 0644]
include/linux/Kbuild
include/linux/nfc.h
include/linux/skbuff.h
include/linux/socket.h
include/net/nfc/hci.h
include/net/nfc/llc.h [new file with mode: 0644]
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
net/Makefile
net/core/skbuff.c
net/nfc/core.c
net/nfc/hci/Makefile
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/hci/hci.h
net/nfc/hci/hcp.c
net/nfc/hci/llc.c [new file with mode: 0644]
net/nfc/hci/llc.h [new file with mode: 0644]
net/nfc/hci/llc_nop.c [new file with mode: 0644]
net/nfc/hci/llc_shdlc.c [new file with mode: 0644]
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/nci/core.c
net/nfc/nci/ntf.c
net/nfc/nci/rsp.c
net/nfc/netlink.c

index 3b20b73..ec85767 100644 (file)
@@ -5,21 +5,9 @@
 menu "Near Field Communication (NFC) devices"
        depends on NFC
 
-config PN544_NFC
-       tristate "PN544 NFC driver"
-       depends on I2C
-       select CRC_CCITT
-       default n
-       ---help---
-         Say yes if you want PN544 Near Field Communication driver.
-         This is for i2c connected version. If unsure, say N here.
-
-         To compile this driver as a module, choose m here. The module will
-         be called pn544.
-
 config PN544_HCI_NFC
        tristate "HCI PN544 NFC driver"
-       depends on I2C && NFC_SHDLC
+       depends on I2C && NFC_HCI && NFC_SHDLC
        select CRC_CCITT
        default n
        ---help---
index 473e44c..36c3590 100644 (file)
@@ -2,8 +2,7 @@
 # Makefile for nfc devices
 #
 
-obj-$(CONFIG_PN544_NFC)                += pn544.o
-obj-$(CONFIG_PN544_HCI_NFC)    += pn544_hci.o
+obj-$(CONFIG_PN544_HCI_NFC)    += pn544/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 
index e7fd493..50b1ee4 100644 (file)
@@ -352,8 +352,6 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
        struct nfcwilink *drv = priv_data;
        int rc;
 
-       nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
-
        if (!skb)
                return -EFAULT;
 
@@ -362,6 +360,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return -EFAULT;
        }
 
+       nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len);
+
        /* strip the ST header
        (apart for the chnl byte, which is not received in the hdr) */
        skb_pull(skb, (NFCWILINK_HDR_LEN-1));
@@ -604,21 +604,7 @@ static struct platform_driver nfcwilink_driver = {
        },
 };
 
-/* ------- Module Init/Exit interfaces ------ */
-static int __init nfcwilink_init(void)
-{
-       printk(KERN_INFO "NFC Driver for TI WiLink");
-
-       return platform_driver_register(&nfcwilink_driver);
-}
-
-static void __exit nfcwilink_exit(void)
-{
-       platform_driver_unregister(&nfcwilink_driver);
-}
-
-module_init(nfcwilink_init);
-module_exit(nfcwilink_exit);
+module_platform_driver(nfcwilink_driver);
 
 /* ------ Module Info ------ */
 
index d606f52..97c440a 100644 (file)
@@ -356,6 +356,7 @@ struct pn533 {
 
        struct workqueue_struct *wq;
        struct work_struct cmd_work;
+       struct work_struct cmd_complete_work;
        struct work_struct poll_work;
        struct work_struct mi_work;
        struct work_struct tg_work;
@@ -383,6 +384,19 @@ struct pn533 {
        u8 tgt_mode;
 
        u32 device_type;
+
+       struct list_head cmd_queue;
+       u8 cmd_pending;
+};
+
+struct pn533_cmd {
+       struct list_head queue;
+       struct pn533_frame *out_frame;
+       struct pn533_frame *in_frame;
+       int in_frame_len;
+       pn533_cmd_complete_t cmd_complete;
+       void *arg;
+       gfp_t flags;
 };
 
 struct pn533_frame {
@@ -487,7 +501,7 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd)
 
 static void pn533_wq_cmd_complete(struct work_struct *work)
 {
-       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+       struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work);
        struct pn533_frame *in_frame;
        int rc;
 
@@ -502,7 +516,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work)
                                        PN533_FRAME_CMD_PARAMS_LEN(in_frame));
 
        if (rc != -EINPROGRESS)
-               mutex_unlock(&dev->cmd_lock);
+               queue_work(dev->wq, &dev->cmd_work);
 }
 
 static void pn533_recv_response(struct urb *urb)
@@ -550,7 +564,7 @@ static void pn533_recv_response(struct urb *urb)
        dev->wq_in_frame = in_frame;
 
 sched_wq:
-       queue_work(dev->wq, &dev->cmd_work);
+       queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
 static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags)
@@ -606,7 +620,7 @@ static void pn533_recv_ack(struct urb *urb)
 
 sched_wq:
        dev->wq_in_frame = NULL;
-       queue_work(dev->wq, &dev->cmd_work);
+       queue_work(dev->wq, &dev->cmd_complete_work);
 }
 
 static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags)
@@ -669,6 +683,31 @@ error:
        return rc;
 }
 
+static void pn533_wq_cmd(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, cmd_work);
+       struct pn533_cmd *cmd;
+
+       mutex_lock(&dev->cmd_lock);
+
+       if (list_empty(&dev->cmd_queue)) {
+               dev->cmd_pending = 0;
+               mutex_unlock(&dev->cmd_lock);
+               return;
+       }
+
+       cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue);
+
+       mutex_unlock(&dev->cmd_lock);
+
+       __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame,
+                                    cmd->in_frame_len, cmd->cmd_complete,
+                                    cmd->arg, cmd->flags);
+
+       list_del(&cmd->queue);
+       kfree(cmd);
+}
+
 static int pn533_send_cmd_frame_async(struct pn533 *dev,
                                        struct pn533_frame *out_frame,
                                        struct pn533_frame *in_frame,
@@ -676,21 +715,44 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev,
                                        pn533_cmd_complete_t cmd_complete,
                                        void *arg, gfp_t flags)
 {
-       int rc;
+       struct pn533_cmd *cmd;
+       int rc = 0;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (!mutex_trylock(&dev->cmd_lock))
-               return -EBUSY;
+       mutex_lock(&dev->cmd_lock);
 
-       rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
-                                       in_frame_len, cmd_complete, arg, flags);
-       if (rc)
-               goto error;
+       if (!dev->cmd_pending) {
+               rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
+                                                 in_frame_len, cmd_complete,
+                                                 arg, flags);
+               if (!rc)
+                       dev->cmd_pending = 1;
 
-       return 0;
-error:
+               goto unlock;
+       }
+
+       nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__);
+
+       cmd = kzalloc(sizeof(struct pn533_cmd), flags);
+       if (!cmd) {
+               rc = -ENOMEM;
+               goto unlock;
+       }
+
+       INIT_LIST_HEAD(&cmd->queue);
+       cmd->out_frame = out_frame;
+       cmd->in_frame = in_frame;
+       cmd->in_frame_len = in_frame_len;
+       cmd->cmd_complete = cmd_complete;
+       cmd->arg = arg;
+       cmd->flags = flags;
+
+       list_add_tail(&cmd->queue, &dev->cmd_queue);
+
+unlock:
        mutex_unlock(&dev->cmd_lock);
+
        return rc;
 }
 
@@ -1305,8 +1367,6 @@ static void pn533_listen_mode_timer(unsigned long data)
 
        dev->cancel_listen = 1;
 
-       mutex_unlock(&dev->cmd_lock);
-
        pn533_poll_next_mod(dev);
 
        queue_work(dev->wq, &dev->poll_work);
@@ -2131,7 +2191,7 @@ error_cmd:
 
        kfree(arg);
 
-       mutex_unlock(&dev->cmd_lock);
+       queue_work(dev->wq, &dev->cmd_work);
 }
 
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
@@ -2330,13 +2390,12 @@ static int pn533_probe(struct usb_interface *interface,
                        NULL, 0,
                        pn533_send_complete, dev);
 
-       INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
+       INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
+       INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
        INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
        INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
        INIT_WORK(&dev->poll_work, pn533_wq_poll);
-       dev->wq = alloc_workqueue("pn533",
-                                 WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                                 1);
+       dev->wq = alloc_ordered_workqueue("pn533", 0);
        if (dev->wq == NULL)
                goto error;
 
@@ -2346,6 +2405,8 @@ static int pn533_probe(struct usb_interface *interface,
 
        skb_queue_head_init(&dev->resp_q);
 
+       INIT_LIST_HEAD(&dev->cmd_queue);
+
        usb_set_intfdata(interface, dev);
 
        pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION);
@@ -2417,6 +2478,7 @@ error:
 static void pn533_disconnect(struct usb_interface *interface)
 {
        struct pn533 *dev;
+       struct pn533_cmd *cmd, *n;
 
        dev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);
@@ -2433,6 +2495,11 @@ static void pn533_disconnect(struct usb_interface *interface)
 
        del_timer(&dev->listen_timer);
 
+       list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) {
+               list_del(&cmd->queue);
+               kfree(cmd);
+       }
+
        kfree(dev->in_frame);
        usb_free_urb(dev->in_urb);
        kfree(dev->out_frame);
diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile
new file mode 100644 (file)
index 0000000..7257338
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for PN544 HCI based NFC driver
+#
+
+obj-$(CONFIG_PN544_HCI_NFC)    += pn544_i2c.o
+
+pn544_i2c-y            := pn544.o i2c.o
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
new file mode 100644 (file)
index 0000000..fb430d8
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * I2C Link Layer for PN544 HCI based 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/nfc/pn544.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "pn544.h"
+
+#define PN544_I2C_FRAME_HEADROOM 1
+#define PN544_I2C_FRAME_TAILROOM 2
+
+/* framing in HCI mode */
+#define PN544_HCI_I2C_LLC_LEN          1
+#define PN544_HCI_I2C_LLC_CRC          2
+#define PN544_HCI_I2C_LLC_LEN_CRC      (PN544_HCI_I2C_LLC_LEN + \
+                                        PN544_HCI_I2C_LLC_CRC)
+#define PN544_HCI_I2C_LLC_MIN_SIZE     (1 + PN544_HCI_I2C_LLC_LEN_CRC)
+#define PN544_HCI_I2C_LLC_MAX_PAYLOAD  29
+#define PN544_HCI_I2C_LLC_MAX_SIZE     (PN544_HCI_I2C_LLC_LEN_CRC + 1 + \
+                                        PN544_HCI_I2C_LLC_MAX_PAYLOAD)
+
+static struct i2c_device_id pn544_hci_i2c_id_table[] = {
+       {"pn544", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
+
+#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
+
+struct pn544_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct nfc_hci_dev *hdev;
+
+       unsigned int gpio_en;
+       unsigned int gpio_irq;
+       unsigned int gpio_fw;
+       unsigned int en_polarity;
+
+       int powered;
+
+       int hard_fault;         /*
+                                * < 0 if hardware error occured (e.g. i2c err)
+                                * and prevents normal operation.
+                                */
+};
+
+#define I2C_DUMP_SKB(info, skb)                                        \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
+{
+       int polarity, retry, ret;
+       char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
+       int count = sizeof(rset_cmd);
+
+       pr_info(DRIVER_DESC ": %s\n", __func__);
+       dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
+
+       /* Disable fw download */
+       gpio_set_value(phy->gpio_fw, 0);
+
+       for (polarity = 0; polarity < 2; polarity++) {
+               phy->en_polarity = polarity;
+               retry = 3;
+               while (retry--) {
+                       /* power off */
+                       gpio_set_value(phy->gpio_en, !phy->en_polarity);
+                       usleep_range(10000, 15000);
+
+                       /* power on */
+                       gpio_set_value(phy->gpio_en, phy->en_polarity);
+                       usleep_range(10000, 15000);
+
+                       /* send reset */
+                       dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n");
+                       ret = i2c_master_send(phy->i2c_dev, rset_cmd, count);
+                       if (ret == count) {
+                               dev_info(&phy->i2c_dev->dev,
+                                        "nfc_en polarity : active %s\n",
+                                        (polarity == 0 ? "low" : "high"));
+                               goto out;
+                       }
+               }
+       }
+
+       dev_err(&phy->i2c_dev->dev,
+               "Could not detect nfc_en polarity, fallback to active high\n");
+
+out:
+       gpio_set_value(phy->gpio_en, !phy->en_polarity);
+}
+
+static int pn544_hci_i2c_enable(void *phy_id)
+{
+       struct pn544_i2c_phy *phy = phy_id;
+
+       pr_info(DRIVER_DESC ": %s\n", __func__);
+
+       gpio_set_value(phy->gpio_fw, 0);
+       gpio_set_value(phy->gpio_en, phy->en_polarity);
+       usleep_range(10000, 15000);
+
+       phy->powered = 1;
+
+       return 0;
+}
+
+static void pn544_hci_i2c_disable(void *phy_id)
+{
+       struct pn544_i2c_phy *phy = phy_id;
+
+       pr_info(DRIVER_DESC ": %s\n", __func__);
+
+       gpio_set_value(phy->gpio_fw, 0);
+       gpio_set_value(phy->gpio_en, !phy->en_polarity);
+       usleep_range(10000, 15000);
+
+       gpio_set_value(phy->gpio_en, phy->en_polarity);
+       usleep_range(10000, 15000);
+
+       gpio_set_value(phy->gpio_en, !phy->en_polarity);
+       usleep_range(10000, 15000);
+
+       phy->powered = 0;
+}
+
+static void pn544_hci_i2c_add_len_crc(struct sk_buff *skb)
+{
+       u16 crc;
+       int len;
+
+       len = skb->len + 2;
+       *skb_push(skb, 1) = len;
+
+       crc = crc_ccitt(0xffff, skb->data, skb->len);
+       crc = ~crc;
+       *skb_put(skb, 1) = crc & 0xff;
+       *skb_put(skb, 1) = crc >> 8;
+}
+
+static void pn544_hci_i2c_remove_len_crc(struct sk_buff *skb)
+{
+       skb_pull(skb, PN544_I2C_FRAME_HEADROOM);
+       skb_trim(skb, PN544_I2C_FRAME_TAILROOM);
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int pn544_hci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       int r;
+       struct pn544_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       usleep_range(3000, 6000);
+
+       pn544_hci_i2c_add_len_crc(skb);
+
+       I2C_DUMP_SKB("i2c frame written", skb);
+
+       r = i2c_master_send(client, skb->data, skb->len);
+
+       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+               usleep_range(6000, 10000);
+               r = i2c_master_send(client, skb->data, skb->len);
+       }
+
+       if (r >= 0) {
+               if (r != skb->len)
+                       r = -EREMOTEIO;
+               else
+                       r = 0;
+       }
+
+       pn544_hci_i2c_remove_len_crc(skb);
+
+       return r;
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+       int len;
+       u16 crc;
+
+       len = buf[0] + 1;
+       crc = crc_ccitt(0xffff, buf, len - 2);
+       crc = ~crc;
+
+       if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
+               pr_err(PN544_HCI_I2C_DRIVER_NAME
+                      ": CRC error 0x%x != 0x%x 0x%x\n",
+                      crc, buf[len - 1], buf[len - 2]);
+
+               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+                              16, 2, buf, buflen, false);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*
+ * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
+ * that i2c bus will be flushed and that next read will start on a new frame.
+ * returned skb contains only LLC header and payload.
+ * returns:
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * -ENOMEM : cannot allocate skb, frame dropped
+ */
+static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb)
+{
+       int r;
+       u8 len;
+       u8 tmp[PN544_HCI_I2C_LLC_MAX_SIZE - 1];
+       struct i2c_client *client = phy->i2c_dev;
+
+       r = i2c_master_recv(client, &len, 1);
+       if (r != 1) {
+               dev_err(&client->dev, "cannot read len byte\n");
+               return -EREMOTEIO;
+       }
+
+       if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) ||
+           (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) {
+               dev_err(&client->dev, "invalid len byte\n");
+               r = -EBADMSG;
+               goto flush;
+       }
+
+       *skb = alloc_skb(1 + len, GFP_KERNEL);
+       if (*skb == NULL) {
+               r = -ENOMEM;
+               goto flush;
+       }
+
+       *skb_put(*skb, 1) = len;
+
+       r = i2c_master_recv(client, skb_put(*skb, len), len);
+       if (r != len) {
+               kfree_skb(*skb);
+               return -EREMOTEIO;
+       }
+
+       I2C_DUMP_SKB("i2c frame read", *skb);
+
+       r = check_crc((*skb)->data, (*skb)->len);
+       if (r != 0) {
+               kfree_skb(*skb);
+               r = -EBADMSG;
+               goto flush;
+       }
+
+       skb_pull(*skb, 1);
+       skb_trim(*skb, (*skb)->len - 2);
+
+       usleep_range(3000, 6000);
+
+       return 0;
+
+flush:
+       if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
+               r = -EREMOTEIO;
+
+       usleep_range(3000, 6000);
+
+       return r;
+}
+
+/*
+ * Reads an shdlc frame from the chip. This is not as straightforward as it
+ * seems. There are cases where we could loose the frame start synchronization.
+ * The frame format is len-data-crc, and corruption can occur anywhere while
+ * transiting on i2c bus, such that we could read an invalid len.
+ * In order to recover synchronization with the next frame, we must be sure
+ * to read the real amount of data without using the len byte. We do this by
+ * assuming the following:
+ * - the chip will always present only one single complete frame on the bus
+ *   before triggering the interrupt
+ * - the chip will not present a new frame until we have completely read
+ *   the previous one (or until we have handled the interrupt).
+ * The tricky case is when we read a corrupted len that is less than the real
+ * len. We must detect this here in order to determine that we need to flush
+ * the bus. This is the reason why we check the crc here.
+ */
+static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+       struct pn544_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+       struct sk_buff *skb = NULL;
+       int r;
+
+       if (!phy || irq != phy->i2c_dev->irq) {
+               WARN_ON_ONCE(1);
+               return IRQ_NONE;
+       }
+
+       client = phy->i2c_dev;
+       dev_dbg(&client->dev, "IRQ\n");
+
+       if (phy->hard_fault != 0)
+               return IRQ_HANDLED;
+
+       r = pn544_hci_i2c_read(phy, &skb);
+       if (r == -EREMOTEIO) {
+               phy->hard_fault = r;
+
+               nfc_hci_recv_frame(phy->hdev, NULL);
+
+               return IRQ_HANDLED;
+       } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+               return IRQ_HANDLED;
+       }
+
+       nfc_hci_recv_frame(phy->hdev, skb);
+
+       return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+       .write = pn544_hci_i2c_write,
+       .enable = pn544_hci_i2c_enable,
+       .disable = pn544_hci_i2c_disable,
+};
+
+static int __devinit pn544_hci_i2c_probe(struct i2c_client *client,
+                                    const struct i2c_device_id *id)
+{
+       struct pn544_i2c_phy *phy;
+       struct pn544_nfc_platform_data *pdata;
+       int r = 0;
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+       dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               return -ENODEV;
+       }
+
+       phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(&client->dev,
+                       "Cannot allocate memory for pn544 i2c phy.\n");
+               r = -ENOMEM;
+               goto err_phy_alloc;
+       }
+
+       phy->i2c_dev = client;
+       i2c_set_clientdata(client, phy);
+
+       pdata = client->dev.platform_data;
+       if (pdata == NULL) {
+               dev_err(&client->dev, "No platform data\n");
+               r = -EINVAL;
+               goto err_pdata;
+       }
+
+       if (pdata->request_resources == NULL) {
+               dev_err(&client->dev, "request_resources() missing\n");
+               r = -EINVAL;
+               goto err_pdata;
+       }
+
+       r = pdata->request_resources(client);
+       if (r) {
+               dev_err(&client->dev, "Cannot get platform resources\n");
+               goto err_pdata;
+       }
+
+       phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
+       phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
+       phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
+
+       pn544_hci_i2c_platform_init(phy);
+
+       r = request_threaded_irq(client->irq, NULL, pn544_hci_i2c_irq_thread_fn,
+                                IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                PN544_HCI_I2C_DRIVER_NAME, phy);
+       if (r < 0) {
+               dev_err(&client->dev, "Unable to register IRQ handler\n");
+               goto err_rti;
+       }
+
+       r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+                           PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM,
+                           PN544_HCI_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
+       if (r < 0)
+               goto err_hci;
+
+       return 0;
+
+err_hci:
+       free_irq(client->irq, phy);
+
+err_rti:
+       if (pdata->free_resources != NULL)
+               pdata->free_resources();
+
+err_pdata:
+       kfree(phy);
+
+err_phy_alloc:
+       return r;
+}
+
+static __devexit int pn544_hci_i2c_remove(struct i2c_client *client)
+{
+       struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+       struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       pn544_hci_remove(phy->hdev);
+
+       if (phy->powered)
+               pn544_hci_i2c_disable(phy);
+
+       free_irq(client->irq, phy);
+       if (pdata->free_resources)
+               pdata->free_resources();
+
+       kfree(phy);
+
+       return 0;
+}
+
+static struct i2c_driver pn544_hci_i2c_driver = {
+       .driver = {
+                  .name = PN544_HCI_I2C_DRIVER_NAME,
+                 },
+       .probe = pn544_hci_i2c_probe,
+       .id_table = pn544_hci_i2c_id_table,
+       .remove = __devexit_p(pn544_hci_i2c_remove),
+};
+
+static int __init pn544_hci_i2c_init(void)
+{
+       int r;
+
+       pr_debug(DRIVER_DESC ": %s\n", __func__);
+
+       r = i2c_add_driver(&pn544_hci_i2c_driver);
+       if (r) {
+               pr_err(PN544_HCI_I2C_DRIVER_NAME
+                      ": driver registration failed\n");
+               return r;
+       }
+
+       return 0;
+}
+
+static void __exit pn544_hci_i2c_exit(void)
+{
+       i2c_del_driver(&pn544_hci_i2c_driver);
+}
+
+module_init(pn544_hci_i2c_init);
+module_exit(pn544_hci_i2c_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
similarity index 51%
rename from drivers/nfc/pn544_hci.c
rename to drivers/nfc/pn544/pn544.c
index aa71807..cc666de 100644 (file)
  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include <linux/crc-ccitt.h>
-#include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-#include <linux/miscdevice.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
 
 #include <linux/nfc.h>
 #include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
+#include <net/nfc/llc.h>
 
-#include <linux/nfc/pn544.h>
-
-#define DRIVER_DESC "HCI NFC driver for PN544"
-
-#define PN544_HCI_DRIVER_NAME "pn544_hci"
+#include "pn544.h"
 
 /* Timing restrictions (ms) */
 #define PN544_HCI_RESETVEN_TIME                30
 
-static struct i2c_device_id pn544_hci_id_table[] = {
-       {"pn544", 0},
-       {}
-};
-
-MODULE_DEVICE_TABLE(i2c, pn544_hci_id_table);
-
 #define HCI_MODE 0
 #define FW_MODE 1
 
-/* framing in HCI mode */
-#define PN544_HCI_LLC_LEN              1
-#define PN544_HCI_LLC_CRC              2
-#define PN544_HCI_LLC_LEN_CRC          (PN544_HCI_LLC_LEN + PN544_HCI_LLC_CRC)
-#define PN544_HCI_LLC_MIN_SIZE         (1 + PN544_HCI_LLC_LEN_CRC)
-#define PN544_HCI_LLC_MAX_PAYLOAD      29
-#define PN544_HCI_LLC_MAX_SIZE         (PN544_HCI_LLC_LEN_CRC + 1 + \
-                                        PN544_HCI_LLC_MAX_PAYLOAD)
-
 enum pn544_state {
        PN544_ST_COLD,
        PN544_ST_FW_READY,
@@ -100,6 +74,10 @@ enum pn544_state {
 #define PN544_SYS_MGMT_INFO_NOTIFICATION       0x02
 
 #define PN544_POLLING_LOOP_MGMT_GATE           0x94
+#define PN544_DEP_MODE                         0x01
+#define PN544_DEP_ATR_REQ                      0x02
+#define PN544_DEP_ATR_RES                      0x03
+#define PN544_DEP_MERGE                                0x0D
 #define PN544_PL_RDPHASES                      0x06
 #define PN544_PL_EMULATION                     0x07
 #define PN544_PL_NFCT_DEACTIVATED              0x09
@@ -108,6 +86,15 @@ enum pn544_state {
 
 #define PN544_NFC_WI_MGMT_GATE                 0xA1
 
+#define PN544_HCI_EVT_SND_DATA                 0x01
+#define PN544_HCI_EVT_ACTIVATED                        0x02
+#define PN544_HCI_EVT_DEACTIVATED              0x03
+#define PN544_HCI_EVT_RCV_DATA                 0x04
+#define PN544_HCI_EVT_CONTINUE_MI              0x05
+
+#define PN544_HCI_CMD_ATTREQUEST               0x12
+#define PN544_HCI_CMD_CONTINUE_ACTIVATION      0x13
+
 static struct nfc_hci_gate pn544_gates[] = {
        {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE},
        {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
@@ -130,248 +117,23 @@ static struct nfc_hci_gate pn544_gates[] = {
 #define PN544_CMDS_HEADROOM    2
 
 struct pn544_hci_info {
-       struct i2c_client *i2c_dev;
-       struct nfc_shdlc *shdlc;
+       struct nfc_phy_ops *phy_ops;
+       void *phy_id;
+
+       struct nfc_hci_dev *hdev;
 
        enum pn544_state state;
 
        struct mutex info_lock;
 
-       unsigned int gpio_en;
-       unsigned int gpio_irq;
-       unsigned int gpio_fw;
-       unsigned int en_polarity;
-
-       int hard_fault;         /*
-                                * < 0 if hardware error occured (e.g. i2c err)
-                                * and prevents normal operation.
-                                */
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
 };
 
-static void pn544_hci_platform_init(struct pn544_hci_info *info)
-{
-       int polarity, retry, ret;
-       char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 };
-       int count = sizeof(rset_cmd);
-
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-       dev_info(&info->i2c_dev->dev, "Detecting nfc_en polarity\n");
-
-       /* Disable fw download */
-       gpio_set_value(info->gpio_fw, 0);
-
-       for (polarity = 0; polarity < 2; polarity++) {
-               info->en_polarity = polarity;
-               retry = 3;
-               while (retry--) {
-                       /* power off */
-                       gpio_set_value(info->gpio_en, !info->en_polarity);
-                       usleep_range(10000, 15000);
-
-                       /* power on */
-                       gpio_set_value(info->gpio_en, info->en_polarity);
-                       usleep_range(10000, 15000);
-
-                       /* send reset */
-                       dev_dbg(&info->i2c_dev->dev, "Sending reset cmd\n");
-                       ret = i2c_master_send(info->i2c_dev, rset_cmd, count);
-                       if (ret == count) {
-                               dev_info(&info->i2c_dev->dev,
-                                        "nfc_en polarity : active %s\n",
-                                        (polarity == 0 ? "low" : "high"));
-                               goto out;
-                       }
-               }
-       }
-
-       dev_err(&info->i2c_dev->dev,
-               "Could not detect nfc_en polarity, fallback to active high\n");
-
-out:
-       gpio_set_value(info->gpio_en, !info->en_polarity);
-}
-
-static int pn544_hci_enable(struct pn544_hci_info *info, int mode)
-{
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-
-       gpio_set_value(info->gpio_fw, 0);
-       gpio_set_value(info->gpio_en, info->en_polarity);
-       usleep_range(10000, 15000);
-
-       return 0;
-}
-
-static void pn544_hci_disable(struct pn544_hci_info *info)
-{
-       pr_info(DRIVER_DESC ": %s\n", __func__);
-
-       gpio_set_value(info->gpio_fw, 0);
-       gpio_set_value(info->gpio_en, !info->en_polarity);
-       usleep_range(10000, 15000);
-
-       gpio_set_value(info->gpio_en, info->en_polarity);
-       usleep_range(10000, 15000);
-
-       gpio_set_value(info->gpio_en, !info->en_polarity);
-       usleep_range(10000, 15000);
-}
-
-static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
-{
-       int r;
-
-       usleep_range(3000, 6000);
-
-       r = i2c_master_send(client, buf, len);
-
-       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
-               usleep_range(6000, 10000);
-               r = i2c_master_send(client, buf, len);
-       }
-
-       if (r >= 0 && r != len)
-               r = -EREMOTEIO;
-
-       return r;
-}
-
-static int check_crc(u8 *buf, int buflen)
-{
-       int len;
-       u16 crc;
-
-       len = buf[0] + 1;
-       crc = crc_ccitt(0xffff, buf, len - 2);
-       crc = ~crc;
-
-       if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) {
-               pr_err(PN544_HCI_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
-                      crc, buf[len - 1], buf[len - 2]);
-
-               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
-               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
-                              16, 2, buf, buflen, false);
-               return -EPERM;
-       }
-       return 0;
-}
-
-/*
- * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
- * that i2c bus will be flushed and that next read will start on a new frame.
- * returned skb contains only LLC header and payload.
- * returns:
- * -EREMOTEIO : i2c read error (fatal)
- * -EBADMSG : frame was incorrect and discarded
- * -ENOMEM : cannot allocate skb, frame dropped
- */
-static int pn544_hci_i2c_read(struct i2c_client *client, struct sk_buff **skb)
-{
-       int r;
-       u8 len;
-       u8 tmp[PN544_HCI_LLC_MAX_SIZE - 1];
-
-       r = i2c_master_recv(client, &len, 1);
-       if (r != 1) {
-               dev_err(&client->dev, "cannot read len byte\n");
-               return -EREMOTEIO;
-       }
-
-       if ((len < (PN544_HCI_LLC_MIN_SIZE - 1)) ||
-           (len > (PN544_HCI_LLC_MAX_SIZE - 1))) {
-               dev_err(&client->dev, "invalid len byte\n");
-               r = -EBADMSG;
-               goto flush;
-       }
-
-       *skb = alloc_skb(1 + len, GFP_KERNEL);
-       if (*skb == NULL) {
-               r = -ENOMEM;
-               goto flush;
-       }
-
-       *skb_put(*skb, 1) = len;
-
-       r = i2c_master_recv(client, skb_put(*skb, len), len);
-       if (r != len) {
-               kfree_skb(*skb);
-               return -EREMOTEIO;
-       }
-
-       r = check_crc((*skb)->data, (*skb)->len);
-       if (r != 0) {
-               kfree_skb(*skb);
-               r = -EBADMSG;
-               goto flush;
-       }
-
-       skb_pull(*skb, 1);
-       skb_trim(*skb, (*skb)->len - 2);
-
-       usleep_range(3000, 6000);
-
-       return 0;
-
-flush:
-       if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
-               r = -EREMOTEIO;
-
-       usleep_range(3000, 6000);
-
-       return r;
-}
-
-/*
- * Reads an shdlc frame from the chip. This is not as straightforward as it
- * seems. There are cases where we could loose the frame start synchronization.
- * The frame format is len-data-crc, and corruption can occur anywhere while
- * transiting on i2c bus, such that we could read an invalid len.
- * In order to recover synchronization with the next frame, we must be sure
- * to read the real amount of data without using the len byte. We do this by
- * assuming the following:
- * - the chip will always present only one single complete frame on the bus
- *   before triggering the interrupt
- * - the chip will not present a new frame until we have completely read
- *   the previous one (or until we have handled the interrupt).
- * The tricky case is when we read a corrupted len that is less than the real
- * len. We must detect this here in order to determine that we need to flush
- * the bus. This is the reason why we check the crc here.
- */
-static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
-{
-       struct pn544_hci_info *info = dev_id;
-       struct i2c_client *client = info->i2c_dev;
-       struct sk_buff *skb = NULL;
-       int r;
-
-       BUG_ON(!info);
-       BUG_ON(irq != info->i2c_dev->irq);
-
-       dev_dbg(&client->dev, "IRQ\n");
-
-       if (info->hard_fault != 0)
-               return IRQ_HANDLED;
-
-       r = pn544_hci_i2c_read(client, &skb);
-       if (r == -EREMOTEIO) {
-               info->hard_fault = r;
-
-               nfc_shdlc_recv_frame(info->shdlc, NULL);
-
-               return IRQ_HANDLED;
-       } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
-               return IRQ_HANDLED;
-       }
-
-       nfc_shdlc_recv_frame(info->shdlc, skb);
-
-       return IRQ_HANDLED;
-}
-
-static int pn544_hci_open(struct nfc_shdlc *shdlc)
+static int pn544_hci_open(struct nfc_hci_dev *hdev)
 {
-       struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
        int r = 0;
 
        mutex_lock(&info->info_lock);
@@ -381,7 +143,7 @@ static int pn544_hci_open(struct nfc_shdlc *shdlc)
                goto out;
        }
 
-       r = pn544_hci_enable(info, HCI_MODE);
+       r = info->phy_ops->enable(info->phy_id);
 
        if (r == 0)
                info->state = PN544_ST_READY;
@@ -391,16 +153,16 @@ out:
        return r;
 }
 
-static void pn544_hci_close(struct nfc_shdlc *shdlc)
+static void pn544_hci_close(struct nfc_hci_dev *hdev)
 {
-       struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
        mutex_lock(&info->info_lock);
 
        if (info->state == PN544_ST_COLD)
                goto out;
 
-       pn544_hci_disable(info);
+       info->phy_ops->disable(info->phy_id);
 
        info->state = PN544_ST_COLD;
 
@@ -408,9 +170,8 @@ out:
        mutex_unlock(&info->info_lock);
 }
 
-static int pn544_hci_ready(struct nfc_shdlc *shdlc)
+static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 {
-       struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
        struct sk_buff *skb;
        static struct hw_config {
                u8 adr[2];
@@ -576,25 +337,23 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc)
        return 0;
 }
 
-static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb)
+static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
-       struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
-       struct i2c_client *client = info->i2c_dev;
-
-       if (info->hard_fault != 0)
-               return info->hard_fault;
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
-       return pn544_hci_i2c_write(client, skb->data, skb->len);
+       return info->phy_ops->write(info->phy_id, skb);
 }
 
-static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
+static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
                                u32 im_protocols, u32 tm_protocols)
 {
-       struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
        u8 phases = 0;
        int r;
        u8 duration[2];
        u8 activated;
+       u8 i_mode = 0x3f; /* Enable all supported modes */
+       u8 t_mode = 0x0f;
+       u8 t_merge = 0x01; /* Enable merge by default */
 
        pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
                __func__, im_protocols, tm_protocols);
@@ -632,6 +391,61 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
        if (r < 0)
                return r;
 
+       if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
+               hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
+                                                       &hdev->gb_len);
+               pr_debug("generate local bytes %p", hdev->gb);
+               if (hdev->gb == NULL || hdev->gb_len == 0) {
+                       im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+                       tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
+               }
+       }
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_send_event(hdev,
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                               NFC_HCI_EVT_END_OPERATION, NULL, 0);
+               if (r < 0)
+                       return r;
+
+               r = nfc_hci_set_param(hdev,
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                               PN544_DEP_MODE, &i_mode, 1);
+               if (r < 0)
+                       return r;
+
+               r = nfc_hci_set_param(hdev,
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                               PN544_DEP_ATR_REQ, hdev->gb, hdev->gb_len);
+               if (r < 0)
+                       return r;
+
+               r = nfc_hci_send_event(hdev,
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                               NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+               if (r < 0)
+                       nfc_hci_send_event(hdev,
+                                       PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                                       NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       }
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                               PN544_DEP_MODE, &t_mode, 1);
+               if (r < 0)
+                       return r;
+
+               r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                               PN544_DEP_ATR_RES, hdev->gb, hdev->gb_len);
+               if (r < 0)
+                       return r;
+
+               r = nfc_hci_set_param(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                               PN544_DEP_MERGE, &t_merge, 1);
+               if (r < 0)
+                       return r;
+       }
+
        r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                               NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
        if (r < 0)
@@ -641,7 +455,44 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
        return r;
 }
 
-static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
+static int pn544_hci_dep_link_up(struct nfc_hci_dev *hdev,
+                               struct nfc_target *target, u8 comm_mode,
+                               u8 *gb, size_t gb_len)
+{
+       struct sk_buff *rgb_skb = NULL;
+       int r;
+
+       r = nfc_hci_get_param(hdev, target->hci_reader_gate,
+                               PN544_DEP_ATR_RES, &rgb_skb);
+       if (r < 0)
+               return r;
+
+       if (rgb_skb->len == 0 || rgb_skb->len > NFC_GB_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+       print_hex_dump(KERN_DEBUG, "remote gb: ", DUMP_PREFIX_OFFSET,
+                       16, 1, rgb_skb->data, rgb_skb->len, true);
+
+       r = nfc_set_remote_general_bytes(hdev->ndev, rgb_skb->data,
+                                               rgb_skb->len);
+
+       if (r == 0)
+               r = nfc_dep_link_is_up(hdev->ndev, target->idx, comm_mode,
+                                       NFC_RF_INITIATOR);
+exit:
+       kfree_skb(rgb_skb);
+       return r;
+}
+
+static int pn544_hci_dep_link_down(struct nfc_hci_dev *hdev)
+{
+
+       return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                                       NFC_HCI_EVT_END_OPERATION, NULL, 0);
+}
+
+static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
                                      struct nfc_target *target)
 {
        switch (gate) {
@@ -652,6 +503,9 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
                target->supported_protocols = NFC_PROTO_JEWEL_MASK;
                target->sens_res = 0x0c00;
                break;
+       case PN544_RF_READER_NFCIP1_INITIATOR_GATE:
+               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+               break;
        default:
                return -EPROTO;
        }
@@ -659,15 +513,25 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
        return 0;
 }
 
-static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
+static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
                                                u8 gate,
                                                struct nfc_target *target)
 {
-       struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
        struct sk_buff *uid_skb;
        int r = 0;
 
-       if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
+       if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
+               return r;
+
+       if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               r = nfc_hci_send_cmd(hdev,
+                       PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                       PN544_HCI_CMD_CONTINUE_ACTIVATION, NULL, 0, NULL);
+               if (r < 0)
+                       return r;
+
+               target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+       } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
                if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
                    target->nfcid1_len != 10)
                        return -EPROTO;
@@ -690,6 +554,16 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
                                     PN544_RF_READER_CMD_ACTIVATE_NEXT,
                                     uid_skb->data, uid_skb->len, NULL);
                kfree_skb(uid_skb);
+
+               r = nfc_hci_send_cmd(hdev,
+                                       PN544_RF_READER_NFCIP1_INITIATOR_GATE,
+                                       PN544_HCI_CMD_CONTINUE_ACTIVATION,
+                                       NULL, 0, NULL);
+               if (r < 0)
+                       return r;
+
+               target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
        } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
                /*
                 * TODO: maybe other ISO 14443 require some kind of continue
@@ -704,6 +578,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
        return r;
 }
 
+#define PN544_CB_TYPE_READER_F 1
+
+static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+                                      int err)
+{
+       struct pn544_hci_info *info = context;
+
+       switch (info->async_cb_type) {
+       case PN544_CB_TYPE_READER_F:
+               if (err == 0)
+                       skb_pull(skb, 1);
+               info->async_cb(info->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
 #define MIFARE_CMD_AUTH_KEY_A  0x60
 #define MIFARE_CMD_AUTH_KEY_B  0x61
 #define MIFARE_CMD_HEADER      2
@@ -715,13 +609,12 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
  * <= 0: driver handled the data exchange
  *    1: driver doesn't especially handle, please do standard processing
  */
-static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
+static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev,
                                   struct nfc_target *target,
-                                  struct sk_buff *skb,
-                                  struct sk_buff **res_skb)
+                                  struct sk_buff *skb, data_exchange_cb_t cb,
+                                  void *cb_context)
 {
-       struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
-       int r;
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
        pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
                target->hci_reader_gate);
@@ -746,114 +639,171 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
                                memcpy(data, uid, MIFARE_UID_LEN);
                        }
 
-                       return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                               PN544_MIFARE_CMD,
-                                               skb->data, skb->len, res_skb);
+                       return nfc_hci_send_cmd_async(hdev,
+                                                     target->hci_reader_gate,
+                                                     PN544_MIFARE_CMD,
+                                                     skb->data, skb->len,
+                                                     cb, cb_context);
                } else
                        return 1;
        case PN544_RF_READER_F_GATE:
                *skb_push(skb, 1) = 0;
                *skb_push(skb, 1) = 0;
 
-               r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                    PN544_FELICA_RAW,
-                                    skb->data, skb->len, res_skb);
-               if (r == 0)
-                       skb_pull(*res_skb, 1);
-               return r;
+               info->async_cb_type = PN544_CB_TYPE_READER_F;
+               info->async_cb = cb;
+               info->async_cb_context = cb_context;
+
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             PN544_FELICA_RAW, skb->data,
+                                             skb->len,
+                                             pn544_hci_data_exchange_cb, info);
        case PN544_RF_READER_JEWEL_GATE:
-               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                       PN544_JEWEL_RAW_CMD,
-                                       skb->data, skb->len, res_skb);
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             PN544_JEWEL_RAW_CMD, skb->data,
+                                             skb->len, cb, cb_context);
+       case PN544_RF_READER_NFCIP1_INITIATOR_GATE:
+               *skb_push(skb, 1) = 0;
+
+               return nfc_hci_send_event(hdev, target->hci_reader_gate,
+                                       PN544_HCI_EVT_SND_DATA, skb->data,
+                                       skb->len);
        default:
                return 1;
        }
 }
 
-static int pn544_hci_check_presence(struct nfc_shdlc *shdlc,
+static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       /* Set default false for multiple information chaining */
+       *skb_push(skb, 1) = 0;
+
+       return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE,
+                               PN544_HCI_EVT_SND_DATA, skb->data, skb->len);
+}
+
+static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                                   struct nfc_target *target)
 {
-       struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
+       pr_debug("supported protocol %d", target->supported_protocols);
+       if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK |
+                                       NFC_PROTO_ISO14443_B_MASK)) {
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       PN544_RF_READER_CMD_PRESENCE_CHECK,
+                                       NULL, 0, NULL);
+       } else if (target->supported_protocols & NFC_PROTO_MIFARE_MASK) {
+               if (target->nfcid1_len != 4 && target->nfcid1_len != 7 &&
+                   target->nfcid1_len != 10)
+                       return -EOPNOTSUPP;
+
+                return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
+                                    PN544_RF_READER_CMD_ACTIVATE_NEXT,
+                                    target->nfcid1, target->nfcid1_len, NULL);
+       } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) {
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
+       } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
+               return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+                                       PN544_FELICA_RAW, NULL, 0, NULL);
+       } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       PN544_HCI_CMD_ATTREQUEST,
+                                       NULL, 0, NULL);
+       }
 
-       return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                               PN544_RF_READER_CMD_PRESENCE_CHECK,
-                               NULL, 0, NULL);
+       return 0;
 }
 
-static struct nfc_shdlc_ops pn544_shdlc_ops = {
+static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
+                                       u8 event, struct sk_buff *skb)
+{
+       struct sk_buff *rgb_skb = NULL;
+       int r = 0;
+
+       pr_debug("hci event %d", event);
+       switch (event) {
+       case PN544_HCI_EVT_ACTIVATED:
+               if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE)
+                       nfc_hci_target_discovered(hdev, gate);
+               else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) {
+                       r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ,
+                                               &rgb_skb);
+
+                       if (r < 0)
+                               goto exit;
+
+                       nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
+                                       NFC_COMM_PASSIVE, rgb_skb->data,
+                                       rgb_skb->len);
+
+                       kfree_skb(rgb_skb);
+               }
+
+               break;
+       case PN544_HCI_EVT_DEACTIVATED:
+               nfc_hci_send_event(hdev, gate,
+                       NFC_HCI_EVT_END_OPERATION, NULL, 0);
+               break;
+       case PN544_HCI_EVT_RCV_DATA:
+               if (skb->len < 2) {
+                       r = -EPROTO;
+                       goto exit;
+               }
+
+               if (skb->data[0] != 0) {
+                       pr_debug("data0 %d", skb->data[0]);
+                       r = -EPROTO;
+                       goto exit;
+               }
+
+               skb_pull(skb, 2);
+               nfc_tm_data_received(hdev->ndev, skb);
+
+               return;
+       default:
+               break;
+       }
+
+exit:
+       kfree_skb(skb);
+}
+
+static struct nfc_hci_ops pn544_hci_ops = {
        .open = pn544_hci_open,
        .close = pn544_hci_close,
        .hci_ready = pn544_hci_ready,
        .xmit = pn544_hci_xmit,
        .start_poll = pn544_hci_start_poll,
+       .dep_link_up = pn544_hci_dep_link_up,
+       .dep_link_down = pn544_hci_dep_link_down,
        .target_from_gate = pn544_hci_target_from_gate,
        .complete_target_discovered = pn544_hci_complete_target_discovered,
-       .data_exchange = pn544_hci_data_exchange,
+       .im_transceive = pn544_hci_im_transceive,
+       .tm_send = pn544_hci_tm_send,
        .check_presence = pn544_hci_check_presence,
+       .event_received = pn544_hci_event_received,
 };
 
-static int __devinit pn544_hci_probe(struct i2c_client *client,
-                                    const struct i2c_device_id *id)
+int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+                   int phy_headroom, int phy_tailroom, int phy_payload,
+                   struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       struct pn544_nfc_platform_data *pdata;
-       int r = 0;
        u32 protocols;
        struct nfc_hci_init_data init_data;
-
-       dev_dbg(&client->dev, "%s\n", __func__);
-       dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
-
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               dev_err(&client->dev, "Need I2C_FUNC_I2C\n");
-               return -ENODEV;
-       }
+       int r;
 
        info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL);
        if (!info) {
-               dev_err(&client->dev,
-                       "Cannot allocate memory for pn544_hci_info.\n");
+               pr_err("Cannot allocate memory for pn544_hci_info.\n");
                r = -ENOMEM;
                goto err_info_alloc;
        }
 
-       info->i2c_dev = client;
+       info->phy_ops = phy_ops;
+       info->phy_id = phy_id;
        info->state = PN544_ST_COLD;
        mutex_init(&info->info_lock);
-       i2c_set_clientdata(client, info);
-
-       pdata = client->dev.platform_data;
-       if (pdata == NULL) {
-               dev_err(&client->dev, "No platform data\n");
-               r = -EINVAL;
-               goto err_pdata;
-       }
-
-       if (pdata->request_resources == NULL) {
-               dev_err(&client->dev, "request_resources() missing\n");
-               r = -EINVAL;
-               goto err_pdata;
-       }
-
-       r = pdata->request_resources(client);
-       if (r) {
-               dev_err(&client->dev, "Cannot get platform resources\n");
-               goto err_pdata;
-       }
-
-       info->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
-       info->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
-       info->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
-
-       pn544_hci_platform_init(info);
-
-       r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn,
-                                IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME,
-                                info);
-       if (r < 0) {
-               dev_err(&client->dev, "Unable to register IRQ handler\n");
-               goto err_rti;
-       }
 
        init_data.gate_count = ARRAY_SIZE(pn544_gates);
 
@@ -872,89 +822,41 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops,
-                                        &init_data, protocols,
-                                        PN544_CMDS_HEADROOM, 0,
-                                        PN544_HCI_LLC_MAX_PAYLOAD,
-                                        dev_name(&client->dev));
-       if (!info->shdlc) {
-               dev_err(&client->dev, "Cannot allocate nfc shdlc.\n");
+       info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
+                                            protocols, llc_name,
+                                            phy_headroom + PN544_CMDS_HEADROOM,
+                                            phy_tailroom, phy_payload);
+       if (!info->hdev) {
+               pr_err("Cannot allocate nfc hdev.\n");
                r = -ENOMEM;
-               goto err_allocshdlc;
+               goto err_alloc_hdev;
        }
 
-       nfc_shdlc_set_clientdata(info->shdlc, info);
+       nfc_hci_set_clientdata(info->hdev, info);
 
-       return 0;
+       r = nfc_hci_register_device(info->hdev);
+       if (r)
+               goto err_regdev;
 
-err_allocshdlc:
-       free_irq(client->irq, info);
+       *hdev = info->hdev;
+
+       return 0;
 
-err_rti:
-       if (pdata->free_resources != NULL)
-               pdata->free_resources();
+err_regdev:
+       nfc_hci_free_device(info->hdev);
 
-err_pdata:
+err_alloc_hdev:
        kfree(info);
 
 err_info_alloc:
        return r;
 }
 
-static __devexit int pn544_hci_remove(struct i2c_client *client)
+void pn544_hci_remove(struct nfc_hci_dev *hdev)
 {
-       struct pn544_hci_info *info = i2c_get_clientdata(client);
-       struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
-
-       dev_dbg(&client->dev, "%s\n", __func__);
-
-       nfc_shdlc_free(info->shdlc);
-
-       if (info->state != PN544_ST_COLD) {
-               if (pdata->disable)
-                       pdata->disable();
-       }
-
-       free_irq(client->irq, info);
-       if (pdata->free_resources)
-               pdata->free_resources();
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
 
+       nfc_hci_unregister_device(hdev);
+       nfc_hci_free_device(hdev);
        kfree(info);
-
-       return 0;
 }
-
-static struct i2c_driver pn544_hci_driver = {
-       .driver = {
-                  .name = PN544_HCI_DRIVER_NAME,
-                 },
-       .probe = pn544_hci_probe,
-       .id_table = pn544_hci_id_table,
-       .remove = __devexit_p(pn544_hci_remove),
-};
-
-static int __init pn544_hci_init(void)
-{
-       int r;
-
-       pr_debug(DRIVER_DESC ": %s\n", __func__);
-
-       r = i2c_add_driver(&pn544_hci_driver);
-       if (r) {
-               pr_err(PN544_HCI_DRIVER_NAME ": driver registration failed\n");
-               return r;
-       }
-
-       return 0;
-}
-
-static void __exit pn544_hci_exit(void)
-{
-       i2c_del_driver(&pn544_hci_driver);
-}
-
-module_init(pn544_hci_init);
-module_exit(pn544_hci_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h
new file mode 100644 (file)
index 0000000..f47c645
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 - 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_PN544_H_
+#define __LOCAL_PN544_H_
+
+#include <net/nfc/hci.h>
+
+#define DRIVER_DESC "HCI NFC driver for PN544"
+
+int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
+                   int phy_headroom, int phy_tailroom, int phy_payload,
+                   struct nfc_hci_dev **hdev);
+void pn544_hci_remove(struct nfc_hci_dev *hdev);
+
+#endif /* __LOCAL_PN544_H_ */
index 937c83b..2897f71 100644 (file)
@@ -268,6 +268,7 @@ header-y += netfilter_ipv4.h
 header-y += netfilter_ipv6.h
 header-y += netlink.h
 header-y += netrom.h
+header-y += nfc.h
 header-y += nfs.h
 header-y += nfs2.h
 header-y += nfs3.h
index 6189f27..d908d17 100644 (file)
@@ -183,4 +183,15 @@ struct sockaddr_nfc_llcp {
 
 #define NFC_HEADER_SIZE 1
 
+/**
+ * Pseudo-header info for raw socket packets
+ * First byte is the adapter index
+ * Second byte contains flags
+ *  - 0x01 - Direction (0=RX, 1=TX)
+ *  - 0x02-0x80 - Reserved
+ **/
+#define NFC_LLCP_RAW_HEADER_SIZE       2
+#define NFC_LLCP_DIRECTION_RX          0x00
+#define NFC_LLCP_DIRECTION_TX          0x01
+
 #endif /*__LINUX_NFC_H */
index 06f3050..d583df0 100644 (file)
@@ -513,6 +513,8 @@ extern struct sk_buff *skb_copy(const struct sk_buff *skb,
                                gfp_t priority);
 extern struct sk_buff *pskb_copy(struct sk_buff *skb,
                                 gfp_t gfp_mask);
+extern struct sk_buff *__pskb_copy(struct sk_buff *skb,
+                                int headroom, gfp_t gfp_mask);
 extern int            pskb_expand_head(struct sk_buff *skb,
                                        int nhead, int ntail,
                                        gfp_t gfp_mask);
index e17f822..b2819f8 100644 (file)
@@ -266,6 +266,7 @@ struct ucred {
 
 #define MSG_EOF         MSG_FIN
 
+#define MSG_FASTOPEN   0x20000000      /* Send data in TCP SYN */
 #define MSG_CMSG_CLOEXEC 0x40000000    /* Set close_on_exit for file
                                           descriptor received through
                                           SCM_RIGHTS */
index f5169b0..639f50a 100644 (file)
 
 #include <net/nfc/nfc.h>
 
+struct nfc_phy_ops {
+       int (*write)(void *dev_id, struct sk_buff *skb);
+       int (*enable)(void *dev_id);
+       void (*disable)(void *dev_id);
+};
+
 struct nfc_hci_dev;
 
 struct nfc_hci_ops {
        int (*open) (struct nfc_hci_dev *hdev);
        void (*close) (struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
+       /*
+        * xmit must always send the complete buffer before
+        * returning. Returned result must be 0 for success
+        * or negative for failure.
+        */
        int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*start_poll) (struct nfc_hci_dev *hdev,
                           u32 im_protocols, u32 tm_protocols);
+       int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target,
+                          u8 comm_mode, u8 *gb, size_t gb_len);
+       int (*dep_link_down)(struct nfc_hci_dev *hdev);
        int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
                                 struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
                                           struct nfc_target *target);
-       int (*data_exchange) (struct nfc_hci_dev *hdev,
-                             struct nfc_target *target,
-                             struct sk_buff *skb, struct sk_buff **res_skb);
+       int (*im_transceive) (struct nfc_hci_dev *hdev,
+                             struct nfc_target *target, struct sk_buff *skb,
+                             data_exchange_cb_t cb, void *cb_context);
+       int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
+       void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
+                               struct sk_buff *skb);
 };
 
 /* Pipes */
@@ -74,7 +91,6 @@ struct nfc_hci_dev {
 
        struct list_head msg_tx_queue;
 
-       struct workqueue_struct *msg_tx_wq;
        struct work_struct msg_tx_work;
 
        struct timer_list cmd_timer;
@@ -82,13 +98,14 @@ struct nfc_hci_dev {
 
        struct sk_buff_head rx_hcp_frags;
 
-       struct workqueue_struct *msg_rx_wq;
        struct work_struct msg_rx_work;
 
        struct sk_buff_head msg_rx_queue;
 
        struct nfc_hci_ops *ops;
 
+       struct nfc_llc *llc;
+
        struct nfc_hci_init_data init_data;
 
        void *clientdata;
@@ -105,12 +122,20 @@ struct nfc_hci_dev {
        u8 hw_mpw;
        u8 hw_software;
        u8 hw_bsid;
+
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
+
+       u8 *gb;
+       size_t gb_len;
 };
 
 /* hci device allocation */
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            u32 protocols,
+                                           const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
                                            int max_link_payload);
@@ -202,9 +227,13 @@ int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len);
 int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
                     const u8 *param, size_t param_len, struct sk_buff **skb);
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+                          const u8 *param, size_t param_len,
+                          data_exchange_cb_t cb, void *cb_context);
 int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
                          const u8 *param, size_t param_len);
 int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                       const u8 *param, size_t param_len);
+int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
 
 #endif /* __NET_HCI_H */
diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h
new file mode 100644 (file)
index 0000000..400ab7a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Link Layer Control manager public interface
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __NFC_LLC_H_
+#define __NFC_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <linux/skbuff.h>
+
+#define LLC_NOP_NAME "nop"
+#define LLC_SHDLC_NAME "shdlc"
+
+typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err);
+
+struct nfc_llc;
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+                                xmit_to_drv_t xmit_to_drv,
+                                rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                                int tx_tailroom, llc_failure_t llc_failure);
+void nfc_llc_free(struct nfc_llc *llc);
+
+void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+                                  int *rx_tailroom);
+
+
+int nfc_llc_start(struct nfc_llc *llc);
+int nfc_llc_stop(struct nfc_llc *llc);
+void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb);
+int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb);
+
+int nfc_llc_init(void);
+void nfc_llc_exit(void);
+
+#endif /* __NFC_LLC_H_ */
index 276094b..88785e5 100644 (file)
@@ -32,6 +32,7 @@
 #define NCI_MAX_NUM_MAPPING_CONFIGS                            10
 #define NCI_MAX_NUM_RF_CONFIGS                                 10
 #define NCI_MAX_NUM_CONN                                       10
+#define NCI_MAX_PARAM_LEN                                      251
 
 /* NCI Status Codes */
 #define NCI_STATUS_OK                                          0x00
 #define NCI_RF_INTERFACE_ISO_DEP                               0x02
 #define NCI_RF_INTERFACE_NFC_DEP                               0x03
 
+/* NCI Configuration Parameter Tags */
+#define NCI_PN_ATR_REQ_GEN_BYTES                               0x29
+
 /* NCI Reset types */
 #define NCI_RESET_TYPE_KEEP_CONFIG                             0x00
 #define NCI_RESET_TYPE_RESET_CONFIG                            0x01
@@ -188,6 +192,18 @@ struct nci_core_reset_cmd {
 
 #define NCI_OP_CORE_INIT_CMD           nci_opcode_pack(NCI_GID_CORE, 0x01)
 
+#define NCI_OP_CORE_SET_CONFIG_CMD     nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct set_config_param {
+       __u8    id;
+       __u8    len;
+       __u8    val[NCI_MAX_PARAM_LEN];
+} __packed;
+
+struct nci_core_set_config_cmd {
+       __u8    num_params;
+       struct  set_config_param param; /* support 1 param per cmd is enough */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_CMD     nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
        __u8    rf_protocol;
@@ -252,6 +268,13 @@ struct nci_core_init_rsp_2 {
        __le32  manufact_specific_info;
 } __packed;
 
+#define NCI_OP_CORE_SET_CONFIG_RSP     nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct nci_core_set_config_rsp {
+       __u8    status;
+       __u8    num_params;
+       __u8    params_id[0];   /* variable size array */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP     nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP         nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
@@ -328,6 +351,11 @@ struct activation_params_nfcb_poll_iso_dep {
        __u8    attrib_res[50];
 };
 
+struct activation_params_poll_nfc_dep {
+       __u8    atr_res_len;
+       __u8    atr_res[63];
+};
+
 struct nci_rf_intf_activated_ntf {
        __u8    rf_discovery_id;
        __u8    rf_interface;
@@ -351,6 +379,7 @@ struct nci_rf_intf_activated_ntf {
        union {
                struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
                struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
+               struct activation_params_poll_nfc_dep poll_nfc_dep;
        } activation_params;
 
 } __packed;
index feba740..d705d86 100644 (file)
@@ -54,6 +54,7 @@ enum nci_state {
 /* NCI timeouts */
 #define NCI_RESET_TIMEOUT                      5000
 #define NCI_INIT_TIMEOUT                       5000
+#define NCI_SET_CONFIG_TIMEOUT                 5000
 #define NCI_RF_DISC_TIMEOUT                    5000
 #define NCI_RF_DISC_SELECT_TIMEOUT             5000
 #define NCI_RF_DEACTIVATE_TIMEOUT              30000
@@ -137,6 +138,10 @@ struct nci_dev {
        data_exchange_cb_t      data_exchange_cb;
        void                    *data_exchange_cb_context;
        struct sk_buff          *rx_data_reassembly;
+
+       /* stored during intf_activated_ntf */
+       __u8 remote_gb[NFC_MAX_GT_LEN];
+       __u8 remote_gb_len;
 };
 
 /* ----- NCI Devices ----- */
index 6431f5e..bfbac73 100644 (file)
@@ -72,6 +72,7 @@ struct nfc_ops {
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
+#define NFC_ATR_RES_GT_OFFSET 15
 
 struct nfc_target {
        u32 idx;
@@ -112,7 +113,6 @@ struct nfc_dev {
        int tx_tailroom;
 
        struct timer_list check_pres_timer;
-       struct workqueue_struct *check_pres_wq;
        struct work_struct check_pres_work;
 
        struct nfc_ops *ops;
index bb49c4a..075a0ce 100644 (file)
@@ -59,6 +59,7 @@ ifneq ($(CONFIG_DCB),)
 obj-y                          += dcb/
 endif
 obj-$(CONFIG_IEEE802154)       += ieee802154/
+obj-$(CONFIG_MAC802154)                += mac802154/
 
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)           += sysctl_net.o
@@ -68,4 +69,5 @@ obj-$(CONFIG_DNS_RESOLVER)    += dns_resolver/
 obj-$(CONFIG_CEPH_LIB)         += ceph/
 obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
 obj-$(CONFIG_NFC)              += nfc/
-obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o
+obj-$(CONFIG_OPENVSWITCH)      += openvswitch/
+obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o
\ No newline at end of file
index 1ae43b5..7db8cbb 100644 (file)
@@ -752,6 +752,59 @@ out:
 EXPORT_SYMBOL(pskb_copy);
 
 /**
+ *     __pskb_copy     -       create copy of an sk_buff with private head.
+ *     @skb: buffer to copy
+ *     @headroom: headroom of new skb
+ *     @gfp_mask: allocation priority
+ *
+ *     Make a copy of both an &sk_buff and part of its data, located
+ *     in header. Fragmented data remain shared. This is used when
+ *     the caller wishes to modify only header of &sk_buff and needs
+ *     private copy of the header to alter. Returns %NULL on failure
+ *     or the pointer to the buffer on success.
+ *     The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
+{
+       unsigned int size = skb_headlen(skb) + headroom;
+       struct sk_buff *n = alloc_skb(size, gfp_mask);
+       if (!n)
+               goto out;
+
+       /* Set the data pointer */
+       skb_reserve(n, headroom);
+       /* Set the tail pointer and length */
+       skb_put(n, skb_headlen(skb));
+       /* Copy the bytes */
+       skb_copy_from_linear_data(skb, n->data, n->len);
+
+       n->truesize += skb->data_len;
+       n->data_len  = skb->data_len;
+       n->len       = skb->len;
+
+       if (skb_shinfo(skb)->nr_frags) {
+               int i;
+
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+                       get_page(skb_shinfo(n)->frags[i].page);
+               }
+               skb_shinfo(n)->nr_frags = i;
+       }
+
+       if (skb_has_frag_list(skb)) {
+               skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
+               skb_clone_fraglist(n);
+       }
+
+       copy_skb_header(n, skb);
+out:
+       return n;
+}
+EXPORT_SYMBOL(__pskb_copy);
+
+/**
  *     pskb_expand_head - reallocate header of &sk_buff
  *     @skb: buffer to reallocate
  *     @nhead: room to add at head
index be98ac5..43227a7 100644 (file)
@@ -181,6 +181,7 @@ int nfc_stop_poll(struct nfc_dev *dev)
 
        dev->ops->stop_poll(dev);
        dev->polling = false;
+       dev->rf_mode = NFC_RF_NONE;
 
 error:
        device_unlock(&dev->dev);
@@ -274,12 +275,14 @@ int nfc_dep_link_down(struct nfc_dev *dev)
        if (!rc) {
                dev->dep_link_up = false;
                dev->active_target = NULL;
+               dev->rf_mode = NFC_RF_NONE;
                nfc_llcp_mac_is_down(dev);
                nfc_genl_dep_link_down_event(dev);
        }
 
 error:
        device_unlock(&dev->dev);
+
        return rc;
 }
 
@@ -503,6 +506,7 @@ EXPORT_SYMBOL(nfc_tm_activated);
 int nfc_tm_deactivated(struct nfc_dev *dev)
 {
        dev->dep_link_up = false;
+       dev->rf_mode = NFC_RF_NONE;
 
        return nfc_genl_tm_deactivated(dev);
 }
@@ -679,7 +683,7 @@ static void nfc_release(struct device *d)
 
        if (dev->ops->check_presence) {
                del_timer_sync(&dev->check_pres_timer);
-               destroy_workqueue(dev->check_pres_wq);
+               cancel_work_sync(&dev->check_pres_work);
        }
 
        nfc_genl_data_exit(&dev->genl_data);
@@ -697,6 +701,8 @@ static void nfc_check_pres_work(struct work_struct *work)
 
        if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) {
                rc = dev->ops->check_presence(dev, dev->active_target);
+               if (rc == -EOPNOTSUPP)
+                       goto exit;
                if (!rc) {
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
@@ -708,6 +714,7 @@ static void nfc_check_pres_work(struct work_struct *work)
                }
        }
 
+exit:
        device_unlock(&dev->dev);
 }
 
@@ -715,7 +722,7 @@ static void nfc_check_pres_timeout(unsigned long data)
 {
        struct nfc_dev *dev = (struct nfc_dev *)data;
 
-       queue_work(dev->check_pres_wq, &dev->check_pres_work);
+       queue_work(system_nrt_wq, &dev->check_pres_work);
 }
 
 struct class nfc_class = {
@@ -779,25 +786,17 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        nfc_genl_data_init(&dev->genl_data);
 
+       dev->rf_mode = NFC_RF_NONE;
 
        /* first generation must not be 0 */
        dev->targets_generation = 1;
 
        if (ops->check_presence) {
-               char name[32];
                init_timer(&dev->check_pres_timer);
                dev->check_pres_timer.data = (unsigned long)dev;
                dev->check_pres_timer.function = nfc_check_pres_timeout;
 
                INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
-               snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
-               dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
-                                                    WQ_UNBOUND |
-                                                    WQ_MEM_RECLAIM, 1);
-               if (dev->check_pres_wq == NULL) {
-                       kfree(dev);
-                       return NULL;
-               }
        }
 
        return dev;
index f9c44b2..c5dbb68 100644 (file)
@@ -4,5 +4,5 @@
 
 obj-$(CONFIG_NFC_HCI) += hci.o
 
-hci-y                  := core.o hcp.o command.o
-hci-$(CONFIG_NFC_SHDLC)        += shdlc.o
+hci-y                  := core.o hcp.o command.o llc.o llc_nop.o
+hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o
index 46362ef..07659cf 100644 (file)
 
 #include "hci.h"
 
-static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err,
-                              struct sk_buff *skb, void *cb_data)
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+                              const u8 *param, size_t param_len,
+                              data_exchange_cb_t cb, void *cb_context)
 {
-       struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data;
+       pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+                cmd, param_len);
+
+       /* TODO: Define hci cmd execution delay. Should it be the same
+        * for all commands?
+        */
+       return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
+                                     param, param_len, cb, cb_context, 3000);
+}
+
+/*
+ * HCI command execution completion callback.
+ * err will be a standard linux error (may be converted from HCI response)
+ * skb contains the response data and must be disposed, or may be NULL if
+ * an error occured
+ */
+static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
+{
+       struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
 
        pr_debug("HCI Cmd completed with result=%d\n", err);
 
@@ -55,7 +74,8 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
        hcp_ew.exec_complete = false;
        hcp_ew.result_skb = NULL;
 
-       pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len);
+       pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+                cmd, param_len);
 
        /* TODO: Define hci cmd execution delay. Should it be the same
         * for all commands?
@@ -133,6 +153,23 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
 }
 EXPORT_SYMBOL(nfc_hci_send_cmd);
 
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+                          const u8 *param, size_t param_len,
+                          data_exchange_cb_t cb, void *cb_context)
+{
+       u8 pipe;
+
+       pr_debug("\n");
+
+       pipe = hdev->gate2pipe[gate];
+       if (pipe == NFC_HCI_INVALID_PIPE)
+               return -EADDRNOTAVAIL;
+
+       return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
+                                        cb, cb_context);
+}
+EXPORT_SYMBOL(nfc_hci_send_cmd_async);
+
 int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len)
 {
@@ -220,16 +257,16 @@ static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
        *result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
                                      NFC_HCI_ADM_CREATE_PIPE,
                                      (u8 *) &params, sizeof(params), &skb);
-       if (*result == 0) {
-               resp = (struct hci_create_pipe_resp *)skb->data;
-               pipe = resp->pipe;
-               kfree_skb(skb);
+       if (*result < 0)
+               return NFC_HCI_INVALID_PIPE;
 
-               pr_debug("pipe created=%d\n", pipe);
+       resp = (struct hci_create_pipe_resp *)skb->data;
+       pipe = resp->pipe;
+       kfree_skb(skb);
 
-               return pipe;
-       } else
-               return NFC_HCI_INVALID_PIPE;
+       pr_debug("pipe created=%d\n", pipe);
+
+       return pipe;
 }
 
 static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
@@ -242,8 +279,6 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
 
 static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
 {
-       int r;
-
        u8 param[2];
 
        /* TODO: Find out what the identity reference data is
@@ -251,10 +286,8 @@ static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
 
        pr_debug("\n");
 
-       r = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
-                               NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
-
-       return 0;
+       return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
+                                  NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
 }
 
 int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
index e044801..8af3052 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <net/nfc/nfc.h>
 #include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
 
 #include "hci.h"
 
@@ -57,12 +58,11 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
        if (hdev->cmd_pending_msg) {
                if (timer_pending(&hdev->cmd_timer) == 0) {
                        if (hdev->cmd_pending_msg->cb)
-                               hdev->cmd_pending_msg->cb(hdev,
-                                                         -ETIME,
-                                                         NULL,
-                                                         hdev->
+                               hdev->cmd_pending_msg->cb(hdev->
                                                          cmd_pending_msg->
-                                                         cb_context);
+                                                         cb_context,
+                                                         NULL,
+                                                         -ETIME);
                        kfree(hdev->cmd_pending_msg);
                        hdev->cmd_pending_msg = NULL;
                } else
@@ -78,12 +78,12 @@ next_msg:
 
        pr_debug("msg_tx_queue has a cmd to send\n");
        while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) {
-               r = hdev->ops->xmit(hdev, skb);
+               r = nfc_llc_xmit_from_hci(hdev->llc, skb);
                if (r < 0) {
                        kfree_skb(skb);
                        skb_queue_purge(&msg->msg_frags);
                        if (msg->cb)
-                               msg->cb(hdev, r, NULL, msg->cb_context);
+                               msg->cb(msg->cb_context, NULL, r);
                        kfree(msg);
                        break;
                }
@@ -133,15 +133,15 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err,
        del_timer_sync(&hdev->cmd_timer);
 
        if (hdev->cmd_pending_msg->cb)
-               hdev->cmd_pending_msg->cb(hdev, err, skb,
-                                         hdev->cmd_pending_msg->cb_context);
+               hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context,
+                                         skb, err);
        else
                kfree_skb(skb);
 
        kfree(hdev->cmd_pending_msg);
        hdev->cmd_pending_msg = NULL;
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
@@ -182,7 +182,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak)
        }
 }
 
-static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
+int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
 {
        struct nfc_target *targets;
        struct sk_buff *atqa_skb = NULL;
@@ -233,7 +233,7 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
                        goto exit;
                }
 
-               memcpy (targets->nfcid1, uid_skb->data, uid_skb->len);
+               memcpy(targets->nfcid1, uid_skb->data, uid_skb->len);
                targets->nfcid1_len = uid_skb->len;
 
                if (hdev->ops->complete_target_discovered) {
@@ -263,7 +263,9 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
                break;
        }
 
-       targets->hci_reader_gate = gate;
+       /* if driver set the new gate, we will skip the old one */
+       if (targets->hci_reader_gate == 0x00)
+               targets->hci_reader_gate = gate;
 
        r = nfc_targets_found(hdev->ndev, targets, 1);
 
@@ -275,6 +277,7 @@ exit:
 
        return r;
 }
+EXPORT_SYMBOL(nfc_hci_target_discovered);
 
 void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                            struct sk_buff *skb)
@@ -307,8 +310,13 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
                                              nfc_hci_pipe2gate(hdev, pipe));
                break;
        default:
-               /* TODO: Unknown events are hardware specific
-                * pass them to the driver (needs a new hci_ops) */
+               if (hdev->ops->event_received) {
+                       hdev->ops->event_received(hdev,
+                                               nfc_hci_pipe2gate(hdev, pipe),
+                                               event, skb);
+                       return;
+               }
+
                break;
        }
 
@@ -326,7 +334,7 @@ static void nfc_hci_cmd_timeout(unsigned long data)
 {
        struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
@@ -398,8 +406,7 @@ disconnect_all:
        nfc_hci_disconnect_all_gates(hdev);
 
 exit:
-       if (skb)
-               kfree_skb(skb);
+       kfree_skb(skb);
 
        return r;
 }
@@ -470,29 +477,38 @@ static int hci_dev_up(struct nfc_dev *nfc_dev)
                        return r;
        }
 
+       r = nfc_llc_start(hdev->llc);
+       if (r < 0)
+               goto exit_close;
+
        r = hci_dev_session_init(hdev);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
 
        r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                               NFC_HCI_EVT_END_OPERATION, NULL, 0);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
 
        if (hdev->ops->hci_ready) {
                r = hdev->ops->hci_ready(hdev);
                if (r < 0)
-                       goto exit;
+                       goto exit_llc;
        }
 
        r = hci_dev_version(hdev);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
+
+       return 0;
+
+exit_llc:
+       nfc_llc_stop(hdev->llc);
+
+exit_close:
+       if (hdev->ops->close)
+               hdev->ops->close(hdev);
 
-exit:
-       if (r < 0)
-               if (hdev->ops->close)
-                       hdev->ops->close(hdev);
        return r;
 }
 
@@ -500,6 +516,8 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
+       nfc_llc_stop(hdev->llc);
+
        if (hdev->ops->close)
                hdev->ops->close(hdev);
 
@@ -528,6 +546,28 @@ static void hci_stop_poll(struct nfc_dev *nfc_dev)
                           NFC_HCI_EVT_END_OPERATION, NULL, 0);
 }
 
+static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                               __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->dep_link_up)
+               return hdev->ops->dep_link_up(hdev, target, comm_mode,
+                                               gb, gb_len);
+
+       return 0;
+}
+
+static int hci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->dep_link_down)
+               return hdev->ops->dep_link_down(hdev);
+
+       return 0;
+}
+
 static int hci_activate_target(struct nfc_dev *nfc_dev,
                               struct nfc_target *target, u32 protocol)
 {
@@ -539,83 +579,203 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
 {
 }
 
+#define HCI_CB_TYPE_TRANSCEIVE 1
+
+static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
+{
+       struct nfc_hci_dev *hdev = context;
+
+       switch (hdev->async_cb_type) {
+       case HCI_CB_TYPE_TRANSCEIVE:
+               /*
+                * TODO: Check RF Error indicator to make sure data is valid.
+                * It seems that HCI cmd can complete without error, but data
+                * can be invalid if an RF error occured? Ignore for now.
+                */
+               if (err == 0)
+                       skb_trim(skb, skb->len - 1); /* RF Err ind */
+
+               hdev->async_cb(hdev->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
 static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
                          struct sk_buff *skb, data_exchange_cb_t cb,
                          void *cb_context)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
        int r;
-       struct sk_buff *res_skb = NULL;
 
        pr_debug("target_idx=%d\n", target->idx);
 
        switch (target->hci_reader_gate) {
        case NFC_HCI_RF_READER_A_GATE:
        case NFC_HCI_RF_READER_B_GATE:
-               if (hdev->ops->data_exchange) {
-                       r = hdev->ops->data_exchange(hdev, target, skb,
-                                                    &res_skb);
+               if (hdev->ops->im_transceive) {
+                       r = hdev->ops->im_transceive(hdev, target, skb, cb,
+                                                    cb_context);
                        if (r <= 0)     /* handled */
                                break;
                }
 
                *skb_push(skb, 1) = 0;  /* CTR, see spec:10.2.2.1 */
-               r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                    NFC_HCI_WR_XCHG_DATA,
-                                    skb->data, skb->len, &res_skb);
-               /*
-                * TODO: Check RF Error indicator to make sure data is valid.
-                * It seems that HCI cmd can complete without error, but data
-                * can be invalid if an RF error occured? Ignore for now.
-                */
-               if (r == 0)
-                       skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
+
+               hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
+               hdev->async_cb = cb;
+               hdev->async_cb_context = cb_context;
+
+               r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                          NFC_HCI_WR_XCHG_DATA, skb->data,
+                                          skb->len, hci_transceive_cb, hdev);
                break;
        default:
-               if (hdev->ops->data_exchange) {
-                       r = hdev->ops->data_exchange(hdev, target, skb,
-                                                    &res_skb);
+               if (hdev->ops->im_transceive) {
+                       r = hdev->ops->im_transceive(hdev, target, skb, cb,
+                                                    cb_context);
                        if (r == 1)
                                r = -ENOTSUPP;
                }
                else
                        r = -ENOTSUPP;
+               break;
        }
 
        kfree_skb(skb);
 
-       cb(cb_context, res_skb, r);
+       return r;
+}
 
-       return 0;
+static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->tm_send)
+               return hdev->ops->tm_send(hdev, skb);
+       else
+               return -ENOTSUPP;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
                              struct nfc_target *target)
 {
-#if 0
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
        if (hdev->ops->check_presence)
                return hdev->ops->check_presence(hdev, target);
-#endif
 
        return 0;
 }
 
+static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
+{
+       mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->cmd_pending_msg == NULL) {
+               nfc_driver_failure(hdev->ndev, err);
+               goto exit;
+       }
+
+       __nfc_hci_cmd_completion(hdev, err, NULL);
+
+exit:
+       mutex_unlock(&hdev->msg_tx_mutex);
+}
+
+static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err)
+{
+       nfc_hci_failure(hdev, err);
+}
+
+static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hcp_packet *packet;
+       u8 type;
+       u8 instruction;
+       struct sk_buff *hcp_skb;
+       u8 pipe;
+       struct sk_buff *frag_skb;
+       int msg_len;
+
+       packet = (struct hcp_packet *)skb->data;
+       if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
+               skb_queue_tail(&hdev->rx_hcp_frags, skb);
+               return;
+       }
+
+       /* it's the last fragment. Does it need re-aggregation? */
+       if (skb_queue_len(&hdev->rx_hcp_frags)) {
+               pipe = packet->header & NFC_HCI_FRAGMENT;
+               skb_queue_tail(&hdev->rx_hcp_frags, skb);
+
+               msg_len = 0;
+               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+                       msg_len += (frag_skb->len -
+                                   NFC_HCI_HCP_PACKET_HEADER_LEN);
+               }
+
+               hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
+                                            msg_len, GFP_KERNEL);
+               if (hcp_skb == NULL) {
+                       nfc_hci_failure(hdev, -ENOMEM);
+                       return;
+               }
+
+               *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
+
+               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+                       msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
+                       memcpy(skb_put(hcp_skb, msg_len),
+                              frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
+                              msg_len);
+               }
+
+               skb_queue_purge(&hdev->rx_hcp_frags);
+       } else {
+               packet->header &= NFC_HCI_FRAGMENT;
+               hcp_skb = skb;
+       }
+
+       /* if this is a response, dispatch immediately to
+        * unblock waiting cmd context. Otherwise, enqueue to dispatch
+        * in separate context where handler can also execute command.
+        */
+       packet = (struct hcp_packet *)hcp_skb->data;
+       type = HCP_MSG_GET_TYPE(packet->message.header);
+       if (type == NFC_HCI_HCP_RESPONSE) {
+               pipe = packet->header;
+               instruction = HCP_MSG_GET_CMD(packet->message.header);
+               skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
+                        NFC_HCI_HCP_MESSAGE_HEADER_LEN);
+               nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
+       } else {
+               skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
+               queue_work(system_nrt_wq, &hdev->msg_rx_work);
+       }
+}
+
 static struct nfc_ops hci_nfc_ops = {
        .dev_up = hci_dev_up,
        .dev_down = hci_dev_down,
        .start_poll = hci_start_poll,
        .stop_poll = hci_stop_poll,
+       .dep_link_up = hci_dep_link_up,
+       .dep_link_down = hci_dep_link_down,
        .activate_target = hci_activate_target,
        .deactivate_target = hci_deactivate_target,
        .im_transceive = hci_transceive,
+       .tm_send = hci_tm_send,
        .check_presence = hci_check_presence,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            u32 protocols,
+                                           const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
                                            int max_link_payload)
@@ -632,10 +792,19 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
        if (hdev == NULL)
                return NULL;
 
+       hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit,
+                                    nfc_hci_recv_from_llc, tx_headroom,
+                                    tx_tailroom, nfc_hci_llc_failure);
+       if (hdev->llc == NULL) {
+               kfree(hdev);
+               return NULL;
+       }
+
        hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
+               nfc_llc_free(hdev->llc);
                kfree(hdev);
                return NULL;
        }
@@ -655,29 +824,18 @@ EXPORT_SYMBOL(nfc_hci_allocate_device);
 void nfc_hci_free_device(struct nfc_hci_dev *hdev)
 {
        nfc_free_device(hdev->ndev);
+       nfc_llc_free(hdev->llc);
        kfree(hdev);
 }
 EXPORT_SYMBOL(nfc_hci_free_device);
 
 int nfc_hci_register_device(struct nfc_hci_dev *hdev)
 {
-       struct device *dev = &hdev->ndev->dev;
-       const char *devname = dev_name(dev);
-       char name[32];
-       int r = 0;
-
        mutex_init(&hdev->msg_tx_mutex);
 
        INIT_LIST_HEAD(&hdev->msg_tx_queue);
 
        INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
-       snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname);
-       hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                         WQ_MEM_RECLAIM, 1);
-       if (hdev->msg_tx_wq == NULL) {
-               r = -ENOMEM;
-               goto exit;
-       }
 
        init_timer(&hdev->cmd_timer);
        hdev->cmd_timer.data = (unsigned long)hdev;
@@ -686,27 +844,10 @@ int nfc_hci_register_device(struct nfc_hci_dev *hdev)
        skb_queue_head_init(&hdev->rx_hcp_frags);
 
        INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
-       snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname);
-       hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                         WQ_MEM_RECLAIM, 1);
-       if (hdev->msg_rx_wq == NULL) {
-               r = -ENOMEM;
-               goto exit;
-       }
 
        skb_queue_head_init(&hdev->msg_rx_queue);
 
-       r = nfc_register_device(hdev->ndev);
-
-exit:
-       if (r < 0) {
-               if (hdev->msg_tx_wq)
-                       destroy_workqueue(hdev->msg_tx_wq);
-               if (hdev->msg_rx_wq)
-                       destroy_workqueue(hdev->msg_rx_wq);
-       }
-
-       return r;
+       return nfc_register_device(hdev->ndev);
 }
 EXPORT_SYMBOL(nfc_hci_register_device);
 
@@ -727,9 +868,8 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
 
        nfc_unregister_device(hdev->ndev);
 
-       destroy_workqueue(hdev->msg_tx_wq);
-
-       destroy_workqueue(hdev->msg_rx_wq);
+       cancel_work_sync(&hdev->msg_tx_work);
+       cancel_work_sync(&hdev->msg_rx_work);
 }
 EXPORT_SYMBOL(nfc_hci_unregister_device);
 
@@ -745,93 +885,30 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
 }
 EXPORT_SYMBOL(nfc_hci_get_clientdata);
 
-static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
-{
-       mutex_lock(&hdev->msg_tx_mutex);
-
-       if (hdev->cmd_pending_msg == NULL) {
-               nfc_driver_failure(hdev->ndev, err);
-               goto exit;
-       }
-
-       __nfc_hci_cmd_completion(hdev, err, NULL);
-
-exit:
-       mutex_unlock(&hdev->msg_tx_mutex);
-}
-
 void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
 {
        nfc_hci_failure(hdev, err);
 }
 EXPORT_SYMBOL(nfc_hci_driver_failure);
 
-void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hcp_packet *packet;
-       u8 type;
-       u8 instruction;
-       struct sk_buff *hcp_skb;
-       u8 pipe;
-       struct sk_buff *frag_skb;
-       int msg_len;
-
-       packet = (struct hcp_packet *)skb->data;
-       if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
-               skb_queue_tail(&hdev->rx_hcp_frags, skb);
-               return;
-       }
-
-       /* it's the last fragment. Does it need re-aggregation? */
-       if (skb_queue_len(&hdev->rx_hcp_frags)) {
-               pipe = packet->header & NFC_HCI_FRAGMENT;
-               skb_queue_tail(&hdev->rx_hcp_frags, skb);
-
-               msg_len = 0;
-               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
-                       msg_len += (frag_skb->len -
-                                   NFC_HCI_HCP_PACKET_HEADER_LEN);
-               }
-
-               hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
-                                            msg_len, GFP_KERNEL);
-               if (hcp_skb == NULL) {
-                       nfc_hci_failure(hdev, -ENOMEM);
-                       return;
-               }
-
-               *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
-
-               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
-                       msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
-                       memcpy(skb_put(hcp_skb, msg_len),
-                              frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
-                              msg_len);
-               }
+       nfc_llc_rcv_from_drv(hdev->llc, skb);
+}
+EXPORT_SYMBOL(nfc_hci_recv_frame);
 
-               skb_queue_purge(&hdev->rx_hcp_frags);
-       } else {
-               packet->header &= NFC_HCI_FRAGMENT;
-               hcp_skb = skb;
-       }
+static int __init nfc_hci_init(void)
+{
+       return nfc_llc_init();
+}
 
-       /* if this is a response, dispatch immediately to
-        * unblock waiting cmd context. Otherwise, enqueue to dispatch
-        * in separate context where handler can also execute command.
-        */
-       packet = (struct hcp_packet *)hcp_skb->data;
-       type = HCP_MSG_GET_TYPE(packet->message.header);
-       if (type == NFC_HCI_HCP_RESPONSE) {
-               pipe = packet->header;
-               instruction = HCP_MSG_GET_CMD(packet->message.header);
-               skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
-                        NFC_HCI_HCP_MESSAGE_HEADER_LEN);
-               nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
-       } else {
-               skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
-               queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work);
-       }
+static void __exit nfc_hci_exit(void)
+{
+       nfc_llc_exit();
 }
-EXPORT_SYMBOL(nfc_hci_recv_frame);
+
+subsys_initcall(nfc_hci_init);
+module_exit(nfc_hci_exit);
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC HCI Core");
index fa9a21e..b274d12 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef __LOCAL_HCI_H
 #define __LOCAL_HCI_H
 
+#include <net/nfc/hci.h>
+
 struct gate_pipe_map {
        u8 gate;
        u8 pipe;
@@ -35,15 +37,6 @@ struct hcp_packet {
        struct hcp_message message;
 } __packed;
 
-/*
- * HCI command execution completion callback.
- * result will be a standard linux error (may be converted from HCI response)
- * skb contains the response data and must be disposed, or may be NULL if
- * an error occured
- */
-typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result,
-                             struct sk_buff *skb, void *cb_data);
-
 struct hcp_exec_waiter {
        wait_queue_head_t *wq;
        bool exec_complete;
@@ -55,7 +48,7 @@ struct hci_msg {
        struct list_head msg_l;
        struct sk_buff_head msg_frags;
        bool wait_response;
-       hci_cmd_cb_t cb;
+       data_exchange_cb_t cb;
        void *cb_context;
        unsigned long completion_delay;
 };
@@ -83,7 +76,7 @@ struct hci_create_pipe_resp {
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
                           u8 type, u8 instruction,
                           const u8 *payload, size_t payload_len,
-                          hci_cmd_cb_t cb, void *cb_data,
+                          data_exchange_cb_t cb, void *cb_context,
                           unsigned long completion_delay);
 
 u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
index f4dad1a..208eedd 100644 (file)
@@ -35,7 +35,7 @@
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
                           u8 type, u8 instruction,
                           const u8 *payload, size_t payload_len,
-                          hci_cmd_cb_t cb, void *cb_data,
+                          data_exchange_cb_t cb, void *cb_context,
                           unsigned long completion_delay)
 {
        struct nfc_dev *ndev = hdev->ndev;
@@ -52,7 +52,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        skb_queue_head_init(&cmd->msg_frags);
        cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
        cmd->cb = cb;
-       cmd->cb_context = cb_data;
+       cmd->cb_context = cb_context;
        cmd->completion_delay = completion_delay;
 
        hci_len = payload_len + 1;
@@ -108,7 +108,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
        mutex_unlock(&hdev->msg_tx_mutex);
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 
        return 0;
 
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
new file mode 100644 (file)
index 0000000..ae1205d
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Link Layer Control manager
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <net/nfc/llc.h>
+
+#include "llc.h"
+
+static struct list_head llc_engines;
+
+int nfc_llc_init(void)
+{
+       int r;
+
+       INIT_LIST_HEAD(&llc_engines);
+
+       r = nfc_llc_nop_register();
+       if (r)
+               goto exit;
+
+       r = nfc_llc_shdlc_register();
+       if (r)
+               goto exit;
+
+       return 0;
+
+exit:
+       nfc_llc_exit();
+       return r;
+}
+
+void nfc_llc_exit(void)
+{
+       struct nfc_llc_engine *llc_engine, *n;
+
+       list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
+               list_del(&llc_engine->entry);
+               kfree(llc_engine->name);
+               kfree(llc_engine);
+       }
+}
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
+       if (llc_engine == NULL)
+               return -ENOMEM;
+
+       llc_engine->name = kstrdup(name, GFP_KERNEL);
+       if (llc_engine->name == NULL) {
+               kfree(llc_engine);
+               return -ENOMEM;
+       }
+       llc_engine->ops = ops;
+
+       INIT_LIST_HEAD(&llc_engine->entry);
+       list_add_tail (&llc_engine->entry, &llc_engines);
+
+       return 0;
+}
+
+static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       list_for_each_entry(llc_engine, &llc_engines, entry) {
+               if (strcmp(llc_engine->name, name) == 0)
+                       return llc_engine;
+       }
+
+       return NULL;
+}
+
+void nfc_llc_unregister(const char *name)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       llc_engine = nfc_llc_name_to_engine(name);
+       if (llc_engine == NULL)
+               return;
+
+       list_del(&llc_engine->entry);
+       kfree(llc_engine->name);
+       kfree(llc_engine);
+}
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+                                xmit_to_drv_t xmit_to_drv,
+                                rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                                int tx_tailroom, llc_failure_t llc_failure)
+{
+       struct nfc_llc_engine *llc_engine;
+       struct nfc_llc *llc;
+
+       llc_engine = nfc_llc_name_to_engine(name);
+       if (llc_engine == NULL)
+               return NULL;
+
+       llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
+       if (llc == NULL)
+               return NULL;
+
+       llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
+                                         tx_headroom, tx_tailroom,
+                                         &llc->rx_headroom, &llc->rx_tailroom,
+                                         llc_failure);
+       if (llc->data == NULL) {
+               kfree(llc);
+               return NULL;
+       }
+       llc->ops = llc_engine->ops;
+
+       return llc;
+}
+
+void nfc_llc_free(struct nfc_llc *llc)
+{
+       llc->ops->deinit(llc);
+       kfree(llc);
+}
+
+inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+                                         int *rx_tailroom)
+{
+       *rx_headroom = llc->rx_headroom;
+       *rx_tailroom = llc->rx_tailroom;
+}
+
+inline int nfc_llc_start(struct nfc_llc *llc)
+{
+       return llc->ops->start(llc);
+}
+
+inline int nfc_llc_stop(struct nfc_llc *llc)
+{
+       return llc->ops->stop(llc);
+}
+
+inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       llc->ops->rcv_from_drv(llc, skb);
+}
+
+inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       return llc->ops->xmit_from_hci(llc, skb);
+}
+
+inline void *nfc_llc_get_data(struct nfc_llc *llc)
+{
+       return llc->data;
+}
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
new file mode 100644 (file)
index 0000000..7be0b7f
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Link Layer Control manager
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOCAL_LLC_H_
+#define __LOCAL_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <linux/skbuff.h>
+
+struct nfc_llc_ops {
+       void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                      rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                      int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                      llc_failure_t llc_failure);
+       void (*deinit) (struct nfc_llc *llc);
+       int (*start) (struct nfc_llc *llc);
+       int (*stop) (struct nfc_llc *llc);
+       void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+       int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+struct nfc_llc_engine {
+       const char *name;
+       struct nfc_llc_ops *ops;
+       struct list_head entry;
+};
+
+struct nfc_llc {
+       void *data;
+       struct nfc_llc_ops *ops;
+       int rx_headroom;
+       int rx_tailroom;
+};
+
+void *nfc_llc_get_data(struct nfc_llc *llc);
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+void nfc_llc_unregister(const char *name);
+
+int nfc_llc_nop_register(void);
+
+#if defined(CONFIG_NFC_SHDLC)
+int nfc_llc_shdlc_register(void);
+#else
+static inline int nfc_llc_shdlc_register(void)
+{
+       return 0;
+}
+#endif
+
+#endif /* __LOCAL_LLC_H_ */
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
new file mode 100644 (file)
index 0000000..87b1029
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * nop (passthrough) Link Layer Control
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/types.h>
+
+#include "llc.h"
+
+struct llc_nop {
+       struct nfc_hci_dev *hdev;
+       xmit_to_drv_t xmit_to_drv;
+       rcv_to_hci_t rcv_to_hci;
+       int tx_headroom;
+       int tx_tailroom;
+       llc_failure_t llc_failure;
+};
+
+static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                         rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                         int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                         llc_failure_t llc_failure)
+{
+       struct llc_nop *llc_nop;
+
+       *rx_headroom = 0;
+       *rx_tailroom = 0;
+
+       llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL);
+       if (llc_nop == NULL)
+               return NULL;
+
+       llc_nop->hdev = hdev;
+       llc_nop->xmit_to_drv = xmit_to_drv;
+       llc_nop->rcv_to_hci = rcv_to_hci;
+       llc_nop->tx_headroom = tx_headroom;
+       llc_nop->tx_tailroom = tx_tailroom;
+       llc_nop->llc_failure = llc_failure;
+
+       return llc_nop;
+}
+
+static void llc_nop_deinit(struct nfc_llc *llc)
+{
+       kfree(nfc_llc_get_data(llc));
+}
+
+static int llc_nop_start(struct nfc_llc *llc)
+{
+       return 0;
+}
+
+static int llc_nop_stop(struct nfc_llc *llc)
+{
+       return 0;
+}
+
+static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+       llc_nop->rcv_to_hci(llc_nop->hdev, skb);
+}
+
+static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+       return llc_nop->xmit_to_drv(llc_nop->hdev, skb);
+}
+
+static struct nfc_llc_ops llc_nop_ops = {
+       .init = llc_nop_init,
+       .deinit = llc_nop_deinit,
+       .start = llc_nop_start,
+       .stop = llc_nop_stop,
+       .rcv_from_drv = llc_nop_rcv_from_drv,
+       .xmit_from_hci = llc_nop_xmit_from_hci,
+};
+
+int nfc_llc_nop_register(void)
+{
+       return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops);
+}
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c
new file mode 100644 (file)
index 0000000..8f69d79
--- /dev/null
@@ -0,0 +1,857 @@
+/*
+ * shdlc Link Layer Control
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+
+#include "llc.h"
+
+enum shdlc_state {
+       SHDLC_DISCONNECTED = 0,
+       SHDLC_CONNECTING = 1,
+       SHDLC_NEGOTIATING = 2,
+       SHDLC_HALF_CONNECTED = 3,
+       SHDLC_CONNECTED = 4
+};
+
+struct llc_shdlc {
+       struct nfc_hci_dev *hdev;
+       xmit_to_drv_t xmit_to_drv;
+       rcv_to_hci_t rcv_to_hci;
+
+       struct mutex state_mutex;
+       enum shdlc_state state;
+       int hard_fault;
+
+       wait_queue_head_t *connect_wq;
+       int connect_tries;
+       int connect_result;
+       struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
+
+       u8 w;                           /* window size */
+       bool srej_support;
+
+       struct timer_list t1_timer;     /* send ack timeout */
+       bool t1_active;
+
+       struct timer_list t2_timer;     /* guard/retransmit timeout */
+       bool t2_active;
+
+       int ns;                         /* next seq num for send */
+       int nr;                         /* next expected seq num for receive */
+       int dnr;                        /* oldest sent unacked seq num */
+
+       struct sk_buff_head rcv_q;
+
+       struct sk_buff_head send_q;
+       bool rnr;                       /* other side is not ready to receive */
+
+       struct sk_buff_head ack_pending_q;
+
+       struct work_struct sm_work;
+
+       int tx_headroom;
+       int tx_tailroom;
+
+       llc_failure_t llc_failure;
+};
+
+#define SHDLC_LLC_HEAD_ROOM    2
+
+#define SHDLC_MAX_WINDOW       4
+#define SHDLC_SREJ_SUPPORT     false
+
+#define SHDLC_CONTROL_HEAD_MASK        0xe0
+#define SHDLC_CONTROL_HEAD_I   0x80
+#define SHDLC_CONTROL_HEAD_I2  0xa0
+#define SHDLC_CONTROL_HEAD_S   0xc0
+#define SHDLC_CONTROL_HEAD_U   0xe0
+
+#define SHDLC_CONTROL_NS_MASK  0x38
+#define SHDLC_CONTROL_NR_MASK  0x07
+#define SHDLC_CONTROL_TYPE_MASK        0x18
+
+#define SHDLC_CONTROL_M_MASK   0x1f
+
+enum sframe_type {
+       S_FRAME_RR = 0x00,
+       S_FRAME_REJ = 0x01,
+       S_FRAME_RNR = 0x02,
+       S_FRAME_SREJ = 0x03
+};
+
+enum uframe_modifier {
+       U_FRAME_UA = 0x06,
+       U_FRAME_RSET = 0x19
+};
+
+#define SHDLC_CONNECT_VALUE_MS 5
+#define SHDLC_T1_VALUE_MS(w)   ((5 * w) / 4)
+#define SHDLC_T2_VALUE_MS      300
+
+#define SHDLC_DUMP_SKB(info, skb)                                \
+do {                                                             \
+       pr_debug("%s:\n", info);                                  \
+       print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \
+                      16, 1, skb->data, skb->len, 0);            \
+} while (0)
+
+/* checks x < y <= z modulo 8 */
+static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
+{
+       if (x < z)
+               return ((x < y) && (y <= z)) ? true : false;
+       else
+               return ((y > x) || (y <= z)) ? true : false;
+}
+
+/* checks x <= y < z modulo 8 */
+static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
+{
+       if (x <= z)
+               return ((x <= y) && (y < z)) ? true : false;
+       else                    /* x > z -> z+8 > x */
+               return ((y >= x) || (y < z)) ? true : false;
+}
+
+static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc,
+                                          int payload_len)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM +
+                       shdlc->tx_tailroom + payload_len, GFP_KERNEL);
+       if (skb)
+               skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM);
+
+       return skb;
+}
+
+/* immediately sends an S frame. */
+static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc,
+                                 enum sframe_type sframe_type, int nr)
+{
+       int r;
+       struct sk_buff *skb;
+
+       pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
+
+       skb = llc_shdlc_alloc_skb(shdlc, 0);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
+
+       r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+
+       kfree_skb(skb);
+
+       return r;
+}
+
+/* immediately sends an U frame. skb may contain optional payload */
+static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc,
+                                 struct sk_buff *skb,
+                                 enum uframe_modifier uframe_modifier)
+{
+       int r;
+
+       pr_debug("uframe_modifier=%d\n", uframe_modifier);
+
+       *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
+
+       r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+
+       kfree_skb(skb);
+
+       return r;
+}
+
+/*
+ * Free ack_pending frames until y_nr - 1, and reset t2 according to
+ * the remaining oldest ack_pending frame sent time
+ */
+static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr)
+{
+       struct sk_buff *skb;
+       int dnr = shdlc->dnr;   /* MUST initially be < y_nr */
+
+       pr_debug("release ack pending up to frame %d excluded\n", y_nr);
+
+       while (dnr != y_nr) {
+               pr_debug("release ack pending frame %d\n", dnr);
+
+               skb = skb_dequeue(&shdlc->ack_pending_q);
+               kfree_skb(skb);
+
+               dnr = (dnr + 1) % 8;
+       }
+
+       if (skb_queue_empty(&shdlc->ack_pending_q)) {
+               if (shdlc->t2_active) {
+                       del_timer_sync(&shdlc->t2_timer);
+                       shdlc->t2_active = false;
+
+                       pr_debug
+                           ("All sent frames acked. Stopped T2(retransmit)\n");
+               }
+       } else {
+               skb = skb_peek(&shdlc->ack_pending_q);
+
+               mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb +
+                         msecs_to_jiffies(SHDLC_T2_VALUE_MS));
+               shdlc->t2_active = true;
+
+               pr_debug
+                   ("Start T2(retransmit) for remaining unacked sent frames\n");
+       }
+}
+
+/*
+ * Receive validated frames from lower layer. skb contains HCI payload only.
+ * Handle according to algorithm at spec:10.8.2
+ */
+static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
+                                 struct sk_buff *skb, int ns, int nr)
+{
+       int x_ns = ns;
+       int y_nr = nr;
+
+       pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr);
+
+       if (shdlc->state != SHDLC_CONNECTED)
+               goto exit;
+
+       if (x_ns != shdlc->nr) {
+               llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
+               goto exit;
+       }
+
+       if (shdlc->t1_active == false) {
+               shdlc->t1_active = true;
+               mod_timer(&shdlc->t1_timer, jiffies +
+                         msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
+               pr_debug("(re)Start T1(send ack)\n");
+       }
+
+       if (skb->len) {
+               shdlc->rcv_to_hci(shdlc->hdev, skb);
+               skb = NULL;
+       }
+
+       shdlc->nr = (shdlc->nr + 1) % 8;
+
+       if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+               llc_shdlc_reset_t2(shdlc, y_nr);
+
+               shdlc->dnr = y_nr;
+       }
+
+exit:
+       kfree_skb(skb);
+}
+
+static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr)
+{
+       pr_debug("remote acked up to frame %d excluded\n", y_nr);
+
+       if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+               llc_shdlc_reset_t2(shdlc, y_nr);
+               shdlc->dnr = y_nr;
+       }
+}
+
+static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc)
+{
+       struct sk_buff *skb;
+
+       pr_debug("ns reset to %d\n", shdlc->dnr);
+
+       while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
+               skb_pull(skb, 1);       /* remove control field */
+               skb_queue_head(&shdlc->send_q, skb);
+       }
+       shdlc->ns = shdlc->dnr;
+}
+
+static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
+{
+       struct sk_buff *skb;
+
+       pr_debug("remote asks retransmition from frame %d\n", y_nr);
+
+       if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
+               if (shdlc->t2_active) {
+                       del_timer_sync(&shdlc->t2_timer);
+                       shdlc->t2_active = false;
+                       pr_debug("Stopped T2(retransmit)\n");
+               }
+
+               if (shdlc->dnr != y_nr) {
+                       while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) {
+                               skb = skb_dequeue(&shdlc->ack_pending_q);
+                               kfree_skb(skb);
+                       }
+               }
+
+               llc_shdlc_requeue_ack_pending(shdlc);
+       }
+}
+
+/* See spec RR:10.8.3 REJ:10.8.4 */
+static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc,
+                                 enum sframe_type s_frame_type, int nr)
+{
+       struct sk_buff *skb;
+
+       if (shdlc->state != SHDLC_CONNECTED)
+               return;
+
+       switch (s_frame_type) {
+       case S_FRAME_RR:
+               llc_shdlc_rcv_ack(shdlc, nr);
+               if (shdlc->rnr == true) {       /* see SHDLC 10.7.7 */
+                       shdlc->rnr = false;
+                       if (shdlc->send_q.qlen == 0) {
+                               skb = llc_shdlc_alloc_skb(shdlc, 0);
+                               if (skb)
+                                       skb_queue_tail(&shdlc->send_q, skb);
+                       }
+               }
+               break;
+       case S_FRAME_REJ:
+               llc_shdlc_rcv_rej(shdlc, nr);
+               break;
+       case S_FRAME_RNR:
+               llc_shdlc_rcv_ack(shdlc, nr);
+               shdlc->rnr = true;
+               break;
+       default:
+               break;
+       }
+}
+
+static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
+{
+       pr_debug("result=%d\n", r);
+
+       del_timer_sync(&shdlc->connect_timer);
+
+       if (r == 0) {
+               shdlc->ns = 0;
+               shdlc->nr = 0;
+               shdlc->dnr = 0;
+
+               shdlc->state = SHDLC_HALF_CONNECTED;
+       } else {
+               shdlc->state = SHDLC_DISCONNECTED;
+       }
+
+       shdlc->connect_result = r;
+
+       wake_up(shdlc->connect_wq);
+}
+
+static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc)
+{
+       struct sk_buff *skb;
+
+       pr_debug("\n");
+
+       skb = llc_shdlc_alloc_skb(shdlc, 2);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       *skb_put(skb, 1) = SHDLC_MAX_WINDOW;
+       *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
+
+       return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
+}
+
+static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc)
+{
+       struct sk_buff *skb;
+
+       pr_debug("\n");
+
+       skb = llc_shdlc_alloc_skb(shdlc, 0);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
+}
+
+static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
+                                 struct sk_buff *skb,
+                                 enum uframe_modifier u_frame_modifier)
+{
+       u8 w = SHDLC_MAX_WINDOW;
+       bool srej_support = SHDLC_SREJ_SUPPORT;
+       int r;
+
+       pr_debug("u_frame_modifier=%d\n", u_frame_modifier);
+
+       switch (u_frame_modifier) {
+       case U_FRAME_RSET:
+               switch (shdlc->state) {
+               case SHDLC_NEGOTIATING:
+               case SHDLC_CONNECTING:
+                       /*
+                        * We sent RSET, but chip wants to negociate or we
+                        * got RSET before we managed to send out our.
+                        */
+                       if (skb->len > 0)
+                               w = skb->data[0];
+
+                       if (skb->len > 1)
+                               srej_support = skb->data[1] & 0x01 ? true :
+                                              false;
+
+                       if ((w <= SHDLC_MAX_WINDOW) &&
+                           (SHDLC_SREJ_SUPPORT || (srej_support == false))) {
+                               shdlc->w = w;
+                               shdlc->srej_support = srej_support;
+                               r = llc_shdlc_connect_send_ua(shdlc);
+                               llc_shdlc_connect_complete(shdlc, r);
+                       }
+                       break;
+               case SHDLC_HALF_CONNECTED:
+                       /*
+                        * Chip resent RSET due to its timeout - Ignote it
+                        * as we already sent UA.
+                        */
+                       break;
+               case SHDLC_CONNECTED:
+                       /*
+                        * Chip wants to reset link. This is unexpected and
+                        * unsupported.
+                        */
+                       shdlc->hard_fault = -ECONNRESET;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case U_FRAME_UA:
+               if ((shdlc->state == SHDLC_CONNECTING &&
+                    shdlc->connect_tries > 0) ||
+                   (shdlc->state == SHDLC_NEGOTIATING)) {
+                       llc_shdlc_connect_complete(shdlc, 0);
+                       shdlc->state = SHDLC_CONNECTED;
+               }
+               break;
+       default:
+               break;
+       }
+
+       kfree_skb(skb);
+}
+
+static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
+{
+       struct sk_buff *skb;
+       u8 control;
+       int nr;
+       int ns;
+       enum sframe_type s_frame_type;
+       enum uframe_modifier u_frame_modifier;
+
+       if (shdlc->rcv_q.qlen)
+               pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen);
+
+       while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) {
+               control = skb->data[0];
+               skb_pull(skb, 1);
+               switch (control & SHDLC_CONTROL_HEAD_MASK) {
+               case SHDLC_CONTROL_HEAD_I:
+               case SHDLC_CONTROL_HEAD_I2:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
+                       ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
+                       nr = control & SHDLC_CONTROL_NR_MASK;
+                       llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
+                       break;
+               case SHDLC_CONTROL_HEAD_S:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
+                       s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
+                       nr = control & SHDLC_CONTROL_NR_MASK;
+                       llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
+                       kfree_skb(skb);
+                       break;
+               case SHDLC_CONTROL_HEAD_U:
+                       u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
+                       llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
+                       break;
+               default:
+                       pr_err("UNKNOWN Control=%d\n", control);
+                       kfree_skb(skb);
+                       break;
+               }
+       }
+}
+
+static int llc_shdlc_w_used(int ns, int dnr)
+{
+       int unack_count;
+
+       if (dnr <= ns)
+               unack_count = ns - dnr;
+       else
+               unack_count = 8 - dnr + ns;
+
+       return unack_count;
+}
+
+/* Send frames according to algorithm at spec:10.8.1 */
+static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc)
+{
+       struct sk_buff *skb;
+       int r;
+       unsigned long time_sent;
+
+       if (shdlc->send_q.qlen)
+               pr_debug
+                   ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
+                    shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
+                    shdlc->rnr == false ? "false" : "true",
+                    shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr),
+                    shdlc->ack_pending_q.qlen);
+
+       while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
+              (shdlc->rnr == false)) {
+
+               if (shdlc->t1_active) {
+                       del_timer_sync(&shdlc->t1_timer);
+                       shdlc->t1_active = false;
+                       pr_debug("Stopped T1(send ack)\n");
+               }
+
+               skb = skb_dequeue(&shdlc->send_q);
+
+               *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) |
+                                   shdlc->nr;
+
+               pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
+                        shdlc->nr);
+               SHDLC_DUMP_SKB("shdlc frame written", skb);
+
+               r = shdlc->xmit_to_drv(shdlc->hdev, skb);
+               if (r < 0) {
+                       shdlc->hard_fault = r;
+                       break;
+               }
+
+               shdlc->ns = (shdlc->ns + 1) % 8;
+
+               time_sent = jiffies;
+               *(unsigned long *)skb->cb = time_sent;
+
+               skb_queue_tail(&shdlc->ack_pending_q, skb);
+
+               if (shdlc->t2_active == false) {
+                       shdlc->t2_active = true;
+                       mod_timer(&shdlc->t2_timer, time_sent +
+                                 msecs_to_jiffies(SHDLC_T2_VALUE_MS));
+                       pr_debug("Started T2 (retransmit)\n");
+               }
+       }
+}
+
+static void llc_shdlc_connect_timeout(unsigned long data)
+{
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+       pr_debug("\n");
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_t1_timeout(unsigned long data)
+{
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+       pr_debug("SoftIRQ: need to send ack\n");
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_t2_timeout(unsigned long data)
+{
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
+
+       pr_debug("SoftIRQ: need to retransmit\n");
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void llc_shdlc_sm_work(struct work_struct *work)
+{
+       struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work);
+       int r;
+
+       pr_debug("\n");
+
+       mutex_lock(&shdlc->state_mutex);
+
+       switch (shdlc->state) {
+       case SHDLC_DISCONNECTED:
+               skb_queue_purge(&shdlc->rcv_q);
+               skb_queue_purge(&shdlc->send_q);
+               skb_queue_purge(&shdlc->ack_pending_q);
+               break;
+       case SHDLC_CONNECTING:
+               if (shdlc->hard_fault) {
+                       llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+                       break;
+               }
+
+               if (shdlc->connect_tries++ < 5)
+                       r = llc_shdlc_connect_initiate(shdlc);
+               else
+                       r = -ETIME;
+               if (r < 0)
+                       llc_shdlc_connect_complete(shdlc, r);
+               else {
+                       mod_timer(&shdlc->connect_timer, jiffies +
+                                 msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
+
+                       shdlc->state = SHDLC_NEGOTIATING;
+               }
+               break;
+       case SHDLC_NEGOTIATING:
+               if (timer_pending(&shdlc->connect_timer) == 0) {
+                       shdlc->state = SHDLC_CONNECTING;
+                       queue_work(system_nrt_wq, &shdlc->sm_work);
+               }
+
+               llc_shdlc_handle_rcv_queue(shdlc);
+
+               if (shdlc->hard_fault) {
+                       llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+                       break;
+               }
+               break;
+       case SHDLC_HALF_CONNECTED:
+       case SHDLC_CONNECTED:
+               llc_shdlc_handle_rcv_queue(shdlc);
+               llc_shdlc_handle_send_queue(shdlc);
+
+               if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
+                       pr_debug
+                           ("Handle T1(send ack) elapsed (T1 now inactive)\n");
+
+                       shdlc->t1_active = false;
+                       r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
+                                                  shdlc->nr);
+                       if (r < 0)
+                               shdlc->hard_fault = r;
+               }
+
+               if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) {
+                       pr_debug
+                           ("Handle T2(retransmit) elapsed (T2 inactive)\n");
+
+                       shdlc->t2_active = false;
+
+                       llc_shdlc_requeue_ack_pending(shdlc);
+                       llc_shdlc_handle_send_queue(shdlc);
+               }
+
+               if (shdlc->hard_fault) {
+                       shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
+               }
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&shdlc->state_mutex);
+}
+
+/*
+ * Called from syscall context to establish shdlc link. Sleeps until
+ * link is ready or failure.
+ */
+static int llc_shdlc_connect(struct llc_shdlc *shdlc)
+{
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
+
+       pr_debug("\n");
+
+       mutex_lock(&shdlc->state_mutex);
+
+       shdlc->state = SHDLC_CONNECTING;
+       shdlc->connect_wq = &connect_wq;
+       shdlc->connect_tries = 0;
+       shdlc->connect_result = 1;
+
+       mutex_unlock(&shdlc->state_mutex);
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+
+       wait_event(connect_wq, shdlc->connect_result != 1);
+
+       return shdlc->connect_result;
+}
+
+static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
+{
+       pr_debug("\n");
+
+       mutex_lock(&shdlc->state_mutex);
+
+       shdlc->state = SHDLC_DISCONNECTED;
+
+       mutex_unlock(&shdlc->state_mutex);
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+/*
+ * Receive an incoming shdlc frame. Frame has already been crc-validated.
+ * skb contains only LLC header and payload.
+ * If skb == NULL, it is a notification that the link below is dead.
+ */
+static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
+{
+       if (skb == NULL) {
+               pr_err("NULL Frame -> link is dead\n");
+               shdlc->hard_fault = -EREMOTEIO;
+       } else {
+               SHDLC_DUMP_SKB("incoming frame", skb);
+               skb_queue_tail(&shdlc->rcv_q, skb);
+       }
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+}
+
+static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                           rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                           int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                           llc_failure_t llc_failure)
+{
+       struct llc_shdlc *shdlc;
+
+       *rx_headroom = SHDLC_LLC_HEAD_ROOM;
+       *rx_tailroom = 0;
+
+       shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL);
+       if (shdlc == NULL)
+               return NULL;
+
+       mutex_init(&shdlc->state_mutex);
+       shdlc->state = SHDLC_DISCONNECTED;
+
+       init_timer(&shdlc->connect_timer);
+       shdlc->connect_timer.data = (unsigned long)shdlc;
+       shdlc->connect_timer.function = llc_shdlc_connect_timeout;
+
+       init_timer(&shdlc->t1_timer);
+       shdlc->t1_timer.data = (unsigned long)shdlc;
+       shdlc->t1_timer.function = llc_shdlc_t1_timeout;
+
+       init_timer(&shdlc->t2_timer);
+       shdlc->t2_timer.data = (unsigned long)shdlc;
+       shdlc->t2_timer.function = llc_shdlc_t2_timeout;
+
+       shdlc->w = SHDLC_MAX_WINDOW;
+       shdlc->srej_support = SHDLC_SREJ_SUPPORT;
+
+       skb_queue_head_init(&shdlc->rcv_q);
+       skb_queue_head_init(&shdlc->send_q);
+       skb_queue_head_init(&shdlc->ack_pending_q);
+
+       INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work);
+
+       shdlc->hdev = hdev;
+       shdlc->xmit_to_drv = xmit_to_drv;
+       shdlc->rcv_to_hci = rcv_to_hci;
+       shdlc->tx_headroom = tx_headroom;
+       shdlc->tx_tailroom = tx_tailroom;
+       shdlc->llc_failure = llc_failure;
+
+       return shdlc;
+}
+
+static void llc_shdlc_deinit(struct nfc_llc *llc)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       skb_queue_purge(&shdlc->rcv_q);
+       skb_queue_purge(&shdlc->send_q);
+       skb_queue_purge(&shdlc->ack_pending_q);
+
+       kfree(shdlc);
+}
+
+static int llc_shdlc_start(struct nfc_llc *llc)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       return llc_shdlc_connect(shdlc);
+}
+
+static int llc_shdlc_stop(struct nfc_llc *llc)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       llc_shdlc_disconnect(shdlc);
+
+       return 0;
+}
+
+static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       llc_shdlc_recv_frame(shdlc, skb);
+}
+
+static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       skb_queue_tail(&shdlc->send_q, skb);
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+
+       return 0;
+}
+
+static struct nfc_llc_ops llc_shdlc_ops = {
+       .init = llc_shdlc_init,
+       .deinit = llc_shdlc_deinit,
+       .start = llc_shdlc_start,
+       .stop = llc_shdlc_stop,
+       .rcv_from_drv = llc_shdlc_rcv_from_drv,
+       .xmit_from_hci = llc_shdlc_xmit_from_hci,
+};
+
+int nfc_llc_shdlc_register(void)
+{
+       return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
+}
index b982b5b..7941535 100644 (file)
@@ -261,7 +261,6 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
        struct sk_buff *skb;
        struct nfc_dev *dev;
        struct nfc_llcp_local *local;
-       u16 size = 0;
 
        pr_debug("Sending DISC\n");
 
@@ -273,17 +272,10 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
        if (dev == NULL)
                return -ENODEV;
 
-       size += LLCP_HEADER_SIZE;
-       size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
-
-       skb = alloc_skb(size, GFP_ATOMIC);
+       skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
        if (skb == NULL)
                return -ENOMEM;
 
-       skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
-
-       skb = llcp_add_header(skb, sock->dsap, sock->ssap, LLCP_PDU_DISC);
-
        skb_queue_tail(&local->tx_queue, skb);
 
        return 0;
@@ -312,6 +304,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+
        return nfc_data_exchange(dev, local->target_idx, skb,
                                 nfc_llcp_recv, local);
 }
@@ -426,6 +420,52 @@ error_tlv:
        return err;
 }
 
+int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap)
+{
+       struct sk_buff *skb;
+       struct nfc_dev *dev;
+       u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2];
+       u16 size = 0;
+
+       pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap);
+
+       if (local == NULL)
+               return -ENODEV;
+
+       dev = local->dev;
+       if (dev == NULL)
+               return -ENODEV;
+
+       sdres[0] = tid;
+       sdres[1] = sap;
+       sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0,
+                                      &sdres_tlv_length);
+       if (sdres_tlv == NULL)
+               return -ENOMEM;
+
+       size += LLCP_HEADER_SIZE;
+       size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
+       size += sdres_tlv_length;
+
+       skb = alloc_skb(size, GFP_KERNEL);
+       if (skb == NULL) {
+               kfree(sdres_tlv);
+               return -ENOMEM;
+       }
+
+       skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
+
+       skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);
+
+       memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length);
+
+       skb_queue_tail(&local->tx_queue, skb);
+
+       kfree(sdres_tlv);
+
+       return 0;
+}
+
 int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
 {
        struct sk_buff *skb;
@@ -539,6 +579,52 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
        return len;
 }
 
+int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
+                          struct msghdr *msg, size_t len)
+{
+       struct sk_buff *pdu;
+       struct nfc_llcp_local *local;
+       size_t frag_len = 0, remaining_len;
+       u8 *msg_ptr;
+       int err;
+
+       pr_debug("Send UI frame len %zd\n", len);
+
+       local = sock->local;
+       if (local == NULL)
+               return -ENODEV;
+
+       remaining_len = len;
+       msg_ptr = (u8 *) msg->msg_iov;
+
+       while (remaining_len > 0) {
+
+               frag_len = min_t(size_t, sock->miu, remaining_len);
+
+               pr_debug("Fragment %zd bytes remaining %zd",
+                        frag_len, remaining_len);
+
+               pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT,
+                                        frag_len + LLCP_HEADER_SIZE, &err);
+               if (pdu == NULL) {
+                       pr_err("Could not allocate PDU\n");
+                       continue;
+               }
+
+               pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI);
+
+               memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
+
+               /* No need to check for the peer RW for UI frames */
+               skb_queue_tail(&local->tx_queue, pdu);
+
+               remaining_len -= frag_len;
+               msg_ptr += frag_len;
+       }
+
+       return len;
+}
+
 int nfc_llcp_send_rr(struct nfc_llcp_sock *sock)
 {
        struct sk_buff *skb;
index 82f0f75..6ae2e68 100644 (file)
@@ -56,7 +56,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
        sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
                llcp_sock = nfc_llcp_sock(sk);
 
-               lock_sock(sk);
+               bh_lock_sock(sk);
 
                if (sk->sk_state == LLCP_CONNECTED)
                        nfc_put_device(llcp_sock->dev);
@@ -68,26 +68,36 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
                        list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
                                                 accept_queue) {
                                accept_sk = &lsk->sk;
-                               lock_sock(accept_sk);
+                               bh_lock_sock(accept_sk);
 
                                nfc_llcp_accept_unlink(accept_sk);
 
                                accept_sk->sk_state = LLCP_CLOSED;
 
-                               release_sock(accept_sk);
+                               bh_unlock_sock(accept_sk);
 
                                sock_orphan(accept_sk);
                        }
 
                        if (listen == true) {
-                               release_sock(sk);
+                               bh_unlock_sock(sk);
                                continue;
                        }
                }
 
+               /*
+                * If we have a connection less socket bound, we keep it alive
+                * if the device is still present.
+                */
+               if (sk->sk_state == LLCP_BOUND && sk->sk_type == SOCK_DGRAM &&
+                   listen == true) {
+                       bh_unlock_sock(sk);
+                       continue;
+               }
+
                sk->sk_state = LLCP_CLOSED;
 
-               release_sock(sk);
+               bh_unlock_sock(sk);
 
                sock_orphan(sk);
 
@@ -114,9 +124,9 @@ static void local_release(struct kref *ref)
        nfc_llcp_socket_release(local, false);
        del_timer_sync(&local->link_timer);
        skb_queue_purge(&local->tx_queue);
-       destroy_workqueue(local->tx_wq);
-       destroy_workqueue(local->rx_wq);
-       destroy_workqueue(local->timeout_wq);
+       cancel_work_sync(&local->tx_work);
+       cancel_work_sync(&local->rx_work);
+       cancel_work_sync(&local->timeout_work);
        kfree_skb(local->rx_pending);
        kfree(local);
 }
@@ -134,7 +144,7 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
 {
        struct sock *sk;
        struct hlist_node *node;
-       struct nfc_llcp_sock *llcp_sock;
+       struct nfc_llcp_sock *llcp_sock, *tmp_sock;
 
        pr_debug("ssap dsap %d %d\n", ssap, dsap);
 
@@ -146,10 +156,12 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
        llcp_sock = NULL;
 
        sk_for_each(sk, node, &local->sockets.head) {
-               llcp_sock = nfc_llcp_sock(sk);
+               tmp_sock = nfc_llcp_sock(sk);
 
-               if (llcp_sock->ssap == ssap && llcp_sock->dsap == dsap)
+               if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) {
+                       llcp_sock = tmp_sock;
                        break;
+               }
        }
 
        read_unlock(&local->sockets.lock);
@@ -181,7 +193,7 @@ static void nfc_llcp_symm_timer(unsigned long data)
 
        pr_err("SYMM timeout\n");
 
-       queue_work(local->timeout_wq, &local->timeout_work);
+       queue_work(system_nrt_wq, &local->timeout_work);
 }
 
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
@@ -249,7 +261,12 @@ struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
 
                pr_debug("llcp sock %p\n", tmp_sock);
 
-               if (tmp_sock->sk.sk_state != LLCP_LISTEN)
+               if (tmp_sock->sk.sk_type == SOCK_STREAM &&
+                   tmp_sock->sk.sk_state != LLCP_LISTEN)
+                       continue;
+
+               if (tmp_sock->sk.sk_type == SOCK_DGRAM &&
+                   tmp_sock->sk.sk_state != LLCP_BOUND)
                        continue;
 
                if (tmp_sock->service_name == NULL ||
@@ -426,6 +443,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        u8 *miux_tlv, miux_length;
        __be16 miux;
        u8 gb_len = 0;
+       int ret = 0;
 
        version = LLCP_VERSION_11;
        version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
@@ -450,8 +468,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        gb_len += ARRAY_SIZE(llcp_magic);
 
        if (gb_len > NFC_MAX_GT_LEN) {
-               kfree(version_tlv);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        gb_cur = local->gb;
@@ -471,12 +489,15 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        memcpy(gb_cur, miux_tlv, miux_length);
        gb_cur += miux_length;
 
+       local->gb_len = gb_len;
+
+out:
        kfree(version_tlv);
        kfree(lto_tlv);
+       kfree(wks_tlv);
+       kfree(miux_tlv);
 
-       local->gb_len = gb_len;
-
-       return 0;
+       return ret;
 }
 
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
@@ -554,6 +575,46 @@ static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
        sock->recv_ack_n = (sock->recv_n - 1) % 16;
 }
 
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+                              struct sk_buff *skb, u8 direction)
+{
+       struct hlist_node *node;
+       struct sk_buff *skb_copy = NULL, *nskb;
+       struct sock *sk;
+       u8 *data;
+
+       read_lock(&local->raw_sockets.lock);
+
+       sk_for_each(sk, node, &local->raw_sockets.head) {
+               if (sk->sk_state != LLCP_BOUND)
+                       continue;
+
+               if (skb_copy == NULL) {
+                       skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
+                                              GFP_ATOMIC);
+
+                       if (skb_copy == NULL)
+                               continue;
+
+                       data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+
+                       data[0] = local->dev ? local->dev->idx : 0xFF;
+                       data[1] = direction;
+               }
+
+               nskb = skb_clone(skb_copy, GFP_ATOMIC);
+               if (!nskb)
+                       continue;
+
+               if (sock_queue_rcv_skb(sk, nskb))
+                       kfree_skb(nskb);
+       }
+
+       read_unlock(&local->raw_sockets.lock);
+
+       kfree_skb(skb_copy);
+}
+
 static void nfc_llcp_tx_work(struct work_struct *work)
 {
        struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
@@ -566,7 +627,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
        if (skb != NULL) {
                sk = skb->sk;
                llcp_sock = nfc_llcp_sock(sk);
-               if (llcp_sock != NULL) {
+
+               if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+                       nfc_llcp_send_symm(local->dev);
+               } else {
                        int ret;
 
                        pr_debug("Sending pending skb\n");
@@ -574,6 +638,9 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       nfc_llcp_send_to_raw_sock(local, skb,
+                                                 NFC_LLCP_DIRECTION_TX);
+
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
 
@@ -582,8 +649,6 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                skb_queue_tail(&llcp_sock->tx_pending_queue,
                                               skb);
                        }
-               } else {
-                       nfc_llcp_send_symm(local->dev);
                }
        } else {
                nfc_llcp_send_symm(local->dev);
@@ -657,6 +722,39 @@ static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len)
        return NULL;
 }
 
+static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
+                            struct sk_buff *skb)
+{
+       struct nfc_llcp_sock *llcp_sock;
+       struct nfc_llcp_ui_cb *ui_cb;
+       u8 dsap, ssap;
+
+       dsap = nfc_llcp_dsap(skb);
+       ssap = nfc_llcp_ssap(skb);
+
+       ui_cb = nfc_llcp_ui_skb_cb(skb);
+       ui_cb->dsap = dsap;
+       ui_cb->ssap = ssap;
+
+       printk("%s %d %d\n", __func__, dsap, ssap);
+
+       pr_debug("%d %d\n", dsap, ssap);
+
+       /* We're looking for a bound socket, not a client one */
+       llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
+       if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
+               return;
+
+       /* There is no sequence with UI frames */
+       skb_pull(skb, LLCP_HEADER_SIZE);
+       if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+               pr_err("receive queue is full\n");
+               skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+       }
+
+       nfc_llcp_sock_put(llcp_sock);
+}
+
 static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
                                  struct sk_buff *skb)
 {
@@ -776,9 +874,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
 fail:
        /* Send DM */
        nfc_llcp_send_dm(local, dsap, ssap, reason);
-
-       return;
-
 }
 
 int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
@@ -980,7 +1075,7 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
        }
 
        if (llcp_sock == NULL) {
-               pr_err("Invalid DM\n");
+               pr_debug("Already closed\n");
                return;
        }
 
@@ -991,8 +1086,100 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
        sk->sk_state_change(sk);
 
        nfc_llcp_sock_put(llcp_sock);
+}
 
-       return;
+static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
+                             struct sk_buff *skb)
+{
+       struct nfc_llcp_sock *llcp_sock;
+       u8 dsap, ssap, *tlv, type, length, tid, sap;
+       u16 tlv_len, offset;
+       char *service_name;
+       size_t service_name_len;
+
+       dsap = nfc_llcp_dsap(skb);
+       ssap = nfc_llcp_ssap(skb);
+
+       pr_debug("%d %d\n", dsap, ssap);
+
+       if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) {
+               pr_err("Wrong SNL SAP\n");
+               return;
+       }
+
+       tlv = &skb->data[LLCP_HEADER_SIZE];
+       tlv_len = skb->len - LLCP_HEADER_SIZE;
+       offset = 0;
+
+       while(offset < tlv_len) {
+               type = tlv[0];
+               length = tlv[1];
+
+               switch (type) {
+               case LLCP_TLV_SDREQ:
+                       tid = tlv[2];
+                       service_name = (char *) &tlv[3];
+                       service_name_len = length - 1;
+
+                       pr_debug("Looking for %.16s\n", service_name);
+
+                       if (service_name_len == strlen("urn:nfc:sn:sdp") &&
+                           !strncmp(service_name, "urn:nfc:sn:sdp",
+                                    service_name_len)) {
+                               sap = 1;
+                               goto send_snl;
+                       }
+
+                       llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
+                                                         service_name_len);
+                       if (!llcp_sock) {
+                               sap = 0;
+                               goto send_snl;
+                       }
+
+                       /*
+                        * We found a socket but its ssap has not been reserved
+                        * yet. We need to assign it for good and send a reply.
+                        * The ssap will be freed when the socket is closed.
+                        */
+                       if (llcp_sock->ssap == LLCP_SDP_UNBOUND) {
+                               atomic_t *client_count;
+
+                               sap = nfc_llcp_reserve_sdp_ssap(local);
+
+                               pr_debug("Reserving %d\n", sap);
+
+                               if (sap == LLCP_SAP_MAX) {
+                                       sap = 0;
+                                       goto send_snl;
+                               }
+
+                               client_count =
+                                       &local->local_sdp_cnt[sap -
+                                                             LLCP_WKS_NUM_SAP];
+
+                               atomic_inc(client_count);
+
+                               llcp_sock->ssap = sap;
+                               llcp_sock->reserved_ssap = sap;
+                       } else {
+                               sap = llcp_sock->ssap;
+                       }
+
+                       pr_debug("%p %d\n", llcp_sock, sap);
+
+               send_snl:
+                       nfc_llcp_send_snl(local, tid, sap);
+                       break;
+
+               default:
+                       pr_err("Invalid SNL tlv value 0x%x\n", type);
+                       break;
+               }
+
+               offset += length + 2;
+               tlv += length + 2;
+       }
 }
 
 static void nfc_llcp_rx_work(struct work_struct *work)
@@ -1018,11 +1205,18 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
                               16, 1, skb->data, skb->len, true);
 
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+
        switch (ptype) {
        case LLCP_PDU_SYMM:
                pr_debug("SYMM\n");
                break;
 
+       case LLCP_PDU_UI:
+               pr_debug("UI\n");
+               nfc_llcp_recv_ui(local, skb);
+               break;
+
        case LLCP_PDU_CONNECT:
                pr_debug("CONNECT\n");
                nfc_llcp_recv_connect(local, skb);
@@ -1043,6 +1237,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                nfc_llcp_recv_dm(local, skb);
                break;
 
+       case LLCP_PDU_SNL:
+               pr_debug("SNL\n");
+               nfc_llcp_recv_snl(local, skb);
+               break;
+
        case LLCP_PDU_I:
        case LLCP_PDU_RR:
        case LLCP_PDU_RNR:
@@ -1052,11 +1251,9 @@ static void nfc_llcp_rx_work(struct work_struct *work)
 
        }
 
-       queue_work(local->tx_wq, &local->tx_work);
+       queue_work(system_nrt_wq, &local->tx_work);
        kfree_skb(local->rx_pending);
        local->rx_pending = NULL;
-
-       return;
 }
 
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
@@ -1071,9 +1268,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
 
        local->rx_pending = skb_get(skb);
        del_timer(&local->link_timer);
-       queue_work(local->rx_wq, &local->rx_work);
-
-       return;
+       queue_work(system_nrt_wq, &local->rx_work);
 }
 
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
@@ -1086,7 +1281,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
 
        local->rx_pending = skb_get(skb);
        del_timer(&local->link_timer);
-       queue_work(local->rx_wq, &local->rx_work);
+       queue_work(system_nrt_wq, &local->rx_work);
 
        return 0;
 }
@@ -1121,7 +1316,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
        if (rf_mode == NFC_RF_INITIATOR) {
                pr_debug("Queueing Tx work\n");
 
-               queue_work(local->tx_wq, &local->tx_work);
+               queue_work(system_nrt_wq, &local->tx_work);
        } else {
                mod_timer(&local->link_timer,
                          jiffies + msecs_to_jiffies(local->remote_lto));
@@ -1130,10 +1325,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
 
 int nfc_llcp_register_device(struct nfc_dev *ndev)
 {
-       struct device *dev = &ndev->dev;
        struct nfc_llcp_local *local;
-       char name[32];
-       int err;
 
        local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
        if (local == NULL)
@@ -1149,41 +1341,15 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
 
        skb_queue_head_init(&local->tx_queue);
        INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
-       snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev));
-       local->tx_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->tx_wq == NULL) {
-               err = -ENOMEM;
-               goto err_local;
-       }
 
        local->rx_pending = NULL;
        INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
-       snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev));
-       local->rx_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->rx_wq == NULL) {
-               err = -ENOMEM;
-               goto err_tx_wq;
-       }
 
        INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
-       snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev));
-       local->timeout_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->timeout_wq == NULL) {
-               err = -ENOMEM;
-               goto err_rx_wq;
-       }
 
-       local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
-       local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
+       rwlock_init(&local->sockets.lock);
+       rwlock_init(&local->connecting_sockets.lock);
+       rwlock_init(&local->raw_sockets.lock);
 
        nfc_llcp_build_gb(local);
 
@@ -1193,17 +1359,6 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        list_add(&llcp_devices, &local->list);
 
        return 0;
-
-err_rx_wq:
-       destroy_workqueue(local->rx_wq);
-
-err_tx_wq:
-       destroy_workqueue(local->tx_wq);
-
-err_local:
-       kfree(local);
-
-       return 0;
 }
 
 void nfc_llcp_unregister_device(struct nfc_dev *dev)
index 83b8bba..276da3a 100644 (file)
@@ -56,12 +56,9 @@ struct nfc_llcp_local {
 
        struct timer_list link_timer;
        struct sk_buff_head tx_queue;
-       struct workqueue_struct *tx_wq;
        struct work_struct       tx_work;
-       struct workqueue_struct *rx_wq;
        struct work_struct       rx_work;
        struct sk_buff *rx_pending;
-       struct workqueue_struct *timeout_wq;
        struct work_struct       timeout_work;
 
        u32 target_idx;
@@ -89,6 +86,7 @@ struct nfc_llcp_local {
        /* sockets array */
        struct llcp_sock_list sockets;
        struct llcp_sock_list connecting_sockets;
+       struct llcp_sock_list raw_sockets;
 };
 
 struct nfc_llcp_sock {
@@ -126,6 +124,13 @@ struct nfc_llcp_sock {
        struct sock *parent;
 };
 
+struct nfc_llcp_ui_cb {
+       __u8 dsap;
+       __u8 ssap;
+};
+
+#define nfc_llcp_ui_skb_cb(__skb) ((struct nfc_llcp_ui_cb *)&((__skb)->cb[0]))
+
 #define nfc_llcp_sock(sk) ((struct nfc_llcp_sock *) (sk))
 #define nfc_llcp_dev(sk)  (nfc_llcp_sock((sk))->dev)
 
@@ -187,6 +192,8 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
 u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
 void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap);
 int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock);
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+                              struct sk_buff *skb, u8 direction);
 
 /* Sock API */
 struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp);
@@ -209,10 +216,13 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
+int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap);
 int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason);
 int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
                          struct msghdr *msg, size_t len);
+int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
+                          struct msghdr *msg, size_t len);
 int nfc_llcp_send_rr(struct nfc_llcp_sock *sock);
 
 /* Socket API */
index 03e608c..b2b1c9f 100644 (file)
@@ -142,6 +142,60 @@ error:
        return ret;
 }
 
+static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
+                             int alen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       struct nfc_llcp_local *local;
+       struct nfc_dev *dev;
+       struct sockaddr_nfc_llcp llcp_addr;
+       int len, ret = 0;
+
+       if (!addr || addr->sa_family != AF_NFC)
+               return -EINVAL;
+
+       pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
+
+       memset(&llcp_addr, 0, sizeof(llcp_addr));
+       len = min_t(unsigned int, sizeof(llcp_addr), alen);
+       memcpy(&llcp_addr, addr, len);
+
+       lock_sock(sk);
+
+       if (sk->sk_state != LLCP_CLOSED) {
+               ret = -EBADFD;
+               goto error;
+       }
+
+       dev = nfc_get_device(llcp_addr.dev_idx);
+       if (dev == NULL) {
+               ret = -ENODEV;
+               goto error;
+       }
+
+       local = nfc_llcp_find_local(dev);
+       if (local == NULL) {
+               ret = -ENODEV;
+               goto put_dev;
+       }
+
+       llcp_sock->dev = dev;
+       llcp_sock->local = nfc_llcp_local_get(local);
+       llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
+
+       nfc_llcp_sock_link(&local->raw_sockets, sk);
+
+       sk->sk_state = LLCP_BOUND;
+
+put_dev:
+       nfc_put_device(dev);
+
+error:
+       release_sock(sk);
+       return ret;
+}
+
 static int llcp_sock_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = sock->sk;
@@ -151,8 +205,8 @@ static int llcp_sock_listen(struct socket *sock, int backlog)
 
        lock_sock(sk);
 
-       if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM)
-           || sk->sk_state != LLCP_BOUND) {
+       if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) ||
+           sk->sk_state != LLCP_BOUND) {
                ret = -EBADFD;
                goto error;
        }
@@ -301,6 +355,7 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
                 llcp_sock->dsap, llcp_sock->ssap);
 
        uaddr->sa_family = AF_NFC;
+
        *len = sizeof(struct sockaddr_nfc_llcp);
 
        llcp_addr->dev_idx = llcp_sock->dev->idx;
@@ -417,7 +472,10 @@ static int llcp_sock_release(struct socket *sock)
 
        release_sock(sk);
 
-       nfc_llcp_sock_unlink(&local->sockets, sk);
+       if (sock->type == SOCK_RAW)
+               nfc_llcp_sock_unlink(&local->raw_sockets, sk);
+       else
+               nfc_llcp_sock_unlink(&local->sockets, sk);
 
 out:
        sock_orphan(sk);
@@ -554,6 +612,25 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        lock_sock(sk);
 
+       if (sk->sk_type == SOCK_DGRAM) {
+               struct sockaddr_nfc_llcp *addr =
+                       (struct sockaddr_nfc_llcp *)msg->msg_name;
+
+               if (msg->msg_namelen < sizeof(*addr)) {
+                       release_sock(sk);
+
+                       pr_err("Invalid socket address length %d\n",
+                              msg->msg_namelen);
+
+                       return -EINVAL;
+               }
+
+               release_sock(sk);
+
+               return nfc_llcp_send_ui_frame(llcp_sock, addr->dsap, addr->ssap,
+                                             msg, len);
+       }
+
        if (sk->sk_state != LLCP_CONNECTED) {
                release_sock(sk);
                return -ENOTCONN;
@@ -609,11 +686,28 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                return -EFAULT;
        }
 
+       if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {
+               struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb);
+               struct sockaddr_nfc_llcp sockaddr;
+
+               pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
+
+               sockaddr.sa_family = AF_NFC;
+               sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP;
+               sockaddr.dsap = ui_cb->dsap;
+               sockaddr.ssap = ui_cb->ssap;
+
+               memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr));
+               msg->msg_namelen = sizeof(sockaddr);
+       }
+
        /* Mark read part of skb as used */
        if (!(flags & MSG_PEEK)) {
 
                /* SOCK_STREAM: re-queue skb if it contains unreceived data */
-               if (sk->sk_type == SOCK_STREAM) {
+               if (sk->sk_type == SOCK_STREAM ||
+                   sk->sk_type == SOCK_DGRAM ||
+                   sk->sk_type == SOCK_RAW) {
                        skb_pull(skb, copied);
                        if (skb->len) {
                                skb_queue_head(&sk->sk_receive_queue, skb);
@@ -654,6 +748,26 @@ static const struct proto_ops llcp_sock_ops = {
        .mmap           = sock_no_mmap,
 };
 
+static const struct proto_ops llcp_rawsock_ops = {
+       .family         = PF_NFC,
+       .owner          = THIS_MODULE,
+       .bind           = llcp_raw_sock_bind,
+       .connect        = sock_no_connect,
+       .release        = llcp_sock_release,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = llcp_sock_getname,
+       .poll           = llcp_sock_poll,
+       .ioctl          = sock_no_ioctl,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = llcp_sock_recvmsg,
+       .mmap           = sock_no_mmap,
+};
+
 static void llcp_sock_destruct(struct sock *sk)
 {
        struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -731,10 +845,15 @@ static int llcp_sock_create(struct net *net, struct socket *sock,
 
        pr_debug("%p\n", sock);
 
-       if (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM)
+       if (sock->type != SOCK_STREAM &&
+           sock->type != SOCK_DGRAM &&
+           sock->type != SOCK_RAW)
                return -ESOCKTNOSUPPORT;
 
-       sock->ops = &llcp_sock_ops;
+       if (sock->type == SOCK_RAW)
+               sock->ops = &llcp_rawsock_ops;
+       else
+               sock->ops = &llcp_sock_ops;
 
        sk = nfc_llcp_sock_alloc(sock, sock->type, GFP_ATOMIC);
        if (sk == NULL)
index 9195b3d..f8dd44d 100644 (file)
@@ -175,6 +175,27 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
                     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
 }
 
+struct nci_set_config_param {
+       __u8    id;
+       size_t  len;
+       __u8    *val;
+};
+
+static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
+{
+       struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
+       struct nci_core_set_config_cmd cmd;
+
+       BUG_ON(param->len > NCI_MAX_PARAM_LEN);
+
+       cmd.num_params = 1;
+       cmd.param.id = param->id;
+       cmd.param.len = param->len;
+       memcpy(cmd.param.val, param->val, param->len);
+
+       nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
+}
+
 static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
 {
        struct nci_rf_disc_cmd cmd;
@@ -183,10 +204,10 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
        cmd.num_disc_configs = 0;
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_JEWEL_MASK
-            || protocols & NFC_PROTO_MIFARE_MASK
-            || protocols & NFC_PROTO_ISO14443_MASK
-            || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+           (protocols & NFC_PROTO_JEWEL_MASK ||
+            protocols & NFC_PROTO_MIFARE_MASK ||
+            protocols & NFC_PROTO_ISO14443_MASK ||
+            protocols & NFC_PROTO_NFC_DEP_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_A_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -202,8 +223,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
        }
 
        if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
-           (protocols & NFC_PROTO_FELICA_MASK
-            || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+           (protocols & NFC_PROTO_FELICA_MASK ||
+            protocols & NFC_PROTO_NFC_DEP_MASK)) {
                cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
                        NCI_NFC_F_PASSIVE_POLL_MODE;
                cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -387,6 +408,30 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
        return nci_close_device(ndev);
 }
 
+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, &param.len);
+       if ((param.val == NULL) || (param.len == 0))
+               return 0;
+
+       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)&param,
+                          msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+}
+
 static int nci_start_poll(struct nfc_dev *nfc_dev,
                          __u32 im_protocols, __u32 tm_protocols)
 {
@@ -414,6 +459,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
                        return -EBUSY;
        }
 
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               rc = nci_set_local_general_bytes(nfc_dev);
+               if (rc) {
+                       pr_err("failed to set local general bytes\n");
+                       return rc;
+               }
+       }
+
        rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
                         msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
@@ -508,7 +561,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 
-       pr_debug("target_idx %d\n", target->idx);
+       pr_debug("entry\n");
 
        if (!ndev->target_active_prot) {
                pr_err("unable to deactivate target, no active target\n");
@@ -523,6 +576,38 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
        }
 }
 
+
+static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                          __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
+
+       rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
+       if (rc)
+               return rc;
+
+       rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
+                                         ndev->remote_gb_len);
+       if (!rc)
+               rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
+                                       NFC_RF_INITIATOR);
+
+       return rc;
+}
+
+static int nci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       pr_debug("entry\n");
+
+       nci_deactivate_target(nfc_dev, NULL);
+
+       return 0;
+}
+
+
 static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
                          struct sk_buff *skb,
                          data_exchange_cb_t cb, void *cb_context)
@@ -556,6 +641,8 @@ static struct nfc_ops nci_nfc_ops = {
        .dev_down = nci_dev_down,
        .start_poll = nci_start_poll,
        .stop_poll = nci_stop_poll,
+       .dep_link_up = nci_dep_link_up,
+       .dep_link_down = nci_dep_link_down,
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
@@ -716,8 +803,8 @@ int nci_recv_frame(struct sk_buff *skb)
 
        pr_debug("len %d\n", skb->len);
 
-       if (!ndev || (!test_bit(NCI_UP, &ndev->flags)
-                     && !test_bit(NCI_INIT, &ndev->flags))) {
+       if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
+           !test_bit(NCI_INIT, &ndev->flags))) {
                kfree_skb(skb);
                return -ENXIO;
        }
index af7a93b..b2aa98e 100644 (file)
@@ -176,6 +176,8 @@ static int nci_add_new_protocol(struct nci_dev *ndev,
                        protocol = NFC_PROTO_ISO14443_B_MASK;
        else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
                protocol = NFC_PROTO_FELICA_MASK;
+       else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP)
+               protocol = NFC_PROTO_NFC_DEP_MASK;
        else
                protocol = 0;
 
@@ -361,6 +363,33 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
        return NCI_STATUS_OK;
 }
 
+static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
+                       struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+{
+       struct activation_params_poll_nfc_dep *poll;
+       int i;
+
+       switch (ntf->activation_rf_tech_and_mode) {
+       case NCI_NFC_A_PASSIVE_POLL_MODE:
+       case NCI_NFC_F_PASSIVE_POLL_MODE:
+               poll = &ntf->activation_params.poll_nfc_dep;
+               poll->atr_res_len = min_t(__u8, *data++, 63);
+               pr_debug("atr_res_len %d\n", poll->atr_res_len);
+               if (poll->atr_res_len > 0) {
+                       for (i = 0; i < poll->atr_res_len; i++)
+                               poll->atr_res[poll->atr_res_len-1-i] = data[i];
+               }
+               break;
+
+       default:
+               pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+                      ntf->activation_rf_tech_and_mode);
+               return NCI_STATUS_RF_PROTOCOL_ERROR;
+       }
+
+       return NCI_STATUS_OK;
+}
+
 static void nci_target_auto_activated(struct nci_dev *ndev,
                                      struct nci_rf_intf_activated_ntf *ntf)
 {
@@ -454,6 +483,11 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
                                                                    &ntf, data);
                        break;
 
+               case NCI_RF_INTERFACE_NFC_DEP:
+                       err = nci_extract_activation_params_nfc_dep(ndev,
+                                                                   &ntf, data);
+                       break;
+
                case NCI_RF_INTERFACE_FRAME:
                        /* no activation params */
                        break;
@@ -473,6 +507,24 @@ exit:
 
                /* set the available credits to initial value */
                atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+
+               /* store general bytes to be reported later in dep_link_up */
+               if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
+                       ndev->remote_gb_len = 0;
+
+                       if (ntf.activation_params_len > 0) {
+                               /* ATR_RES general bytes at offset 15 */
+                               ndev->remote_gb_len = min_t(__u8,
+                                       (ntf.activation_params
+                                       .poll_nfc_dep.atr_res_len
+                                       - NFC_ATR_RES_GT_OFFSET),
+                                       NFC_MAX_GT_LEN);
+                               memcpy(ndev->remote_gb,
+                                      (ntf.activation_params.poll_nfc_dep
+                                      .atr_res + NFC_ATR_RES_GT_OFFSET),
+                                      ndev->remote_gb_len);
+                       }
+               }
        }
 
        if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
index 3003c33..dd072f3 100644 (file)
@@ -119,6 +119,16 @@ exit:
        nci_req_complete(ndev, rsp_1->status);
 }
 
+static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
+                                          struct sk_buff *skb)
+{
+       struct nci_core_set_config_rsp *rsp = (void *) skb->data;
+
+       pr_debug("status 0x%x\n", rsp->status);
+
+       nci_req_complete(ndev, rsp->status);
+}
+
 static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
                                       struct sk_buff *skb)
 {
@@ -194,6 +204,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
                nci_core_init_rsp_packet(ndev, skb);
                break;
 
+       case NCI_OP_CORE_SET_CONFIG_RSP:
+               nci_core_set_config_rsp_packet(ndev, skb);
+               break;
+
        case NCI_OP_RF_DISCOVER_MAP_RSP:
                nci_rf_disc_map_rsp_packet(ndev, skb);
                break;
index 99bc6f7..77f7eb5 100644 (file)
@@ -167,7 +167,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev)
 
        dev->genl_data.poll_req_pid = 0;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!msg)
                return -ENOMEM;
 
@@ -195,7 +195,7 @@ int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
@@ -226,7 +226,7 @@ int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
@@ -258,7 +258,7 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
@@ -288,7 +288,7 @@ int nfc_genl_device_added(struct nfc_dev *dev)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
@@ -321,7 +321,7 @@ int nfc_genl_device_removed(struct nfc_dev *dev)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
@@ -364,7 +364,8 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
-           nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up))
+           nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
+           nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
 
        return genlmsg_end(msg, hdr);
@@ -434,7 +435,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,
 
        pr_debug("DEP link is up\n");
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!msg)
                return -ENOMEM;
 
@@ -473,7 +474,7 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev)
 
        pr_debug("DEP link is down\n");
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!msg)
                return -ENOMEM;
 
@@ -514,7 +515,7 @@ static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info)
        if (!dev)
                return -ENODEV;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg) {
                rc = -ENOMEM;
                goto out_putdev;
@@ -761,31 +762,63 @@ static struct genl_ops nfc_genl_ops[] = {
        },
 };
 
-static int nfc_genl_rcv_nl_event(struct notifier_block *this,
-                                unsigned long event, void *ptr)
+
+struct urelease_work {
+       struct  work_struct w;
+       int     pid;
+};
+
+static void nfc_urelease_event_work(struct work_struct *work)
 {
-       struct netlink_notify *n = ptr;
+       struct urelease_work *w = container_of(work, struct urelease_work, w);
        struct class_dev_iter iter;
        struct nfc_dev *dev;
 
-       if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
-               goto out;
+       pr_debug("pid %d\n", w->pid);
 
-       pr_debug("NETLINK_URELEASE event from id %d\n", n->pid);
+       mutex_lock(&nfc_devlist_mutex);
 
        nfc_device_iter_init(&iter);
        dev = nfc_device_iter_next(&iter);
 
        while (dev) {
-               if (dev->genl_data.poll_req_pid == n->pid) {
+               mutex_lock(&dev->genl_data.genl_data_mutex);
+
+               if (dev->genl_data.poll_req_pid == w->pid) {
                        nfc_stop_poll(dev);
                        dev->genl_data.poll_req_pid = 0;
                }
+
+               mutex_unlock(&dev->genl_data.genl_data_mutex);
+
                dev = nfc_device_iter_next(&iter);
        }
 
        nfc_device_iter_exit(&iter);
 
+       mutex_unlock(&nfc_devlist_mutex);
+
+       kfree(w);
+}
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+                                unsigned long event, void *ptr)
+{
+       struct netlink_notify *n = ptr;
+       struct urelease_work *w;
+
+       if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+               goto out;
+
+       pr_debug("NETLINK_URELEASE event from id %d\n", n->pid);
+
+       w = kmalloc(sizeof(*w), GFP_ATOMIC);
+       if (w) {
+               INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+               w->pid = n->pid;
+               schedule_work((struct work_struct *) w);
+       }
+
 out:
        return NOTIFY_DONE;
 }