diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-10-29 14:52:37 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-10-29 14:52:37 -0400 |
commit | e298c79efcf7b88fded994a03375589d8b726ce7 (patch) | |
tree | ca430a7220b7ee30c1a0aa09b5e2c1511c9d0e4c | |
parent | d1f10302568221c20628200bc8baa426c55f61c0 (diff) | |
parent | 52feb444a90304eb13c03115bb9758101dbb9254 (diff) | |
download | linux-e298c79efcf7b88fded994a03375589d8b726ce7.tar.bz2 |
Merge tag 'nfc-next-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-3.0
This is the first NFC pull request for 3.8
With this one we have:
- pn544 p2p support.
- pn544 physical and HCI layers separation. We are getting the pn544 driver
ready to support non i2c physical layers.
- LLCP SNL (Service Name Lookup). This is the NFC p2p service discovery
protocol.
- LLCP datagram sockets (connection less) support.
- IDR library usage for NFC devices indexes assignement.
- NFC netlink extension for setting and getting LLCP link characteristics.
- Various code style fixes and cleanups spread over the pn533, LLCP, HCI and
pn544 code.
-rw-r--r-- | drivers/nfc/Makefile | 2 | ||||
-rw-r--r-- | drivers/nfc/pn533.c | 18 | ||||
-rw-r--r-- | drivers/nfc/pn544/Makefile | 7 | ||||
-rw-r--r-- | drivers/nfc/pn544/i2c.c | 500 | ||||
-rw-r--r-- | drivers/nfc/pn544/pn544.c (renamed from drivers/nfc/pn544_hci.c) | 679 | ||||
-rw-r--r-- | drivers/nfc/pn544/pn544.h | 32 | ||||
-rw-r--r-- | include/net/nfc/hci.h | 18 | ||||
-rw-r--r-- | include/net/nfc/nfc.h | 2 | ||||
-rw-r--r-- | include/uapi/linux/nfc.h | 15 | ||||
-rw-r--r-- | net/nfc/Kconfig | 4 | ||||
-rw-r--r-- | net/nfc/core.c | 33 | ||||
-rw-r--r-- | net/nfc/hci/command.c | 24 | ||||
-rw-r--r-- | net/nfc/hci/core.c | 71 | ||||
-rw-r--r-- | net/nfc/hci/llc.c | 2 | ||||
-rw-r--r-- | net/nfc/hci/llc_shdlc.c | 7 | ||||
-rw-r--r-- | net/nfc/llcp/Kconfig | 4 | ||||
-rw-r--r-- | net/nfc/llcp/commands.c | 120 | ||||
-rw-r--r-- | net/nfc/llcp/llcp.c | 226 | ||||
-rw-r--r-- | net/nfc/llcp/llcp.h | 13 | ||||
-rw-r--r-- | net/nfc/llcp/sock.c | 42 | ||||
-rw-r--r-- | net/nfc/nci/Kconfig | 4 | ||||
-rw-r--r-- | net/nfc/nci/core.c | 29 | ||||
-rw-r--r-- | net/nfc/netlink.c | 157 | ||||
-rw-r--r-- | net/nfc/nfc.h | 6 | ||||
-rw-r--r-- | net/nfc/rawsock.c | 1 |
25 files changed, 1472 insertions, 544 deletions
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index bf05831fdf09..36c359043f54 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,7 +2,7 @@ # Makefile for nfc devices # -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 diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 97c440a8cd61..18e279d3e836 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -84,6 +84,10 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_LISTEN_TIME 2 /* frame definitions */ +#define PN533_NORMAL_FRAME_MAX_LEN 262 /* 6 (PREAMBLE, SOF, LEN, LCS, TFI) + 254 (DATA) + 2 (DCS, postamble) */ + #define PN533_FRAME_TAIL_SIZE 2 #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ PN533_FRAME_TAIL_SIZE) @@ -1165,8 +1169,7 @@ static void pn533_poll_create_mod_list(struct pn533 *dev, pn533_poll_add_mod(dev, PN533_LISTEN_MOD); } -static int pn533_start_poll_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) +static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_len) { struct pn533_poll_response *resp; int rc; @@ -1304,8 +1307,7 @@ static void pn533_wq_tg_get_data(struct work_struct *work) } #define ATR_REQ_GB_OFFSET 17 -static int pn533_init_target_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) +static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_len) { struct pn533_cmd_init_target_response *resp; u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb; @@ -1402,9 +1404,9 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg, if (cur_mod->len == 0) { del_timer(&dev->listen_timer); - return pn533_init_target_complete(dev, arg, params, params_len); + return pn533_init_target_complete(dev, params, params_len); } else { - rc = pn533_start_poll_complete(dev, arg, params, params_len); + rc = pn533_start_poll_complete(dev, params, params_len); if (!rc) return rc; } @@ -2373,9 +2375,9 @@ static int pn533_probe(struct usb_interface *interface, goto error; } - dev->in_frame = kmalloc(dev->in_maxlen, GFP_KERNEL); + dev->in_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); - dev->out_frame = kmalloc(dev->out_maxlen, GFP_KERNEL); + dev->out_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->in_frame || !dev->out_frame || diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile new file mode 100644 index 000000000000..725733881eb3 --- /dev/null +++ b/drivers/nfc/pn544/Makefile @@ -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 index 000000000000..fb430d882352 --- /dev/null +++ b/drivers/nfc/pn544/i2c.c @@ -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); diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544/pn544.c index c9c8570273ab..cc666de3b8e5 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544/pn544.c @@ -18,47 +18,21 @@ * 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/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}, @@ -128,259 +115,22 @@ static struct nfc_hci_gate pn544_gates[] = { /* Largest headroom needed for outgoing custom commands */ #define PN544_CMDS_HEADROOM 2 -#define PN544_FRAME_HEADROOM 1 -#define PN544_FRAME_TAILROOM 2 struct pn544_hci_info { - struct i2c_client *i2c_dev; + 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) { - if (r != len) - return -EREMOTEIO; - else - return 0; - } - - 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; - struct sk_buff *skb = NULL; - int r; - - if (!info || irq != info->i2c_dev->irq) { - WARN_ON_ONCE(1); - return IRQ_NONE; - } - - client = info->i2c_dev; - 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_hci_recv_frame(info->hdev, NULL); - - return IRQ_HANDLED; - } else if ((r == -ENOMEM) || (r == -EBADMSG)) { - return IRQ_HANDLED; - } - - nfc_hci_recv_frame(info->hdev, skb); - - return IRQ_HANDLED; -} - static int pn544_hci_open(struct nfc_hci_dev *hdev) { struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); @@ -393,7 +143,7 @@ static int pn544_hci_open(struct nfc_hci_dev *hdev) 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; @@ -412,7 +162,7 @@ static void pn544_hci_close(struct nfc_hci_dev *hdev) if (info->state == PN544_ST_COLD) goto out; - pn544_hci_disable(info); + info->phy_ops->disable(info->phy_id); info->state = PN544_ST_COLD; @@ -587,40 +337,11 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) return 0; } -static void pn544_hci_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_remove_len_crc(struct sk_buff *skb) -{ - skb_pull(skb, PN544_FRAME_HEADROOM); - skb_trim(skb, PN544_FRAME_TAILROOM); -} - static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) { struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); - struct i2c_client *client = info->i2c_dev; - int r; - if (info->hard_fault != 0) - return info->hard_fault; - - pn544_hci_add_len_crc(skb); - r = pn544_hci_i2c_write(client, skb->data, skb->len); - pn544_hci_remove_len_crc(skb); - - return r; + return info->phy_ops->write(info->phy_id, skb); } static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, @@ -630,6 +351,9 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, 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); @@ -667,6 +391,61 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, 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) @@ -676,6 +455,43 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, return r; } +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) { @@ -687,6 +503,9 @@ static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, 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; } @@ -701,7 +520,18 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, 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; @@ -724,6 +554,16 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, 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 @@ -769,7 +609,7 @@ static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, * <= 0: driver handled the data exchange * 1: driver doesn't especially handle, please do standard processing */ -static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, +static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context) @@ -822,17 +662,110 @@ static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, 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_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) { - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_RF_READER_CMD_PRESENCE_CHECK, - NULL, 0, NULL); + 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 0; +} + +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 = { @@ -841,74 +774,36 @@ static struct nfc_hci_ops pn544_hci_ops = { .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 | IRQF_ONESHOT, - 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); @@ -928,13 +823,11 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, NFC_PROTO_NFC_DEP_MASK; info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, - protocols, LLC_SHDLC_NAME, - PN544_FRAME_HEADROOM + - PN544_CMDS_HEADROOM, - PN544_FRAME_TAILROOM, - PN544_HCI_LLC_MAX_PAYLOAD); + protocols, llc_name, + phy_headroom + PN544_CMDS_HEADROOM, + phy_tailroom, phy_payload); if (!info->hdev) { - dev_err(&client->dev, "Cannot allocate nfc hdev.\n"); + pr_err("Cannot allocate nfc hdev.\n"); r = -ENOMEM; goto err_alloc_hdev; } @@ -945,79 +838,25 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, if (r) goto err_regdev; + *hdev = info->hdev; + return 0; err_regdev: nfc_hci_free_device(info->hdev); err_alloc_hdev: - free_irq(client->irq, info); - -err_rti: - if (pdata->free_resources != NULL) - pdata->free_resources(); - -err_pdata: 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_hci_free_device(info->hdev); - - 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 index 000000000000..f47c6454914b --- /dev/null +++ b/drivers/nfc/pn544/pn544.h @@ -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_ */ diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index e900072950cb..639f50af42df 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -24,6 +24,12 @@ #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 { @@ -38,15 +44,21 @@ struct nfc_hci_ops { 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, + 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 */ @@ -114,6 +126,9 @@ struct nfc_hci_dev { int async_cb_type; data_exchange_cb_t async_cb; void *async_cb_context; + + u8 *gb; + size_t gb_len; }; /* hci device allocation */ @@ -219,5 +234,6 @@ 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/nfc.h b/include/net/nfc/nfc.h index f05b10682c9d..fce80b2f9be7 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -95,7 +95,7 @@ struct nfc_genl_data { }; struct nfc_dev { - unsigned int idx; + int idx; u32 target_next_idx; struct nfc_target *targets; int n_targets; diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index d908d17da56d..0e63cee8d810 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -60,6 +60,13 @@ * target mode. * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated * from target mode. + * @NFC_CMD_LLC_GET_PARAMS: request LTO, RW, and MIUX parameters for a device + * @NFC_CMD_LLC_SET_PARAMS: set one or more of LTO, RW, and MIUX parameters for + * a device. LTO must be set before the link is up otherwise -EINPROGRESS + * is returned. RW and MIUX can be set at anytime and will be passed in + * subsequent CONNECT and CC messages. + * If one of the passed parameters is wrong none is set and -EINVAL is + * returned. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -77,6 +84,8 @@ enum nfc_commands { NFC_EVENT_TARGET_LOST, NFC_EVENT_TM_ACTIVATED, NFC_EVENT_TM_DEACTIVATED, + NFC_CMD_LLC_GET_PARAMS, + NFC_CMD_LLC_SET_PARAMS, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -102,6 +111,9 @@ enum nfc_commands { * @NFC_ATTR_RF_MODE: Initiator or target * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for + * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter + * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter + * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -119,6 +131,9 @@ enum nfc_attrs { NFC_ATTR_DEVICE_POWERED, NFC_ATTR_IM_PROTOCOLS, NFC_ATTR_TM_PROTOCOLS, + NFC_ATTR_LLC_PARAM_LTO, + NFC_ATTR_LLC_PARAM_RW, + NFC_ATTR_LLC_PARAM_MIUX, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 8d8d9bc4b6ff..60c3bbb63e8e 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -3,8 +3,8 @@ # menuconfig NFC - depends on NET && EXPERIMENTAL - tristate "NFC subsystem support (EXPERIMENTAL)" + depends on NET + tristate "NFC subsystem support" default n help Say Y here if you want to build support for NFC (Near field diff --git a/net/nfc/core.c b/net/nfc/core.c index 479bee36dc3e..aa64ea441676 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -40,6 +40,9 @@ int nfc_devlist_generation; DEFINE_MUTEX(nfc_devlist_mutex); +/* NFC device ID bitmap */ +static DEFINE_IDA(nfc_index_ida); + /** * nfc_dev_up - turn on the NFC device * @@ -181,6 +184,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); @@ -194,7 +198,7 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx) if (dev->n_targets == 0) return NULL; - for (i = 0; i < dev->n_targets ; i++) { + for (i = 0; i < dev->n_targets; i++) { if (dev->targets[i].idx == target_idx) return &dev->targets[i]; } @@ -274,12 +278,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 +509,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); } @@ -697,6 +704,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 +717,7 @@ static void nfc_check_pres_work(struct work_struct *work) } } +exit: device_unlock(&dev->dev); } @@ -753,7 +763,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, int tx_headroom, int tx_tailroom) { - static atomic_t dev_no = ATOMIC_INIT(0); struct nfc_dev *dev; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || @@ -767,11 +776,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, if (!dev) return NULL; - dev->dev.class = &nfc_class; - dev->idx = atomic_inc_return(&dev_no) - 1; - dev_set_name(&dev->dev, "nfc%d", dev->idx); - device_initialize(&dev->dev); - dev->ops = ops; dev->supported_protocols = supported_protocols; dev->tx_headroom = tx_headroom; @@ -779,6 +783,7 @@ 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; @@ -806,6 +811,14 @@ int nfc_register_device(struct nfc_dev *dev) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL); + if (dev->idx < 0) + return dev->idx; + + dev->dev.class = &nfc_class; + dev_set_name(&dev->dev, "nfc%d", dev->idx); + device_initialize(&dev->dev); + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; rc = device_add(&dev->dev); @@ -834,10 +847,12 @@ EXPORT_SYMBOL(nfc_register_device); */ void nfc_unregister_device(struct nfc_dev *dev) { - int rc; + int rc, id; pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + id = dev->idx; + mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; @@ -856,6 +871,8 @@ void nfc_unregister_device(struct nfc_dev *dev) pr_debug("The userspace won't be notified that the device %s was removed\n", dev_name(&dev->dev)); + ida_simple_remove(&nfc_index_ida, id); + } EXPORT_SYMBOL(nfc_unregister_device); diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 71c6a7086b8f..07659cfd6d7b 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -257,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 *) ¶ms, 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) @@ -279,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 @@ -288,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) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 5fbb6e40793e..bc571b0efb92 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -65,8 +65,9 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) -ETIME); kfree(hdev->cmd_pending_msg); hdev->cmd_pending_msg = NULL; - } else + } else { goto exit; + } } next_msg: @@ -182,7 +183,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; @@ -263,7 +264,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 +278,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 +311,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; } @@ -527,7 +536,8 @@ static int hci_start_poll(struct nfc_dev *nfc_dev, return hdev->ops->start_poll(hdev, im_protocols, tm_protocols); else return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + NFC_HCI_EVT_READER_REQUESTED, + NULL, 0); } static void hci_stop_poll(struct nfc_dev *nfc_dev) @@ -538,6 +548,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) { @@ -586,8 +618,8 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 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, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r <= 0) /* handled */ break; @@ -604,14 +636,14 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, skb->len, hci_transceive_cb, hdev); break; default: - if (hdev->ops->data_exchange) { - r = hdev->ops->data_exchange(hdev, target, skb, cb, + if (hdev->ops->im_transceive) { + r = hdev->ops->im_transceive(hdev, target, skb, cb, cb_context); if (r == 1) r = -ENOTSUPP; - } - else + } else { r = -ENOTSUPP; + } break; } @@ -620,6 +652,16 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, return r; } +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) { @@ -723,9 +765,12 @@ static struct nfc_ops hci_nfc_ops = { .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, }; @@ -848,7 +893,7 @@ void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) } EXPORT_SYMBOL(nfc_hci_driver_failure); -void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) +void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) { nfc_llc_rcv_from_drv(hdev->llc, skb); } diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index ae1205ded87f..fe5e966e5b88 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c @@ -72,7 +72,7 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops) llc_engine->ops = ops; INIT_LIST_HEAD(&llc_engine->entry); - list_add_tail (&llc_engine->entry, &llc_engines); + list_add_tail(&llc_engine->entry, &llc_engines); return 0; } diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 01cbc72943cd..27b313befc35 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -634,9 +634,9 @@ static void llc_shdlc_sm_work(struct work_struct *work) r = llc_shdlc_connect_initiate(shdlc); else r = -ETIME; - if (r < 0) + if (r < 0) { llc_shdlc_connect_complete(shdlc, r); - else { + } else { mod_timer(&shdlc->connect_timer, jiffies + msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); @@ -682,9 +682,8 @@ static void llc_shdlc_sm_work(struct work_struct *work) llc_shdlc_handle_send_queue(shdlc); } - if (shdlc->hard_fault) { + if (shdlc->hard_fault) shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); - } break; default: break; diff --git a/net/nfc/llcp/Kconfig b/net/nfc/llcp/Kconfig index fbf5e8150908..a1a41cd68255 100644 --- a/net/nfc/llcp/Kconfig +++ b/net/nfc/llcp/Kconfig @@ -1,6 +1,6 @@ config NFC_LLCP - depends on NFC && EXPERIMENTAL - bool "NFC LLCP support (EXPERIMENTAL)" + depends on NFC + bool "NFC LLCP support" default n help Say Y here if you want to build support for a kernel NFC LLCP diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index c45ccd6c094c..ed2d17312d61 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -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; @@ -324,8 +316,7 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) struct sk_buff *skb; u8 *service_name_tlv = NULL, service_name_tlv_length; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -343,13 +334,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) size += service_name_tlv_length; } - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len); @@ -386,8 +375,7 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) struct nfc_llcp_local *local; struct sk_buff *skb; u8 *miux_tlv = NULL, miux_tlv_length; - u8 *rw_tlv = NULL, rw_tlv_length, rw; - __be16 miux; + u8 *rw_tlv = NULL, rw_tlv_length; int err; u16 size = 0; @@ -397,13 +385,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) if (local == NULL) return -ENODEV; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_tlv_length); size += miux_tlv_length; - rw = LLCP_MAX_RW; - rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length); + rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length); size += rw_tlv_length; skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size); @@ -428,6 +414,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; @@ -541,6 +573,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; diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index cc10d073c338..f6804532047a 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -45,12 +45,38 @@ void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk) write_unlock(&l->lock); } +static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock) +{ + struct nfc_llcp_local *local = sock->local; + struct sk_buff *s, *tmp; + + pr_debug("%p\n", &sock->sk); + + skb_queue_purge(&sock->tx_queue); + skb_queue_purge(&sock->tx_pending_queue); + skb_queue_purge(&sock->tx_backlog_queue); + + if (local == NULL) + return; + + /* Search for local pending SKBs that are related to this socket */ + skb_queue_walk_safe(&local->tx_queue, s, tmp) { + if (s->sk != &sock->sk) + continue; + + skb_unlink(s, &local->tx_queue); + kfree_skb(s); + } +} + static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) { struct sock *sk; struct hlist_node *node, *tmp; struct nfc_llcp_sock *llcp_sock; + skb_queue_purge(&local->tx_queue); + write_lock(&local->sockets.lock); sk_for_each_safe(sk, node, tmp, &local->sockets.head) { @@ -58,6 +84,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) bh_lock_sock(sk); + nfc_llcp_socket_purge(llcp_sock); + if (sk->sk_state == LLCP_CONNECTED) nfc_put_device(llcp_sock->dev); @@ -65,7 +93,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) struct nfc_llcp_sock *lsk, *n; struct sock *accept_sk; - list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue, + list_for_each_entry_safe(lsk, n, + &llcp_sock->accept_queue, accept_queue) { accept_sk = &lsk->sk; bh_lock_sock(accept_sk); @@ -85,6 +114,16 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen) } } + /* + * 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; bh_unlock_sock(sk); @@ -134,7 +173,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 +185,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); @@ -249,7 +290,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 || @@ -421,10 +467,9 @@ static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local) static int nfc_llcp_build_gb(struct nfc_llcp_local *local) { u8 *gb_cur, *version_tlv, version, version_length; - u8 *lto_tlv, lto, lto_length; + u8 *lto_tlv, lto_length; u8 *wks_tlv, wks_length; u8 *miux_tlv, miux_length; - __be16 miux; u8 gb_len = 0; int ret = 0; @@ -433,9 +478,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) 1, &version_length); gb_len += version_length; - /* 1500 ms */ - lto = 150; - lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, <o, 1, <o_length); + lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, <o_length); gb_len += lto_length; pr_debug("Local wks 0x%lx\n", local->local_wks); @@ -443,8 +486,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local) &wks_length); gb_len += wks_length; - miux = cpu_to_be16(LLCP_MAX_MIUX); - miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0, + miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0, &miux_length); gb_len += miux_length; @@ -610,7 +652,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"); @@ -629,8 +674,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); @@ -704,6 +747,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) { @@ -823,9 +899,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) @@ -953,6 +1026,9 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local, sk = &llcp_sock->sk; lock_sock(sk); + + nfc_llcp_socket_purge(llcp_sock); + if (sk->sk_state == LLCP_CLOSED) { release_sock(sk); nfc_llcp_sock_put(llcp_sock); @@ -1027,7 +1103,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; } @@ -1038,8 +1114,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) @@ -1072,6 +1240,11 @@ static void nfc_llcp_rx_work(struct work_struct *work) 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); @@ -1092,6 +1265,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: @@ -1104,8 +1282,6 @@ static void nfc_llcp_rx_work(struct work_struct *work) schedule_work(&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) @@ -1121,8 +1297,6 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) local->rx_pending = skb_get(skb); del_timer(&local->link_timer); schedule_work(&local->rx_work); - - return; } int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) @@ -1205,6 +1379,10 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) rwlock_init(&local->connecting_sockets.lock); rwlock_init(&local->raw_sockets.lock); + local->lto = 150; /* 1500 ms */ + local->rw = LLCP_MAX_RW; + local->miux = cpu_to_be16(LLCP_MAX_MIUX); + nfc_llcp_build_gb(local); local->remote_miu = LLCP_DEFAULT_MIU; diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index fdb2d24e60bd..0d62366f8cc3 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -64,6 +64,9 @@ struct nfc_llcp_local { u32 target_idx; u8 rf_mode; u8 comm_mode; + u8 lto; + u8 rw; + __be16 miux; unsigned long local_wks; /* Well known services */ unsigned long local_sdp; /* Local services */ unsigned long local_sap; /* Local SAPs, not available for discovery */ @@ -124,6 +127,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) @@ -209,10 +219,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 */ diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 63e4cdc92376..0fa1e92ceac8 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -205,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; } @@ -608,6 +608,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; @@ -663,11 +682,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 || sk->sk_type == SOCK_RAW) { + 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); diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig index decdc49b26d8..6d69b5f0f19b 100644 --- a/net/nfc/nci/Kconfig +++ b/net/nfc/nci/Kconfig @@ -1,6 +1,6 @@ config NFC_NCI - depends on NFC && EXPERIMENTAL - tristate "NCI protocol support (EXPERIMENTAL)" + depends on NFC + tristate "NCI protocol support" default n help NCI (NFC Controller Interface) is a communication protocol between diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index acf9abb7d99b..5f98dc1bf039 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -205,10 +205,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; @@ -224,8 +224,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; @@ -414,13 +414,13 @@ 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, rc = 0; + int i; param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); if ((param.val == NULL) || (param.len == 0)) - return rc; + return 0; - if (param.len > NCI_MAX_PARAM_LEN) + if (param.len > NFC_MAX_GT_LEN) return -EINVAL; for (i = 0; i < param.len; i++) @@ -429,10 +429,8 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) param.id = NCI_PN_ATR_REQ_GEN_BYTES; param.val = local_gb; - rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, - msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); - - return rc; + return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); } static int nci_start_poll(struct nfc_dev *nfc_dev, @@ -579,7 +577,6 @@ 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) { @@ -806,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; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index c1b5285cbde7..3568ae16786d 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -29,6 +29,8 @@ #include "nfc.h" +#include "llcp/llcp.h" + static struct genl_multicast_group nfc_genl_event_mcgrp = { .name = NFC_GENL_MCAST_EVENT_NAME, }; @@ -364,7 +366,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); @@ -590,7 +593,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] && !info->attrs[NFC_ATTR_PROTOCOLS]) && - !info->attrs[NFC_ATTR_TM_PROTOCOLS])) + !info->attrs[NFC_ATTR_TM_PROTOCOLS])) return -EINVAL; idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); @@ -715,6 +718,146 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info) return rc; } +static int nfc_genl_send_params(struct sk_buff *msg, + struct nfc_llcp_local *local, + u32 portid, u32 seq) +{ + void *hdr; + + hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0, + NFC_CMD_LLC_GET_PARAMS); + if (!hdr) + return -EMSGSIZE; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) || + nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) || + nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux))) + goto nla_put_failure; + + return genlmsg_end(msg, hdr); + +nla_put_failure: + + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + int rc = 0; + struct sk_buff *msg = NULL; + u32 idx; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + rc = -ENODEV; + goto exit; + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + rc = -ENOMEM; + goto exit; + } + + rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + if (rc < 0) { + if (msg) + nlmsg_free(msg); + + return rc; + } + + return genlmsg_reply(msg, info); +} + +static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + u8 rw = 0; + u16 miux = 0; + u32 idx; + int rc = 0; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] && + !info->attrs[NFC_ATTR_LLC_PARAM_RW] && + !info->attrs[NFC_ATTR_LLC_PARAM_MIUX])) + return -EINVAL; + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) { + rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]); + + if (rw > LLCP_MAX_RW) + return -EINVAL; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) { + miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]); + + if (miux > LLCP_MAX_MIUX) + return -EINVAL; + } + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + device_lock(&dev->dev); + + local = nfc_llcp_find_local(dev); + if (!local) { + nfc_put_device(dev); + rc = -ENODEV; + goto exit; + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) { + if (dev->dep_link_up) { + rc = -EINPROGRESS; + goto exit; + } + + local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]); + } + + if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) + local->rw = rw; + + if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) + local->miux = cpu_to_be16(miux); + +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + return rc; +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -759,6 +902,16 @@ static struct genl_ops nfc_genl_ops[] = { .done = nfc_genl_dump_targets_done, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_LLC_GET_PARAMS, + .doit = nfc_genl_llc_get_params, + .policy = nfc_genl_policy, + }, + { + .cmd = NFC_CMD_LLC_SET_PARAMS, + .doit = nfc_genl_llc_set_params, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index c5e42b79a418..87d914d2876a 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -56,6 +56,7 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev); int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len); u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); +struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); @@ -97,6 +98,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev, return 0; } +static inline struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev) +{ + return NULL; +} + static inline int nfc_llcp_init(void) { return 0; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 8b8a6a2b2bad..313bf1bc848a 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -256,7 +256,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, return rc ? : copied; } - static const struct proto_ops rawsock_ops = { .family = PF_NFC, .owner = THIS_MODULE, |